add vault name into dbs
This commit is contained in:
parent
c637038d95
commit
04f3a0c97c
@ -65,6 +65,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.0",
|
||||||
"mime-types": "^2.1.33",
|
"mime-types": "^2.1.33",
|
||||||
|
"nanoid": "^3.1.30",
|
||||||
"obsidian": "^0.12.0",
|
"obsidian": "^0.12.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
|
|||||||
@ -53,6 +53,7 @@ export interface RemotelySavePluginSettings {
|
|||||||
password: string;
|
password: string;
|
||||||
serviceType: SUPPORTED_SERVICES_TYPE;
|
serviceType: SUPPORTED_SERVICES_TYPE;
|
||||||
currLogLevel?: string;
|
currLogLevel?: string;
|
||||||
|
vaultRandomID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteItem {
|
export interface RemoteItem {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { TAbstractFile, TFolder, TFile, Vault } from "obsidian";
|
import { TAbstractFile, TFolder, TFile, Vault } from "obsidian";
|
||||||
|
|
||||||
import type { SyncPlanType } from "./sync";
|
import type { SyncPlanType } from "./sync";
|
||||||
import { readAllSyncPlanRecordTexts } from "./localdb";
|
import { readAllSyncPlanRecordTextsByVault } from "./localdb";
|
||||||
import type { InternalDBs } from "./localdb";
|
import type { InternalDBs } from "./localdb";
|
||||||
import { mkdirpInVault } from "./misc";
|
import { mkdirpInVault } from "./misc";
|
||||||
|
|
||||||
@ -11,10 +11,14 @@ const log = origLog.getLogger("rs-default");
|
|||||||
const DEFAULT_DEBUG_FOLDER = "_debug_remotely_save/";
|
const DEFAULT_DEBUG_FOLDER = "_debug_remotely_save/";
|
||||||
const DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX = "sync_plans_hist_exported_on_";
|
const DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX = "sync_plans_hist_exported_on_";
|
||||||
|
|
||||||
export const exportSyncPlansToFiles = async (db: InternalDBs, vault: Vault) => {
|
export const exportVaultSyncPlansToFiles = async (
|
||||||
|
db: InternalDBs,
|
||||||
|
vault: Vault,
|
||||||
|
vaultRandomID: string
|
||||||
|
) => {
|
||||||
log.info("exporting");
|
log.info("exporting");
|
||||||
await mkdirpInVault(DEFAULT_DEBUG_FOLDER, vault);
|
await mkdirpInVault(DEFAULT_DEBUG_FOLDER, vault);
|
||||||
const records = await readAllSyncPlanRecordTexts(db);
|
const records = await readAllSyncPlanRecordTextsByVault(db, vaultRandomID);
|
||||||
let md = "";
|
let md = "";
|
||||||
if (records.length === 0) {
|
if (records.length === 0) {
|
||||||
md = "No sync plans history found";
|
md = "No sync plans history found";
|
||||||
|
|||||||
194
src/localdb.ts
194
src/localdb.ts
@ -9,7 +9,8 @@ export type LocalForage = typeof localforage;
|
|||||||
import * as origLog from "loglevel";
|
import * as origLog from "loglevel";
|
||||||
const log = origLog.getLogger("rs-default");
|
const log = origLog.getLogger("rs-default");
|
||||||
|
|
||||||
export const DEFAULT_DB_VERSION_NUMBER: number = 20211114;
|
const DB_VERSION_NUMBER_IN_HISTORY = [20211114, 20220108];
|
||||||
|
export const DEFAULT_DB_VERSION_NUMBER: number = 20220108;
|
||||||
export const DEFAULT_DB_NAME = "remotelysavedb";
|
export const DEFAULT_DB_NAME = "remotelysavedb";
|
||||||
export const DEFAULT_TBL_VERSION = "schemaversion";
|
export const DEFAULT_TBL_VERSION = "schemaversion";
|
||||||
export const DEFAULT_TBL_DELETE_HISTORY = "filefolderoperationhistory";
|
export const DEFAULT_TBL_DELETE_HISTORY = "filefolderoperationhistory";
|
||||||
@ -25,6 +26,7 @@ export interface FileFolderHistoryRecord {
|
|||||||
actionType: "delete" | "rename";
|
actionType: "delete" | "rename";
|
||||||
keyType: "folder" | "file";
|
keyType: "folder" | "file";
|
||||||
renameTo: string;
|
renameTo: string;
|
||||||
|
vaultRandomID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SyncMetaMappingRecord {
|
interface SyncMetaMappingRecord {
|
||||||
@ -37,12 +39,14 @@ interface SyncMetaMappingRecord {
|
|||||||
remoteExtraKey: string;
|
remoteExtraKey: string;
|
||||||
remoteType: SUPPORTED_SERVICES_TYPE;
|
remoteType: SUPPORTED_SERVICES_TYPE;
|
||||||
keyType: "folder" | "file";
|
keyType: "folder" | "file";
|
||||||
|
vaultRandomID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SyncPlanRecord {
|
interface SyncPlanRecord {
|
||||||
ts: number;
|
ts: number;
|
||||||
remoteType: string;
|
remoteType: string;
|
||||||
syncPlan: string;
|
syncPlan: string;
|
||||||
|
vaultRandomID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InternalDBs {
|
export interface InternalDBs {
|
||||||
@ -52,7 +56,103 @@ export interface InternalDBs {
|
|||||||
syncPlansTbl: LocalForage;
|
syncPlansTbl: LocalForage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prepareDBs = async () => {
|
/**
|
||||||
|
* This migration mainly aims to assign vault name or vault id into all tables.
|
||||||
|
* @param db
|
||||||
|
* @param vaultRandomID
|
||||||
|
*/
|
||||||
|
const migrateDBsFrom20211114To20220108 = async (
|
||||||
|
db: InternalDBs,
|
||||||
|
vaultRandomID: string
|
||||||
|
) => {
|
||||||
|
const oldVer = 20211114;
|
||||||
|
const newVer = 20220108;
|
||||||
|
log.debug(`start upgrading internal db from ${oldVer} to ${newVer}`);
|
||||||
|
|
||||||
|
const allPromisesToWait: Promise<any>[] = [];
|
||||||
|
|
||||||
|
log.debug("assign vault id to any delete history");
|
||||||
|
const keysInDeleteHistoryTbl = await db.deleteHistoryTbl.keys();
|
||||||
|
for (const key of keysInDeleteHistoryTbl) {
|
||||||
|
if (key.startsWith(vaultRandomID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const value = (await db.deleteHistoryTbl.getItem(
|
||||||
|
key
|
||||||
|
)) as FileFolderHistoryRecord;
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value.vaultRandomID === undefined || value.vaultRandomID === "") {
|
||||||
|
value.vaultRandomID = vaultRandomID;
|
||||||
|
}
|
||||||
|
const newKey = `${vaultRandomID}\t${key}`;
|
||||||
|
allPromisesToWait.push(db.deleteHistoryTbl.setItem(newKey, value));
|
||||||
|
allPromisesToWait.push(db.deleteHistoryTbl.removeItem(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("assign vault id to any sync mapping");
|
||||||
|
const keysInSyncMappingTbl = await db.syncMappingTbl.keys();
|
||||||
|
for (const key of keysInSyncMappingTbl) {
|
||||||
|
if (key.startsWith(vaultRandomID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const value = (await db.syncMappingTbl.getItem(
|
||||||
|
key
|
||||||
|
)) as SyncMetaMappingRecord;
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value.vaultRandomID === undefined || value.vaultRandomID === "") {
|
||||||
|
value.vaultRandomID = vaultRandomID;
|
||||||
|
}
|
||||||
|
const newKey = `${vaultRandomID}\t${key}`;
|
||||||
|
allPromisesToWait.push(db.syncMappingTbl.setItem(newKey, value));
|
||||||
|
allPromisesToWait.push(db.syncMappingTbl.removeItem(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("assign vault id to any sync plan records");
|
||||||
|
const keysInSyncPlansTbl = await db.syncPlansTbl.keys();
|
||||||
|
for (const key of keysInSyncPlansTbl) {
|
||||||
|
if (key.startsWith(vaultRandomID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const value = (await db.syncPlansTbl.getItem(key)) as SyncPlanRecord;
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value.vaultRandomID === undefined || value.vaultRandomID === "") {
|
||||||
|
value.vaultRandomID = vaultRandomID;
|
||||||
|
}
|
||||||
|
const newKey = `${vaultRandomID}\t${key}`;
|
||||||
|
allPromisesToWait.push(db.syncPlansTbl.setItem(newKey, value));
|
||||||
|
allPromisesToWait.push(db.syncPlansTbl.removeItem(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("finally update version if everything is ok");
|
||||||
|
await Promise.all(allPromisesToWait);
|
||||||
|
await db.versionTbl.setItem("version", newVer);
|
||||||
|
|
||||||
|
log.debug(`finish upgrading internal db from ${oldVer} to ${newVer}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const migrateDBs = async (
|
||||||
|
db: InternalDBs,
|
||||||
|
oldVer: number,
|
||||||
|
newVer: number,
|
||||||
|
vaultRandomID: string
|
||||||
|
) => {
|
||||||
|
if (oldVer === newVer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oldVer === 20211114 && newVer === 20220108) {
|
||||||
|
return await migrateDBsFrom20211114To20220108(db, vaultRandomID);
|
||||||
|
}
|
||||||
|
// not implemented
|
||||||
|
throw Error(`not supported internal db changes from ${oldVer} to ${newVer}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const prepareDBs = async (vaultRandomID: string) => {
|
||||||
const db = {
|
const db = {
|
||||||
versionTbl: localforage.createInstance({
|
versionTbl: localforage.createInstance({
|
||||||
name: DEFAULT_DB_NAME,
|
name: DEFAULT_DB_NAME,
|
||||||
@ -74,11 +174,22 @@ export const prepareDBs = async () => {
|
|||||||
|
|
||||||
const originalVersion = (await db.versionTbl.getItem("version")) as number;
|
const originalVersion = (await db.versionTbl.getItem("version")) as number;
|
||||||
if (originalVersion === null) {
|
if (originalVersion === null) {
|
||||||
|
log.debug(
|
||||||
|
`no internal db version, setting it to ${DEFAULT_DB_VERSION_NUMBER}`
|
||||||
|
);
|
||||||
await db.versionTbl.setItem("version", DEFAULT_DB_VERSION_NUMBER);
|
await db.versionTbl.setItem("version", DEFAULT_DB_VERSION_NUMBER);
|
||||||
} else if (originalVersion === DEFAULT_DB_VERSION_NUMBER) {
|
} else if (originalVersion === DEFAULT_DB_VERSION_NUMBER) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} else {
|
} else {
|
||||||
await migrateDBs(db, originalVersion, DEFAULT_DB_VERSION_NUMBER);
|
log.debug(
|
||||||
|
`trying to upgrade db version from ${originalVersion} to ${DEFAULT_DB_VERSION_NUMBER}`
|
||||||
|
);
|
||||||
|
await migrateDBs(
|
||||||
|
db,
|
||||||
|
originalVersion,
|
||||||
|
DEFAULT_DB_VERSION_NUMBER,
|
||||||
|
vaultRandomID
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("db connected");
|
log.info("db connected");
|
||||||
@ -103,33 +214,32 @@ export const destroyDBs = async () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const migrateDBs = async (db: InternalDBs, oldVer: number, newVer: number) => {
|
export const loadDeleteRenameHistoryTableByVault = async (
|
||||||
if (oldVer === newVer) {
|
db: InternalDBs,
|
||||||
return;
|
vaultRandomID: string
|
||||||
}
|
) => {
|
||||||
// not implemented
|
|
||||||
throw Error(`not supported internal db changes from ${oldVer} to ${newVer}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const loadDeleteRenameHistoryTable = async (db: InternalDBs) => {
|
|
||||||
const records = [] as FileFolderHistoryRecord[];
|
const records = [] as FileFolderHistoryRecord[];
|
||||||
await db.deleteHistoryTbl.iterate((value, key, iterationNumber) => {
|
await db.deleteHistoryTbl.iterate((value, key, iterationNumber) => {
|
||||||
records.push(value as FileFolderHistoryRecord);
|
if (key.startsWith(`${vaultRandomID}\t`)) {
|
||||||
|
records.push(value as FileFolderHistoryRecord);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
records.sort((a, b) => a.actionWhen - b.actionWhen); // ascending
|
records.sort((a, b) => a.actionWhen - b.actionWhen); // ascending
|
||||||
return records;
|
return records;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearDeleteRenameHistoryOfKey = async (
|
export const clearDeleteRenameHistoryOfKeyAndVault = async (
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
key: string
|
key: string,
|
||||||
|
vaultRandomID: string
|
||||||
) => {
|
) => {
|
||||||
await db.deleteHistoryTbl.removeItem(key);
|
await db.deleteHistoryTbl.removeItem(`${vaultRandomID}\t${key}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertDeleteRecord = async (
|
export const insertDeleteRecordByVault = async (
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
fileOrFolder: TAbstractFile
|
fileOrFolder: TAbstractFile,
|
||||||
|
vaultRandomID: string
|
||||||
) => {
|
) => {
|
||||||
// log.info(fileOrFolder);
|
// log.info(fileOrFolder);
|
||||||
let k: FileFolderHistoryRecord;
|
let k: FileFolderHistoryRecord;
|
||||||
@ -143,6 +253,7 @@ export const insertDeleteRecord = async (
|
|||||||
actionType: "delete",
|
actionType: "delete",
|
||||||
keyType: "file",
|
keyType: "file",
|
||||||
renameTo: "",
|
renameTo: "",
|
||||||
|
vaultRandomID: vaultRandomID,
|
||||||
};
|
};
|
||||||
} else if (fileOrFolder instanceof TFolder) {
|
} else if (fileOrFolder instanceof TFolder) {
|
||||||
// key should endswith "/"
|
// key should endswith "/"
|
||||||
@ -158,15 +269,17 @@ export const insertDeleteRecord = async (
|
|||||||
actionType: "delete",
|
actionType: "delete",
|
||||||
keyType: "folder",
|
keyType: "folder",
|
||||||
renameTo: "",
|
renameTo: "",
|
||||||
|
vaultRandomID: vaultRandomID,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await db.deleteHistoryTbl.setItem(k.key, k);
|
await db.deleteHistoryTbl.setItem(`${vaultRandomID}\t${k.key}`, k);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertRenameRecord = async (
|
export const insertRenameRecordByVault = async (
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
fileOrFolder: TAbstractFile,
|
fileOrFolder: TAbstractFile,
|
||||||
oldPath: string
|
oldPath: string,
|
||||||
|
vaultRandomID: string
|
||||||
) => {
|
) => {
|
||||||
// log.info(fileOrFolder);
|
// log.info(fileOrFolder);
|
||||||
let k: FileFolderHistoryRecord;
|
let k: FileFolderHistoryRecord;
|
||||||
@ -180,6 +293,7 @@ export const insertRenameRecord = async (
|
|||||||
actionType: "rename",
|
actionType: "rename",
|
||||||
keyType: "file",
|
keyType: "file",
|
||||||
renameTo: fileOrFolder.path,
|
renameTo: fileOrFolder.path,
|
||||||
|
vaultRandomID: vaultRandomID,
|
||||||
};
|
};
|
||||||
} else if (fileOrFolder instanceof TFolder) {
|
} else if (fileOrFolder instanceof TFolder) {
|
||||||
const key = oldPath.endsWith("/") ? oldPath : `${oldPath}/`;
|
const key = oldPath.endsWith("/") ? oldPath : `${oldPath}/`;
|
||||||
@ -195,12 +309,13 @@ export const insertRenameRecord = async (
|
|||||||
actionType: "rename",
|
actionType: "rename",
|
||||||
keyType: "folder",
|
keyType: "folder",
|
||||||
renameTo: renameTo,
|
renameTo: renameTo,
|
||||||
|
vaultRandomID: vaultRandomID,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await db.deleteHistoryTbl.setItem(k.key, k);
|
await db.deleteHistoryTbl.setItem(`${vaultRandomID}\t${k.key}`, k);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const upsertSyncMetaMappingData = async (
|
export const upsertSyncMetaMappingDataByVault = async (
|
||||||
serviceType: SUPPORTED_SERVICES_TYPE,
|
serviceType: SUPPORTED_SERVICES_TYPE,
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
localKey: string,
|
localKey: string,
|
||||||
@ -209,7 +324,8 @@ export const upsertSyncMetaMappingData = async (
|
|||||||
remoteKey: string,
|
remoteKey: string,
|
||||||
remoteMTime: number,
|
remoteMTime: number,
|
||||||
remoteSize: number,
|
remoteSize: number,
|
||||||
remoteExtraKey: string /* ETag from s3 */
|
remoteExtraKey: string,
|
||||||
|
vaultRandomID: string
|
||||||
) => {
|
) => {
|
||||||
const aggregratedInfo: SyncMetaMappingRecord = {
|
const aggregratedInfo: SyncMetaMappingRecord = {
|
||||||
localKey: localKey,
|
localKey: localKey,
|
||||||
@ -221,19 +337,24 @@ export const upsertSyncMetaMappingData = async (
|
|||||||
remoteExtraKey: remoteExtraKey,
|
remoteExtraKey: remoteExtraKey,
|
||||||
remoteType: serviceType,
|
remoteType: serviceType,
|
||||||
keyType: localKey.endsWith("/") ? "folder" : "file",
|
keyType: localKey.endsWith("/") ? "folder" : "file",
|
||||||
|
vaultRandomID: vaultRandomID,
|
||||||
};
|
};
|
||||||
await db.syncMappingTbl.setItem(remoteKey, aggregratedInfo);
|
await db.syncMappingTbl.setItem(
|
||||||
|
`${vaultRandomID}\t${remoteKey}`,
|
||||||
|
aggregratedInfo
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSyncMetaMappingByRemoteKey = async (
|
export const getSyncMetaMappingByRemoteKeyAndVault = async (
|
||||||
serviceType: SUPPORTED_SERVICES_TYPE,
|
serviceType: SUPPORTED_SERVICES_TYPE,
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
remoteKey: string,
|
remoteKey: string,
|
||||||
remoteMTime: number,
|
remoteMTime: number,
|
||||||
remoteExtraKey: string
|
remoteExtraKey: string,
|
||||||
|
vaultRandomID: string
|
||||||
) => {
|
) => {
|
||||||
const potentialItem = (await db.syncMappingTbl.getItem(
|
const potentialItem = (await db.syncMappingTbl.getItem(
|
||||||
remoteKey
|
`${vaultRandomID}\t${remoteKey}`
|
||||||
)) as SyncMetaMappingRecord;
|
)) as SyncMetaMappingRecord;
|
||||||
|
|
||||||
if (potentialItem === null) {
|
if (potentialItem === null) {
|
||||||
@ -258,26 +379,33 @@ export const clearAllSyncMetaMapping = async (db: InternalDBs) => {
|
|||||||
await db.syncMappingTbl.clear();
|
await db.syncMappingTbl.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertSyncPlanRecord = async (
|
export const insertSyncPlanRecordByVault = async (
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
syncPlan: SyncPlanType
|
syncPlan: SyncPlanType,
|
||||||
|
vaultRandomID: string
|
||||||
) => {
|
) => {
|
||||||
const record = {
|
const record = {
|
||||||
ts: syncPlan.ts,
|
ts: syncPlan.ts,
|
||||||
|
vaultRandomID: vaultRandomID,
|
||||||
remoteType: syncPlan.remoteType,
|
remoteType: syncPlan.remoteType,
|
||||||
syncPlan: JSON.stringify(syncPlan /* directly stringify */, null, 2),
|
syncPlan: JSON.stringify(syncPlan /* directly stringify */, null, 2),
|
||||||
} as SyncPlanRecord;
|
} as SyncPlanRecord;
|
||||||
await db.syncPlansTbl.setItem(`${syncPlan.ts}`, record);
|
await db.syncPlansTbl.setItem(`${vaultRandomID}\t${syncPlan.ts}`, record);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearAllSyncPlanRecords = async (db: InternalDBs) => {
|
export const clearAllSyncPlanRecords = async (db: InternalDBs) => {
|
||||||
await db.syncPlansTbl.clear();
|
await db.syncPlansTbl.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const readAllSyncPlanRecordTexts = async (db: InternalDBs) => {
|
export const readAllSyncPlanRecordTextsByVault = async (
|
||||||
|
db: InternalDBs,
|
||||||
|
vaultRandomID: string
|
||||||
|
) => {
|
||||||
const records = [] as SyncPlanRecord[];
|
const records = [] as SyncPlanRecord[];
|
||||||
await db.syncPlansTbl.iterate((value, key, iterationNumber) => {
|
await db.syncPlansTbl.iterate((value, key, iterationNumber) => {
|
||||||
records.push(value as SyncPlanRecord);
|
if (key.startsWith(`${vaultRandomID}\t`)) {
|
||||||
|
records.push(value as SyncPlanRecord);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
records.sort((a, b) => -(a.ts - b.ts)); // descending
|
records.sort((a, b) => -(a.ts - b.ts)); // descending
|
||||||
|
|
||||||
|
|||||||
51
src/main.ts
51
src/main.ts
@ -1,5 +1,6 @@
|
|||||||
import { Modal, Notice, Plugin, Setting } from "obsidian";
|
import { Modal, Notice, Plugin, Setting } from "obsidian";
|
||||||
import cloneDeep from "lodash/cloneDeep";
|
import cloneDeep from "lodash/cloneDeep";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import type { RemotelySavePluginSettings } from "./baseTypes";
|
import type { RemotelySavePluginSettings } from "./baseTypes";
|
||||||
import {
|
import {
|
||||||
COMMAND_CALLBACK,
|
COMMAND_CALLBACK,
|
||||||
@ -10,10 +11,10 @@ import {
|
|||||||
import { importQrCodeUri } from "./importExport";
|
import { importQrCodeUri } from "./importExport";
|
||||||
import type { InternalDBs } from "./localdb";
|
import type { InternalDBs } from "./localdb";
|
||||||
import {
|
import {
|
||||||
insertDeleteRecord,
|
insertDeleteRecordByVault,
|
||||||
insertRenameRecord,
|
insertRenameRecordByVault,
|
||||||
insertSyncPlanRecord,
|
insertSyncPlanRecordByVault,
|
||||||
loadDeleteRenameHistoryTable,
|
loadDeleteRenameHistoryTableByVault,
|
||||||
prepareDBs,
|
prepareDBs,
|
||||||
} from "./localdb";
|
} from "./localdb";
|
||||||
import { RemoteClient } from "./remote";
|
import { RemoteClient } from "./remote";
|
||||||
@ -47,6 +48,7 @@ const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
|||||||
password: "",
|
password: "",
|
||||||
serviceType: "s3",
|
serviceType: "s3",
|
||||||
currLogLevel: "info",
|
currLogLevel: "info",
|
||||||
|
vaultRandomID: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
interface OAuth2Info {
|
interface OAuth2Info {
|
||||||
@ -86,6 +88,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.checkIfOauthExpires();
|
await this.checkIfOauthExpires();
|
||||||
|
await this.checkIfVaultIDAssigned(); // MUST before prepareDB()
|
||||||
|
|
||||||
await this.prepareDB();
|
await this.prepareDB();
|
||||||
|
|
||||||
@ -93,13 +96,22 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.vault.on("delete", async (fileOrFolder) => {
|
this.app.vault.on("delete", async (fileOrFolder) => {
|
||||||
await insertDeleteRecord(this.db, fileOrFolder);
|
await insertDeleteRecordByVault(
|
||||||
|
this.db,
|
||||||
|
fileOrFolder,
|
||||||
|
this.settings.vaultRandomID
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.vault.on("rename", async (fileOrFolder, oldPath) => {
|
this.app.vault.on("rename", async (fileOrFolder, oldPath) => {
|
||||||
await insertRenameRecord(this.db, fileOrFolder, oldPath);
|
await insertRenameRecordByVault(
|
||||||
|
this.db,
|
||||||
|
fileOrFolder,
|
||||||
|
oldPath,
|
||||||
|
this.settings.vaultRandomID
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -316,7 +328,10 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
new Notice("3/7 Starting to fetch local meta data.");
|
new Notice("3/7 Starting to fetch local meta data.");
|
||||||
this.syncStatus = "getting_local_meta";
|
this.syncStatus = "getting_local_meta";
|
||||||
const local = this.app.vault.getAllLoadedFiles();
|
const local = this.app.vault.getAllLoadedFiles();
|
||||||
const localHistory = await loadDeleteRenameHistoryTable(this.db);
|
const localHistory = await loadDeleteRenameHistoryTableByVault(
|
||||||
|
this.db,
|
||||||
|
this.settings.vaultRandomID
|
||||||
|
);
|
||||||
// log.info(local);
|
// log.info(local);
|
||||||
// log.info(localHistory);
|
// log.info(localHistory);
|
||||||
|
|
||||||
@ -338,11 +353,16 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
local,
|
local,
|
||||||
localHistory,
|
localHistory,
|
||||||
this.db,
|
this.db,
|
||||||
|
this.settings.vaultRandomID,
|
||||||
client.serviceType,
|
client.serviceType,
|
||||||
this.settings.password
|
this.settings.password
|
||||||
);
|
);
|
||||||
log.info(syncPlan.mixedStates); // for debugging
|
log.info(syncPlan.mixedStates); // for debugging
|
||||||
await insertSyncPlanRecord(this.db, syncPlan);
|
await insertSyncPlanRecordByVault(
|
||||||
|
this.db,
|
||||||
|
syncPlan,
|
||||||
|
this.settings.vaultRandomID
|
||||||
|
);
|
||||||
|
|
||||||
// The operations above are read only and kind of safe.
|
// The operations above are read only and kind of safe.
|
||||||
// The operations below begins to write or delete (!!!) something.
|
// The operations below begins to write or delete (!!!) something.
|
||||||
@ -353,6 +373,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
await doActualSync(
|
await doActualSync(
|
||||||
client,
|
client,
|
||||||
this.db,
|
this.db,
|
||||||
|
this.settings.vaultRandomID,
|
||||||
this.app.vault,
|
this.app.vault,
|
||||||
syncPlan,
|
syncPlan,
|
||||||
this.settings.password,
|
this.settings.password,
|
||||||
@ -481,8 +502,18 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkIfVaultIDAssigned() {
|
||||||
|
if (
|
||||||
|
this.settings.vaultRandomID === undefined ||
|
||||||
|
this.settings.vaultRandomID === ""
|
||||||
|
) {
|
||||||
|
this.settings.vaultRandomID = nanoid();
|
||||||
|
await this.saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async prepareDB() {
|
async prepareDB() {
|
||||||
this.db = await prepareDBs();
|
this.db = await prepareDBs(this.settings.vaultRandomID);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyDBs() {
|
destroyDBs() {
|
||||||
@ -495,7 +526,7 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
pathName: string,
|
pathName: string,
|
||||||
decision: string
|
decision: string
|
||||||
) {
|
) {
|
||||||
const msg = `${i}/${totalCount}, ${decision}, ${pathName}`;
|
const msg = `syncing progress=${i}/${totalCount},decision=${decision},path=${pathName}`;
|
||||||
this.currSyncMsg = msg;
|
this.currSyncMsg = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { App, Modal, Notice, PluginSettingTab, Setting } from "obsidian";
|
import { App, Modal, Notice, PluginSettingTab, Setting } from "obsidian";
|
||||||
import type { SUPPORTED_SERVICES_TYPE, WebdavAuthType } from "./baseTypes";
|
import type { SUPPORTED_SERVICES_TYPE, WebdavAuthType } from "./baseTypes";
|
||||||
import { exportSyncPlansToFiles } from "./debugMode";
|
import { exportVaultSyncPlansToFiles } from "./debugMode";
|
||||||
import { exportQrCodeUri } from "./importExport";
|
import { exportQrCodeUri } from "./importExport";
|
||||||
import {
|
import {
|
||||||
clearAllSyncMetaMapping,
|
clearAllSyncMetaMapping,
|
||||||
@ -19,6 +19,7 @@ import {
|
|||||||
DEFAULT_ONEDRIVE_CONFIG,
|
DEFAULT_ONEDRIVE_CONFIG,
|
||||||
getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive,
|
getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive,
|
||||||
} from "./remoteForOnedrive";
|
} from "./remoteForOnedrive";
|
||||||
|
import { messyConfigToNormal } from "./configPersist";
|
||||||
|
|
||||||
import * as origLog from "loglevel";
|
import * as origLog from "loglevel";
|
||||||
const log = origLog.getLogger("rs-default");
|
const log = origLog.getLogger("rs-default");
|
||||||
@ -946,6 +947,24 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
log.info(`the log level is changed to ${val}`);
|
log.info(`the log level is changed to ${val}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
const outputCurrSettingsDiv = debugDiv.createDiv("div");
|
||||||
|
new Setting(outputCurrSettingsDiv)
|
||||||
|
.setName("output current settings from disk to console")
|
||||||
|
.setDesc(
|
||||||
|
"The settings save on disk in encoded. Click this to see the decoded settings in console."
|
||||||
|
)
|
||||||
|
.addButton(async (button) => {
|
||||||
|
button.setButtonText("Output");
|
||||||
|
button.onClick(async () => {
|
||||||
|
const c = messyConfigToNormal(await this.plugin.loadData());
|
||||||
|
if (c.currLogLevel === "debug") {
|
||||||
|
// no need to ouput it again because debug mode already output it
|
||||||
|
} else {
|
||||||
|
log.info(c);
|
||||||
|
}
|
||||||
|
new Notice("Finished outputing in console.");
|
||||||
|
});
|
||||||
|
});
|
||||||
const syncPlanDiv = debugDiv.createEl("div");
|
const syncPlanDiv = debugDiv.createEl("div");
|
||||||
new Setting(syncPlanDiv)
|
new Setting(syncPlanDiv)
|
||||||
.setName("export sync plans")
|
.setName("export sync plans")
|
||||||
@ -955,7 +974,11 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
|||||||
.addButton(async (button) => {
|
.addButton(async (button) => {
|
||||||
button.setButtonText("Export");
|
button.setButtonText("Export");
|
||||||
button.onClick(async () => {
|
button.onClick(async () => {
|
||||||
await exportSyncPlansToFiles(this.plugin.db, this.app.vault);
|
await exportVaultSyncPlansToFiles(
|
||||||
|
this.plugin.db,
|
||||||
|
this.app.vault,
|
||||||
|
this.plugin.settings.vaultRandomID
|
||||||
|
);
|
||||||
new Notice("sync plans history exported");
|
new Notice("sync plans history exported");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
35
src/sync.ts
35
src/sync.ts
@ -9,9 +9,9 @@ import {
|
|||||||
} from "./encrypt";
|
} from "./encrypt";
|
||||||
import type { FileFolderHistoryRecord, InternalDBs } from "./localdb";
|
import type { FileFolderHistoryRecord, InternalDBs } from "./localdb";
|
||||||
import {
|
import {
|
||||||
clearDeleteRenameHistoryOfKey,
|
clearDeleteRenameHistoryOfKeyAndVault,
|
||||||
getSyncMetaMappingByRemoteKey,
|
getSyncMetaMappingByRemoteKeyAndVault,
|
||||||
upsertSyncMetaMappingData,
|
upsertSyncMetaMappingDataByVault,
|
||||||
} from "./localdb";
|
} from "./localdb";
|
||||||
import { isHiddenPath, isVaildText, mkdirpInVault } from "./misc";
|
import { isHiddenPath, isVaildText, mkdirpInVault } from "./misc";
|
||||||
import { RemoteClient } from "./remote";
|
import { RemoteClient } from "./remote";
|
||||||
@ -170,6 +170,7 @@ const ensembleMixedStates = async (
|
|||||||
local: TAbstractFile[],
|
local: TAbstractFile[],
|
||||||
deleteHistory: FileFolderHistoryRecord[],
|
deleteHistory: FileFolderHistoryRecord[],
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
|
vaultRandomID: string,
|
||||||
remoteType: SUPPORTED_SERVICES_TYPE,
|
remoteType: SUPPORTED_SERVICES_TYPE,
|
||||||
password: string = ""
|
password: string = ""
|
||||||
) => {
|
) => {
|
||||||
@ -190,12 +191,13 @@ const ensembleMixedStates = async (
|
|||||||
throw Error(`unexpected key=${remoteEncryptedKey}`);
|
throw Error(`unexpected key=${remoteEncryptedKey}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const backwardMapping = await getSyncMetaMappingByRemoteKey(
|
const backwardMapping = await getSyncMetaMappingByRemoteKeyAndVault(
|
||||||
remoteType,
|
remoteType,
|
||||||
db,
|
db,
|
||||||
key,
|
key,
|
||||||
entry.lastModified,
|
entry.lastModified,
|
||||||
entry.etag
|
entry.etag,
|
||||||
|
vaultRandomID
|
||||||
);
|
);
|
||||||
|
|
||||||
let r = {} as FileOrFolderMixedState;
|
let r = {} as FileOrFolderMixedState;
|
||||||
@ -443,6 +445,7 @@ export const getSyncPlan = async (
|
|||||||
local: TAbstractFile[],
|
local: TAbstractFile[],
|
||||||
deleteHistory: FileFolderHistoryRecord[],
|
deleteHistory: FileFolderHistoryRecord[],
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
|
vaultRandomID: string,
|
||||||
remoteType: SUPPORTED_SERVICES_TYPE,
|
remoteType: SUPPORTED_SERVICES_TYPE,
|
||||||
password: string = ""
|
password: string = ""
|
||||||
) => {
|
) => {
|
||||||
@ -451,6 +454,7 @@ export const getSyncPlan = async (
|
|||||||
local,
|
local,
|
||||||
deleteHistory,
|
deleteHistory,
|
||||||
db,
|
db,
|
||||||
|
vaultRandomID,
|
||||||
remoteType,
|
remoteType,
|
||||||
password
|
password
|
||||||
);
|
);
|
||||||
@ -467,6 +471,7 @@ export const getSyncPlan = async (
|
|||||||
|
|
||||||
const dispatchOperationToActual = async (
|
const dispatchOperationToActual = async (
|
||||||
key: string,
|
key: string,
|
||||||
|
vaultRandomID: string,
|
||||||
state: FileOrFolderMixedState,
|
state: FileOrFolderMixedState,
|
||||||
client: RemoteClient,
|
client: RemoteClient,
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
@ -501,7 +506,7 @@ const dispatchOperationToActual = async (
|
|||||||
password,
|
password,
|
||||||
remoteEncryptedKey
|
remoteEncryptedKey
|
||||||
);
|
);
|
||||||
await clearDeleteRenameHistoryOfKey(db, state.key);
|
await clearDeleteRenameHistoryOfKeyAndVault(db, state.key, vaultRandomID);
|
||||||
} else if (state.decision === "upload_clearhist") {
|
} else if (state.decision === "upload_clearhist") {
|
||||||
const remoteObjMeta = await client.uploadToRemote(
|
const remoteObjMeta = await client.uploadToRemote(
|
||||||
state.key,
|
state.key,
|
||||||
@ -511,7 +516,7 @@ const dispatchOperationToActual = async (
|
|||||||
remoteEncryptedKey,
|
remoteEncryptedKey,
|
||||||
foldersCreatedBefore
|
foldersCreatedBefore
|
||||||
);
|
);
|
||||||
await upsertSyncMetaMappingData(
|
await upsertSyncMetaMappingDataByVault(
|
||||||
client.serviceType,
|
client.serviceType,
|
||||||
db,
|
db,
|
||||||
state.key,
|
state.key,
|
||||||
@ -520,9 +525,10 @@ const dispatchOperationToActual = async (
|
|||||||
state.key,
|
state.key,
|
||||||
remoteObjMeta.lastModified,
|
remoteObjMeta.lastModified,
|
||||||
remoteObjMeta.size,
|
remoteObjMeta.size,
|
||||||
remoteObjMeta.etag
|
remoteObjMeta.etag,
|
||||||
|
vaultRandomID
|
||||||
);
|
);
|
||||||
await clearDeleteRenameHistoryOfKey(db, state.key);
|
await clearDeleteRenameHistoryOfKeyAndVault(db, state.key, vaultRandomID);
|
||||||
} else if (state.decision === "download") {
|
} else if (state.decision === "download") {
|
||||||
await mkdirpInVault(state.key, vault);
|
await mkdirpInVault(state.key, vault);
|
||||||
await client.downloadFromRemote(
|
await client.downloadFromRemote(
|
||||||
@ -534,7 +540,7 @@ const dispatchOperationToActual = async (
|
|||||||
);
|
);
|
||||||
} else if (state.decision === "delremote_clearhist") {
|
} else if (state.decision === "delremote_clearhist") {
|
||||||
await client.deleteFromRemote(state.key, password, remoteEncryptedKey);
|
await client.deleteFromRemote(state.key, password, remoteEncryptedKey);
|
||||||
await clearDeleteRenameHistoryOfKey(db, state.key);
|
await clearDeleteRenameHistoryOfKeyAndVault(db, state.key, vaultRandomID);
|
||||||
} else if (state.decision === "upload") {
|
} else if (state.decision === "upload") {
|
||||||
const remoteObjMeta = await client.uploadToRemote(
|
const remoteObjMeta = await client.uploadToRemote(
|
||||||
state.key,
|
state.key,
|
||||||
@ -544,7 +550,7 @@ const dispatchOperationToActual = async (
|
|||||||
remoteEncryptedKey,
|
remoteEncryptedKey,
|
||||||
foldersCreatedBefore
|
foldersCreatedBefore
|
||||||
);
|
);
|
||||||
await upsertSyncMetaMappingData(
|
await upsertSyncMetaMappingDataByVault(
|
||||||
client.serviceType,
|
client.serviceType,
|
||||||
db,
|
db,
|
||||||
state.key,
|
state.key,
|
||||||
@ -553,10 +559,11 @@ const dispatchOperationToActual = async (
|
|||||||
state.key,
|
state.key,
|
||||||
remoteObjMeta.lastModified,
|
remoteObjMeta.lastModified,
|
||||||
remoteObjMeta.size,
|
remoteObjMeta.size,
|
||||||
remoteObjMeta.etag
|
remoteObjMeta.etag,
|
||||||
|
vaultRandomID
|
||||||
);
|
);
|
||||||
} else if (state.decision === "clearhist") {
|
} else if (state.decision === "clearhist") {
|
||||||
await clearDeleteRenameHistoryOfKey(db, state.key);
|
await clearDeleteRenameHistoryOfKeyAndVault(db, state.key, vaultRandomID);
|
||||||
} else {
|
} else {
|
||||||
throw Error("this should never happen!");
|
throw Error("this should never happen!");
|
||||||
}
|
}
|
||||||
@ -565,6 +572,7 @@ const dispatchOperationToActual = async (
|
|||||||
export const doActualSync = async (
|
export const doActualSync = async (
|
||||||
client: RemoteClient,
|
client: RemoteClient,
|
||||||
db: InternalDBs,
|
db: InternalDBs,
|
||||||
|
vaultRandomID: string,
|
||||||
vault: Vault,
|
vault: Vault,
|
||||||
syncPlan: SyncPlanType,
|
syncPlan: SyncPlanType,
|
||||||
password: string = "",
|
password: string = "",
|
||||||
@ -586,6 +594,7 @@ export const doActualSync = async (
|
|||||||
}
|
}
|
||||||
await dispatchOperationToActual(
|
await dispatchOperationToActual(
|
||||||
k2,
|
k2,
|
||||||
|
vaultRandomID,
|
||||||
v2,
|
v2,
|
||||||
client,
|
client,
|
||||||
db,
|
db,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user