mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-16 03:05:57 +00:00
Fixed:
- Make less file corruption. - Some notice was not hidden automatically
This commit is contained in:
150
main.ts
150
main.ts
@@ -553,6 +553,13 @@ async function testCrypt() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// <-- Encryption
|
// <-- Encryption
|
||||||
|
const delay = (ms: number): Promise<void> => {
|
||||||
|
return new Promise((res) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res();
|
||||||
|
}, ms);
|
||||||
|
});
|
||||||
|
};
|
||||||
//<--Functions
|
//<--Functions
|
||||||
class LocalPouchDB {
|
class LocalPouchDB {
|
||||||
auth: Credential;
|
auth: Credential;
|
||||||
@@ -707,6 +714,7 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getDBLeaf(id: string, waitForReady: boolean): Promise<string> {
|
async getDBLeaf(id: string, waitForReady: boolean): Promise<string> {
|
||||||
|
await this.waitForGCComplete();
|
||||||
// when in cache, use that.
|
// when in cache, use that.
|
||||||
if (this.hashCacheRev[id]) {
|
if (this.hashCacheRev[id]) {
|
||||||
return this.hashCacheRev[id];
|
return this.hashCacheRev[id];
|
||||||
@@ -766,6 +774,7 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getDBEntryMeta(path: string, opt?: PouchDB.Core.GetOptions): Promise<false | LoadedEntry> {
|
async getDBEntryMeta(path: string, opt?: PouchDB.Core.GetOptions): Promise<false | LoadedEntry> {
|
||||||
|
await this.waitForGCComplete();
|
||||||
let id = path2id(path);
|
let id = path2id(path);
|
||||||
try {
|
try {
|
||||||
let obj: EntryDocResponse = null;
|
let obj: EntryDocResponse = null;
|
||||||
@@ -810,6 +819,7 @@ class LocalPouchDB {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
async getDBEntry(path: string, opt?: PouchDB.Core.GetOptions, dump = false, waitForReady = true): Promise<false | LoadedEntry> {
|
async getDBEntry(path: string, opt?: PouchDB.Core.GetOptions, dump = false, waitForReady = true): Promise<false | LoadedEntry> {
|
||||||
|
await this.waitForGCComplete();
|
||||||
let id = path2id(path);
|
let id = path2id(path);
|
||||||
try {
|
try {
|
||||||
let obj: EntryDocResponse = null;
|
let obj: EntryDocResponse = null;
|
||||||
@@ -909,6 +919,7 @@ class LocalPouchDB {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
async deleteDBEntry(path: string, opt?: PouchDB.Core.GetOptions): Promise<boolean> {
|
async deleteDBEntry(path: string, opt?: PouchDB.Core.GetOptions): Promise<boolean> {
|
||||||
|
await this.waitForGCComplete();
|
||||||
let id = path2id(path);
|
let id = path2id(path);
|
||||||
try {
|
try {
|
||||||
let obj: EntryDocResponse = null;
|
let obj: EntryDocResponse = null;
|
||||||
@@ -951,6 +962,7 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async deleteDBEntryPrefix(prefixSrc: string): Promise<boolean> {
|
async deleteDBEntryPrefix(prefixSrc: string): Promise<boolean> {
|
||||||
|
await this.waitForGCComplete();
|
||||||
// delete database entries by prefix.
|
// delete database entries by prefix.
|
||||||
// it called from folder deletion.
|
// it called from folder deletion.
|
||||||
let c = 0;
|
let c = 0;
|
||||||
@@ -1012,6 +1024,7 @@ class LocalPouchDB {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
async putDBEntry(note: LoadedEntry) {
|
async putDBEntry(note: LoadedEntry) {
|
||||||
|
await this.waitForGCComplete();
|
||||||
let leftData = note.data;
|
let leftData = note.data;
|
||||||
let savenNotes = [];
|
let savenNotes = [];
|
||||||
let processed = 0;
|
let processed = 0;
|
||||||
@@ -1209,7 +1222,7 @@ class LocalPouchDB {
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let r = await this.localDatabase.put(newDoc);
|
let r = await this.localDatabase.put(newDoc, { force: true });
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, newDoc._deleted);
|
this.updateRecentModifiedDocs(r.id, r.rev, newDoc._deleted);
|
||||||
if (typeof this.corruptedEntries[note._id] != "undefined") {
|
if (typeof this.corruptedEntries[note._id] != "undefined") {
|
||||||
delete this.corruptedEntries[note._id];
|
delete this.corruptedEntries[note._id];
|
||||||
@@ -1235,6 +1248,7 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
replicateAllToServer(setting: ObsidianLiveSyncSettings, showingNotice?: boolean) {
|
replicateAllToServer(setting: ObsidianLiveSyncSettings, showingNotice?: boolean) {
|
||||||
return new Promise(async (res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
|
await this.waitForGCComplete();
|
||||||
this.closeReplication();
|
this.closeReplication();
|
||||||
Logger("send all data to server", LOG_LEVEL.NOTICE);
|
Logger("send all data to server", LOG_LEVEL.NOTICE);
|
||||||
let notice: Notice = null;
|
let notice: Notice = null;
|
||||||
@@ -1307,6 +1321,7 @@ class LocalPouchDB {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.waitForGCComplete();
|
||||||
if (setting.versionUpFlash != "") {
|
if (setting.versionUpFlash != "") {
|
||||||
new Notice("Open settings and check message, please.");
|
new Notice("Open settings and check message, please.");
|
||||||
return;
|
return;
|
||||||
@@ -1483,6 +1498,7 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async resetDatabase() {
|
async resetDatabase() {
|
||||||
|
await this.waitForGCComplete();
|
||||||
if (this.changeHandler != null) {
|
if (this.changeHandler != null) {
|
||||||
this.changeHandler.removeAllListeners();
|
this.changeHandler.removeAllListeners();
|
||||||
this.changeHandler.cancel();
|
this.changeHandler.cancel();
|
||||||
@@ -1589,72 +1605,87 @@ class LocalPouchDB {
|
|||||||
Logger("Mark this device as 'resolved'.", LOG_LEVEL.NOTICE);
|
Logger("Mark this device as 'resolved'.", LOG_LEVEL.NOTICE);
|
||||||
await dbret.db.put(remoteMilestone);
|
await dbret.db.put(remoteMilestone);
|
||||||
}
|
}
|
||||||
|
gcRunning = false;
|
||||||
|
async waitForGCComplete() {
|
||||||
|
while (this.gcRunning) {
|
||||||
|
Logger("Waiting for Garbage Collection completed.");
|
||||||
|
await delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
async garbageCollect() {
|
async garbageCollect() {
|
||||||
// get all documents of NewEntry2
|
if (this.gcRunning) return;
|
||||||
// we don't use queries , just use allDocs();
|
this.gcRunning = true;
|
||||||
let c = 0;
|
try {
|
||||||
let readCount = 0;
|
// get all documents of NewEntry2
|
||||||
let hashPieces: string[] = [];
|
// we don't use queries , just use allDocs();
|
||||||
let usedPieces: string[] = [];
|
this.disposeHashCache();
|
||||||
Logger("Collecting Garbage");
|
let c = 0;
|
||||||
do {
|
let readCount = 0;
|
||||||
let result = await this.localDatabase.allDocs({ include_docs: true, skip: c, limit: 500, conflicts: true });
|
let hashPieces: string[] = [];
|
||||||
readCount = result.rows.length;
|
let usedPieces: string[] = [];
|
||||||
Logger("checked:" + readCount);
|
Logger("Collecting Garbage");
|
||||||
if (readCount > 0) {
|
do {
|
||||||
//there are some result
|
let result = await this.localDatabase.allDocs({ include_docs: true, skip: c, limit: 500, conflicts: true });
|
||||||
for (let v of result.rows) {
|
readCount = result.rows.length;
|
||||||
let doc = v.doc;
|
Logger("checked:" + readCount);
|
||||||
if (doc.type == "newnote" || doc.type == "plain") {
|
if (readCount > 0) {
|
||||||
// used pieces memo.
|
//there are some result
|
||||||
usedPieces = Array.from(new Set([...usedPieces, ...doc.children]));
|
for (let v of result.rows) {
|
||||||
if (doc._conflicts) {
|
let doc = v.doc;
|
||||||
for (let cid of doc._conflicts) {
|
if (doc.type == "newnote" || doc.type == "plain") {
|
||||||
let p = await this.localDatabase.get<EntryDoc>(doc._id, { rev: cid });
|
// used pieces memo.
|
||||||
if (p.type == "newnote" || p.type == "plain") {
|
usedPieces = Array.from(new Set([...usedPieces, ...doc.children]));
|
||||||
usedPieces = Array.from(new Set([...usedPieces, ...p.children]));
|
if (doc._conflicts) {
|
||||||
|
for (let cid of doc._conflicts) {
|
||||||
|
let p = await this.localDatabase.get<EntryDoc>(doc._id, { rev: cid });
|
||||||
|
if (p.type == "newnote" || p.type == "plain") {
|
||||||
|
usedPieces = Array.from(new Set([...usedPieces, ...p.children]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (doc.type == "leaf") {
|
||||||
|
// all pieces.
|
||||||
|
hashPieces = Array.from(new Set([...hashPieces, doc._id]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (doc.type == "leaf") {
|
}
|
||||||
// all pieces.
|
c += readCount;
|
||||||
hashPieces = Array.from(new Set([...hashPieces, doc._id]));
|
} while (readCount != 0);
|
||||||
|
// items collected.
|
||||||
|
Logger("Finding unused pieces");
|
||||||
|
this.disposeHashCache();
|
||||||
|
const garbages = hashPieces.filter((e) => usedPieces.indexOf(e) == -1);
|
||||||
|
let deleteCount = 0;
|
||||||
|
Logger("we have to delete:" + garbages.length);
|
||||||
|
let deleteDoc: EntryDoc[] = [];
|
||||||
|
for (let v of garbages) {
|
||||||
|
try {
|
||||||
|
let item = await this.localDatabase.get(v);
|
||||||
|
item._deleted = true;
|
||||||
|
deleteDoc.push(item);
|
||||||
|
if (deleteDoc.length > 50) {
|
||||||
|
await this.localDatabase.bulkDocs(deleteDoc);
|
||||||
|
deleteDoc = [];
|
||||||
|
Logger("delete:" + deleteCount);
|
||||||
|
}
|
||||||
|
deleteCount++;
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex.status && ex.status == 404) {
|
||||||
|
// NO OP. It should be timing problem.
|
||||||
|
} else {
|
||||||
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c += readCount;
|
if (deleteDoc.length > 0) {
|
||||||
} while (readCount != 0);
|
await this.localDatabase.bulkDocs(deleteDoc);
|
||||||
// items collected.
|
|
||||||
Logger("Finding unused pieces");
|
|
||||||
const garbages = hashPieces.filter((e) => usedPieces.indexOf(e) == -1);
|
|
||||||
let deleteCount = 0;
|
|
||||||
Logger("we have to delete:" + garbages.length);
|
|
||||||
let deleteDoc: EntryDoc[] = [];
|
|
||||||
for (let v of garbages) {
|
|
||||||
try {
|
|
||||||
let item = await this.localDatabase.get(v);
|
|
||||||
item._deleted = true;
|
|
||||||
deleteDoc.push(item);
|
|
||||||
if (deleteDoc.length > 50) {
|
|
||||||
await this.localDatabase.bulkDocs(deleteDoc);
|
|
||||||
deleteDoc = [];
|
|
||||||
Logger("delete:" + deleteCount);
|
|
||||||
}
|
|
||||||
deleteCount++;
|
|
||||||
} catch (ex) {
|
|
||||||
if (ex.status && ex.status == 404) {
|
|
||||||
// NO OP. It should be timing problem.
|
|
||||||
} else {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Logger(`GC:deleted ${deleteCount} items.`);
|
||||||
|
} finally {
|
||||||
|
this.gcRunning = false;
|
||||||
}
|
}
|
||||||
if (deleteDoc.length > 0) {
|
this.disposeHashCache();
|
||||||
await this.localDatabase.bulkDocs(deleteDoc);
|
|
||||||
}
|
|
||||||
Logger(`GC:deleted ${deleteCount} items.`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2030,6 +2061,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
clearTimeout(this.notifies[messagecontent].timer);
|
clearTimeout(this.notifies[messagecontent].timer);
|
||||||
this.notifies[messagecontent].count++;
|
this.notifies[messagecontent].count++;
|
||||||
this.notifies[messagecontent].notice.setMessage(`(${this.notifies[messagecontent].count}):${messagecontent}`);
|
this.notifies[messagecontent].notice.setMessage(`(${this.notifies[messagecontent].count}):${messagecontent}`);
|
||||||
|
this.notifies[messagecontent].timer = setTimeout(() => {
|
||||||
|
this.notifies[messagecontent].notice.hide();
|
||||||
|
delete this.notifies[messagecontent];
|
||||||
|
}, 5000);
|
||||||
} else {
|
} else {
|
||||||
let notify = new Notice(messagecontent, 0);
|
let notify = new Notice(messagecontent, 0);
|
||||||
this.notifies[messagecontent] = {
|
this.notifies[messagecontent] = {
|
||||||
@@ -2648,6 +2683,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateIntoDB(file: TFile) {
|
async updateIntoDB(file: TFile) {
|
||||||
|
await this.localDatabase.waitForGCComplete();
|
||||||
let content = "";
|
let content = "";
|
||||||
let datatype: "plain" | "newnote" = "newnote";
|
let datatype: "plain" | "newnote" = "newnote";
|
||||||
if (file.extension != "md") {
|
if (file.extension != "md") {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.1.23",
|
"version": "0.1.24",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.1.23",
|
"version": "0.1.24",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.1.23",
|
"version": "0.1.24",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.1.23",
|
"version": "0.1.24",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user