From c75962aad56b1c8b98c5148fed9d730ae26be28f Mon Sep 17 00:00:00 2001 From: fyears Date: Sat, 23 Oct 2021 12:02:03 +0800 Subject: [PATCH] track local history --- package.json | 1 + src/localdb.ts | 53 ++++++++++++++++++++ main.ts => src/main.ts | 111 ++++++++++++++++++++++++++++++++++++++++- webpack.config.js | 2 +- 4 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 src/localdb.ts rename main.ts => src/main.ts (74%) diff --git a/package.json b/package.json index 4d37625..d6e67ab 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@types/mime-types": "^2.1.1", "aws-crt": "^1.10.1", "codemirror": "^5.63.1", + "lovefield-ts": "^0.7.0", "mime-types": "^2.1.33", "obsidian": "^0.12.0", "rimraf": "^3.0.2", diff --git a/src/localdb.ts b/src/localdb.ts new file mode 100644 index 0000000..ba8cbe2 --- /dev/null +++ b/src/localdb.ts @@ -0,0 +1,53 @@ +import * as lf from "lovefield-ts/dist/es6/lf.js"; + +export type DatabaseConnection = lf.DatabaseConnection; + +export const DEFAULT_DB_NAME = "saveremotedb"; +export const DEFAULT_TBL_DELETE_HISTORY = "filefolderoperationhistory"; + +export interface FileFolderHistoryRecord { + key: string; + ctime: number; + mtime: number; + size: number; + action_when: number; + action_type: "delete" | "rename"; + key_type: "folder" | "file"; + rename_to: string; +} + +export async function prepareDB() { + const schemaBuilder = lf.schema.create(DEFAULT_DB_NAME, 1); + schemaBuilder + .createTable(DEFAULT_TBL_DELETE_HISTORY) + .addColumn("id", lf.Type.INTEGER) + .addColumn("key", lf.Type.STRING) + .addColumn("ctime", lf.Type.INTEGER) + .addColumn("mtime", lf.Type.INTEGER) + .addColumn("size", lf.Type.INTEGER) + .addColumn("action_when", lf.Type.INTEGER) + .addColumn("action_type", lf.Type.STRING) + .addColumn("key_type", lf.Type.STRING) + .addPrimaryKey(["id"], true) + .addIndex("idxKey", ["key"]); + const db = await schemaBuilder.connect({ + storeType: lf.DataStoreType.INDEXED_DB, + }); + console.log("db connected"); + return db; +} + +export function destroyDB(db: lf.DatabaseConnection) { + db.close(); + const req = indexedDB.deleteDatabase(DEFAULT_DB_NAME); + req.onsuccess = (event) => { + console.log("db deleted"); + }; + req.onblocked = (event) => { + console.warn("trying to delete db but it was blocked"); + }; + req.onerror = (event) => { + console.error("tried to delete db but something bad!"); + console.error(event); + }; +} diff --git a/main.ts b/src/main.ts similarity index 74% rename from main.ts rename to src/main.ts index 214f243..7524014 100644 --- a/main.ts +++ b/src/main.ts @@ -12,12 +12,21 @@ import { Setting, request, Platform, + TFile, + TFolder, } from "obsidian"; import * as CodeMirror from "codemirror"; +import type { FileFolderHistoryRecord, DatabaseConnection } from "./localdb"; +import { + prepareDB, + destroyDB, + DEFAULT_DB_NAME, + DEFAULT_TBL_DELETE_HISTORY, +} from "./localdb"; import { S3Client, - ListObjectsCommand, + ListObjectsV2Command, PutObjectCommand, GetObjectCommand, } from "@aws-sdk/client-s3"; @@ -103,12 +112,96 @@ const getObjectBodyToArrayBuffer = async ( export default class SaveRemotePlugin extends Plugin { settings: SaveRemotePluginSettings; cm: CodeMirror.Editor; + db: DatabaseConnection; async onload() { console.log("loading plugin obsidian-save-remote"); await this.loadSettings(); + await this.prepareDB(); + + this.registerEvent( + this.app.vault.on("delete", async (fileOrFolder) => { + const schema = this.db.getSchema().table(DEFAULT_TBL_DELETE_HISTORY); + const tbl = this.db.getSchema().table(DEFAULT_TBL_DELETE_HISTORY); + // console.log(fileOrFolder); + let k: FileFolderHistoryRecord; + if (fileOrFolder instanceof TFile) { + k = { + key: fileOrFolder.path, + ctime: fileOrFolder.stat.ctime, + mtime: fileOrFolder.stat.mtime, + size: fileOrFolder.stat.size, + action_when: Date.now(), + action_type: "delete", + key_type: "file", + rename_to: "", + }; + } else if (fileOrFolder instanceof TFolder) { + k = { + key: fileOrFolder.path, + ctime: 0, + mtime: 0, + size: 0, + action_when: Date.now(), + action_type: "delete", + key_type: "folder", + rename_to: "", + }; + } + const row = tbl.createRow(k); + await this.db.insertOrReplace().into(tbl).values([row]).exec(); + }) + ); + + this.registerEvent( + this.app.vault.on("rename", async (fileOrFolder, oldPath) => { + const schema = this.db.getSchema().table(DEFAULT_TBL_DELETE_HISTORY); + const tbl = this.db.getSchema().table(DEFAULT_TBL_DELETE_HISTORY); + // console.log(fileOrFolder); + let k: FileFolderHistoryRecord; + if (fileOrFolder instanceof TFile) { + k = { + key: oldPath, + ctime: fileOrFolder.stat.ctime, + mtime: fileOrFolder.stat.mtime, + size: fileOrFolder.stat.size, + action_when: Date.now(), + action_type: "rename", + key_type: "file", + rename_to: fileOrFolder.path, + }; + } else if (fileOrFolder instanceof TFolder) { + k = { + key: oldPath, + ctime: 0, + mtime: 0, + size: 0, + action_when: Date.now(), + action_type: "rename", + key_type: "folder", + rename_to: fileOrFolder.path, + }; + } + const row = tbl.createRow(k); + await this.db.insertOrReplace().into(tbl).values([row]).exec(); + }) + ); + + this.addRibbonIcon("dice", "Misc", async () => { + const a = this.app.vault.getAllLoadedFiles(); + console.log(a); + + const schema = this.db.getSchema().table(DEFAULT_TBL_DELETE_HISTORY); + + const h = await this.db.select().from(schema).exec(); + + console.log(h); + + // console.log(b) + }); + this.addRibbonIcon("right-arrow-with-tail", "Upload", async () => { // console.log(this.app.vault.getFiles()); // console.log(this.app.vault.getAllLoadedFiles()); @@ -183,10 +276,15 @@ export default class SaveRemotePlugin extends Plugin { try { const listObj = await s3Client.send( - new ListObjectsCommand({ Bucket: this.settings.s3BucketName }) + new ListObjectsV2Command({ Bucket: this.settings.s3BucketName }) ); for (const singleContent of listObj.Contents) { + const mtimeSec = Math.round( + singleContent.LastModified.valueOf() / 1000.0 + ); + console.log(`key ${singleContent.Key} mtime ${mtimeSec}`); + const foldersToBuild = getFolderLevels(singleContent.Key); for (const folder of foldersToBuild) { const r = await this.app.vault.adapter.exists(folder); @@ -246,6 +344,7 @@ export default class SaveRemotePlugin extends Plugin { onunload() { console.log("unloading plugin obsidian-save-remote"); + this.destroyDB(); } async loadSettings() { @@ -255,6 +354,14 @@ export default class SaveRemotePlugin extends Plugin { async saveSettings() { await this.saveData(this.settings); } + + async prepareDB() { + this.db = await prepareDB(); + } + + destroyDB() { + destroyDB(this.db); + } } class SaveRemoteSettingTab extends PluginSettingTab { diff --git a/webpack.config.js b/webpack.config.js index dd1621c..f848f65 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,7 +2,7 @@ const path = require("path"); const TerserPlugin = require("terser-webpack-plugin"); module.exports = { - entry: "./main.ts", + entry: "./src/main.ts", target: "web", output: { filename: "main.js",