210 lines
5.7 KiB
TypeScript
210 lines
5.7 KiB
TypeScript
import { createEffect, createRoot, createSignal } from 'solid-js';
|
|
import PouchDB from 'pouchdb';
|
|
import indexeddb from 'pouchdb-adapter-indexeddb';
|
|
|
|
import { sleep, until } from '../lib/async-wait';
|
|
import { isEqual } from 'lodash';
|
|
|
|
PouchDB.plugin(indexeddb);
|
|
|
|
declare global {
|
|
var localDb: any;
|
|
var db: any;
|
|
var remoteDb: any;
|
|
var currentAccount: any;
|
|
var sync: any;
|
|
}
|
|
|
|
const [state, setState] = createSignal<any>({});
|
|
export { state };
|
|
|
|
export const watchDbLegacy = async () => {
|
|
createRoot(async (dispose) => {
|
|
console.log({ caller: 'watchDbLegacy' });
|
|
|
|
db = globalThis.db;
|
|
localDb = globalThis.localDb;
|
|
|
|
const [status, setStatus] = createSignal<any>({});
|
|
const [syncState, setSyncState] = createSignal<any>({});
|
|
|
|
const updateStatus = async () => {
|
|
const dbInfo = await db.info();
|
|
const localDbInfo = await localDb.info();
|
|
const remoteDbInfo = globalThis.remoteDb
|
|
? await remoteDb.info()
|
|
: undefined;
|
|
const tasks = PouchDB.activeTasks.list();
|
|
const newStatus = {
|
|
...status(),
|
|
tasks,
|
|
dbInfo,
|
|
localDbInfo,
|
|
remoteDbInfo,
|
|
// date: new Date(),
|
|
};
|
|
if (!isEqual(status(), newStatus)) {
|
|
setStatus(newStatus);
|
|
}
|
|
};
|
|
|
|
createEffect(() => {
|
|
console.log({
|
|
caller: 'watchDbLegacy',
|
|
state: state(),
|
|
status: status(),
|
|
syncState: syncState(),
|
|
sync,
|
|
});
|
|
});
|
|
|
|
createEffect(() => {
|
|
const newState = {
|
|
...state(),
|
|
dbName: status()?.dbInfo?.db_name,
|
|
localUpdateSeq: status()?.dbInfo?.update_seq,
|
|
remoteUrl: status()?.remoteDbInfo?.host,
|
|
sync: syncState(),
|
|
};
|
|
if (!isEqual(newState, state())) {
|
|
setState(newState);
|
|
}
|
|
});
|
|
|
|
updateStatus();
|
|
|
|
if (globalThis.sync) {
|
|
globalThis.sync
|
|
.on('change', function (info) {
|
|
// handle change
|
|
console.log({ caller: 'Sync / change', info });
|
|
if (info.direction === 'push') {
|
|
setSyncState({ ...syncState(), lastSeq: info.change.last_seq });
|
|
}
|
|
})
|
|
.on('paused', function (err) {
|
|
// replication paused (e.g. replication up to date, user went offline)
|
|
setSyncState({ ...syncState(), paused: true });
|
|
console.log({ caller: 'Sync / paused', err });
|
|
})
|
|
.on('active', function () {
|
|
// replicate resumed (e.g. new changes replicating, user went back online)
|
|
setSyncState({ ...syncState(), paused: false });
|
|
console.log({ caller: 'Sync / active' });
|
|
})
|
|
.on('denied', function (err) {
|
|
// a document failed to replicate (e.g. due to permissions)
|
|
console.error({ caller: 'Sync / denied', err });
|
|
})
|
|
.on('complete', function (info) {
|
|
// handle complete
|
|
console.log({ caller: 'Sync / complete', info });
|
|
})
|
|
.on('error', function (err) {
|
|
// handle error
|
|
console.error({ caller: 'Sync / error', err });
|
|
});
|
|
}
|
|
|
|
const timerId = setInterval(updateStatus, 15000);
|
|
|
|
// db.compact();
|
|
const openIDb = (name: string) =>
|
|
new Promise((resolve, reject) => {
|
|
const iDb = indexedDB.open(name);
|
|
iDb.onerror = (event: any) => {
|
|
console.error({
|
|
caller: 'watchDbLegacy',
|
|
message: 'open db error',
|
|
target: event.target,
|
|
});
|
|
reject(event.target.errorCode);
|
|
};
|
|
|
|
iDb.onsuccess = (event: any) => {
|
|
resolve(event.target.result);
|
|
};
|
|
});
|
|
|
|
const getAll = (store: any) =>
|
|
new Promise((resolve, reject) => {
|
|
const request = store.getAll();
|
|
request.onerror = (event: any) => {
|
|
console.error({
|
|
caller: 'watchDbLegacy',
|
|
message: 'getAll error',
|
|
target: event.target,
|
|
});
|
|
reject(event.target.errorCode);
|
|
};
|
|
|
|
request.onsuccess = (event: any) => {
|
|
resolve(event.target.result as any[]);
|
|
};
|
|
});
|
|
|
|
const getDeletedDocs = async () => {
|
|
const iDb = (await openIDb(`_pouch_${state().dbName}`)) as IDBDatabase;
|
|
const bySequence = iDb.transaction('docs', 'readonly');
|
|
const store = bySequence.objectStore('docs');
|
|
const allDocs = (await getAll(store)) as any[];
|
|
iDb.close();
|
|
const docs = allDocs
|
|
.filter(
|
|
(doc) =>
|
|
!doc.id.startsWith('_local') &&
|
|
doc.deleted === 1 &&
|
|
doc.seq <= state()?.sync?.lastSeq
|
|
)
|
|
.map((doc) => ({ id: doc.id, rev: doc.rev }));
|
|
// console.log({
|
|
// caller: 'watchDbLegacy / getDeletedDocs',
|
|
// // bySequence,
|
|
// // store,
|
|
// // allDocs,
|
|
// docs,
|
|
// state: state(),
|
|
// });
|
|
return docs;
|
|
};
|
|
|
|
while (true) {
|
|
await until(
|
|
() =>
|
|
state()?.sync?.paused &&
|
|
state()?.sync?.lastSeq >= state()?.localUpdateSeq,
|
|
1000
|
|
);
|
|
|
|
console.log({ caller: 'watchDbLegacy / ready to purge after sync ' });
|
|
|
|
const deletedDocs = await getDeletedDocs();
|
|
|
|
if (deletedDocs.length > 0) {
|
|
console.log({
|
|
caller: 'watchDbLegacy / purge',
|
|
// bySequence,
|
|
// store,
|
|
// allDocs,
|
|
deletedDocs,
|
|
state: state(),
|
|
});
|
|
|
|
deletedDocs.forEach(async (doc) => {
|
|
await sleep(500);
|
|
try {
|
|
await db.purge(doc.id, doc.rev);
|
|
} catch (err) {
|
|
console.log({
|
|
caller: 'watchDbLegacy / purge',
|
|
doc,
|
|
err,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
await sleep(60000);
|
|
}
|
|
});
|
|
};
|