diff --git a/plugin/src/events/sync-event-handler.ts b/plugin/src/events/sync-event-handler.ts index a73d117..9ca66cc 100644 --- a/plugin/src/events/sync-event-handler.ts +++ b/plugin/src/events/sync-event-handler.ts @@ -1,27 +1,106 @@ import { TAbstractFile, TFile } from "obsidian"; import { FileEventHandler } from "./file-event-handler"; import { Logger } from "src/logger"; -import { Syncer } from "src/syncer/syncer"; +import { SyncServer } from "src/services/sync_service"; +import { Database } from "src/database/database"; export class SyncEventHandler implements FileEventHandler { - constructor(private syncer: Syncer) {} + constructor(private database: Database, private syncServer: SyncServer) {} - async onCreate(file: TAbstractFile) { + async onCreate(file: TAbstractFile): Promise { if (file instanceof TFile) { - Logger.getInstance().info(`File created: ${file}`); - this.syncer.onCreate(file.path, await file.vault.read(file)); + Logger.getInstance().info(`File created: ${file.path}`); + + const result = await this.syncServer.create({ + relativePath: file.path, + content: await file.vault.readBinary(file), + createdDate: new Date(file.stat.ctime), + }); + + await this.database.setDocument({ + relativePath: file.path, + documentId: result.documentId, + parentVersionId: result.versionId, + }); + } else { + Logger.getInstance().info(`Folder created: ${file.path}, ignored`); } } - onDelete(file: TAbstractFile) { - Logger.getInstance().info(`File deleted: ${file}`); + async onDelete(file: TAbstractFile): Promise { + if (file instanceof TFile) { + Logger.getInstance().info(`File deleted: ${file.path}`); + + const metadata = this.database.getDocument(file.path); + if (!metadata) { + throw `Document metadata not found for ${file.path}`; + } + + await this.syncServer.delete({ + documentId: metadata.documentId, + createdDate: new Date(), // We got the event now, so it must have been deleted now + }); + + await this.database.removeDocument(file.path); + } else { + Logger.getInstance().info(`Folder deleted: ${file.path}, ignored`); + } } - onRename(file: TAbstractFile, oldPath: string) { - Logger.getInstance().info(`File renamed: ${oldPath} -> ${file}`); + async onRename(file: TAbstractFile, oldPath: string): Promise { + Logger.getInstance().info(`File renamed: ${oldPath} -> ${file.path}`); + + if (file instanceof TFile) { + const metadata = this.database.getDocument(oldPath); + if (!metadata) { + throw `Document metadata not found for ${oldPath}`; + } + + const response = await this.syncServer.update({ + documentId: metadata.documentId, + parentVersionId: metadata.parentVersionId, + relativePath: file.path, + content: await file.vault.readBinary(file), + createdDate: new Date(file.stat.ctime), + }); + + await this.database.moveDocument({ + oldRelativePath: oldPath, + relativePath: file.path, + documentId: response.documentId, + parentVersionId: response.versionId, + }); + } else { + Logger.getInstance().info( + `Folder renamed: ${oldPath} -> ${file.path}, ignored` + ); + } } - onModify(file: TAbstractFile) { - Logger.getInstance().info(`File modified: ${file}`); + async onModify(file: TAbstractFile): Promise { + Logger.getInstance().info(`File modified: ${file.path}`); + + if (file instanceof TFile) { + const metadata = this.database.getDocument(file.path); + if (!metadata) { + throw `Document metadata not found for ${file.path}`; + } + + const response = await this.syncServer.update({ + documentId: metadata.documentId, + parentVersionId: metadata.parentVersionId, + relativePath: file.path, + content: await file.vault.readBinary(file), + createdDate: new Date(file.stat.ctime), + }); + + await this.database.setDocument({ + relativePath: file.path, + documentId: response.documentId, + parentVersionId: response.versionId, + }); + } else { + Logger.getInstance().info(`Folder modified: ${file.path}, ignored`); + } } } diff --git a/plugin/src/services/sync_service.ts b/plugin/src/services/sync_service.ts index c2916b3..90f0a90 100644 --- a/plugin/src/services/sync_service.ts +++ b/plugin/src/services/sync_service.ts @@ -1,22 +1,22 @@ -import { - SettingsContainer, - SyncSettings, -} from "src/database/settings/settings.js"; - import * as plugin from "../../../backend/sync_lib/pkg/sync_lib.js"; import createClient, { Client } from "openapi-fetch"; import type { components, paths } from "./types.js"; // generated by openapi-typescript import { Logger } from "src/logger.js"; -import { DocumentId, DocumentVersionId } from "src/database/database.js"; +import { + Database, + DocumentId, + DocumentVersionId, + SyncSettings, +} from "src/database/database.js"; export class SyncServer { private static VAULT_ID = "default"; private client: Client; - public constructor(private settings: SettingsContainer) { - this.createClient(settings.getSettings()); - settings.onChange((s) => this.createClient(s)); + public constructor(private database: Database) { + this.createClient(database.getSettings()); + database.addOnSettingsChangeHandlers((s) => this.createClient(s)); } private createClient(settings: SyncSettings) { @@ -25,6 +25,20 @@ export class SyncServer { }); } + public async ping(): Promise { + const response = await this.client.GET("/ping"); + + Logger.getInstance().info( + "Ping response: " + JSON.stringify(response.data) + ); + + if (!response.data) { + throw new Error(`Failed to ping server: ${response.error}`); + } + + return response.data; + } + public async create({ relativePath, content, @@ -33,16 +47,14 @@ export class SyncServer { content: ArrayBuffer; relativePath: string; createdDate: Date; - }): Promise< - components["schemas"]["DocumentVersionWithoutContent"] | undefined - > { + }): Promise { let contentBytes = new Uint8Array(content); let response = await this.client.POST("/vaults/{vault_id}/documents", { params: { - path: { vaultId: SyncServer.VAULT_ID }, + path: { vault_id: SyncServer.VAULT_ID }, header: { authorization: - "Bearer " + this.settings.getSettings().token, + "Bearer " + this.database.getSettings().token, }, }, body: { @@ -53,6 +65,10 @@ export class SyncServer { }, }); + if (!response.data) { + throw new Error(`Failed to create document: ${response.error}`); + } + Logger.getInstance().info( "Created document " + JSON.stringify(response.data) ); @@ -72,9 +88,7 @@ export class SyncServer { relativePath: string; content: ArrayBuffer; createdDate: Date; - }): Promise< - components["schemas"]["DocumentVersionWithoutContent"] | undefined - > { + }): Promise { let contentBytes = new Uint8Array(content); let response = await this.client.PUT( @@ -82,12 +96,12 @@ export class SyncServer { { params: { path: { - vaultId: SyncServer.VAULT_ID, - documentId, + vault_id: SyncServer.VAULT_ID, + document_id: documentId, }, header: { authorization: - "Bearer " + this.settings.getSettings().token, + "Bearer " + this.database.getSettings().token, }, }, body: { @@ -100,6 +114,10 @@ export class SyncServer { } ); + if (!response.data) { + throw new Error(`Failed to create document: ${response.error}`); + } + Logger.getInstance().info( "Updated document " + JSON.stringify(response.data) ); @@ -119,12 +137,12 @@ export class SyncServer { { params: { path: { - vaultId: SyncServer.VAULT_ID, - documentId, + vault_id: SyncServer.VAULT_ID, + document_id: documentId, }, header: { authorization: - "Bearer " + this.settings.getSettings().token, + "Bearer " + this.database.getSettings().token, }, }, body: { @@ -133,6 +151,11 @@ export class SyncServer { } ); + // Response will be empty if successful + // if (!response.data) { + // throw new Error(`Failed to delete document: ${response.error}`); + // } + Logger.getInstance().info( "Updated document " + JSON.stringify(response.data) ); @@ -144,23 +167,27 @@ export class SyncServer { documentId, }: { documentId: DocumentId; - }): Promise { + }): Promise { const response = await this.client.GET( "/vaults/{vault_id}/documents/{document_id}", { params: { path: { - vaultId: SyncServer.VAULT_ID, - documentId, + vault_id: SyncServer.VAULT_ID, + document_id: documentId, }, header: { authorization: - "Bearer " + this.settings.getSettings().token, + "Bearer " + this.database.getSettings().token, }, }, } ); + if (!response.data) { + throw new Error(`Failed to get document: ${response.error}`); + } + Logger.getInstance().info( "Get document " + JSON.stringify(response.data) ); @@ -169,20 +196,24 @@ export class SyncServer { } public async getAll(): Promise< - components["schemas"]["DocumentVersionWithoutContent"][] | undefined + components["schemas"]["DocumentVersionWithoutContent"][] > { const response = await this.client.GET("/vaults/{vault_id}/documents", { params: { path: { - vaultId: SyncServer.VAULT_ID, + vault_id: SyncServer.VAULT_ID, }, header: { authorization: - "Bearer " + this.settings.getSettings().token, + "Bearer " + this.database.getSettings().token, }, }, }); + if (!response.data) { + throw new Error(`Failed to get documents: ${response.error}`); + } + Logger.getInstance().info( "Get document " + JSON.stringify(response.data) );