diff --git a/src/main.ts b/src/main.ts index 7301e5d..5de201b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -834,7 +834,9 @@ class RemotelySaveSettingTab extends PluginSettingTab { const client = new RemoteClient( "webdav", undefined, - this.plugin.settings.webdav + this.plugin.settings.webdav, + undefined, + this.app.vault.getName() ); const res = await client.checkConnectivity(); if (res) { diff --git a/src/remote.ts b/src/remote.ts index 85cbf3e..d8fa343 100644 --- a/src/remote.ts +++ b/src/remote.ts @@ -14,7 +14,7 @@ export class RemoteClient { readonly serviceType: SUPPORTED_SERVICES_TYPE; readonly s3Client?: s3.S3Client; readonly s3Config?: S3Config; - readonly webdavClient?: webdav.WebDAVClient; + readonly webdavClient?: webdav.WrappedWebdavClient; readonly webdavConfig?: WebdavConfig; readonly dropboxClient?: dropbox.WrappedDropboxClient; readonly dropboxConfig?: DropboxConfig; @@ -34,8 +34,11 @@ export class RemoteClient { this.s3Config = s3Config; this.s3Client = s3.getS3Client(this.s3Config); } else if (serviceType === "webdav") { + if (vaultName === undefined) { + throw Error("remember to provide vault name while init webdav client"); + } this.webdavConfig = webdavConfig; - this.webdavClient = webdav.getWebdavClient(this.webdavConfig); + this.webdavClient = webdav.getWebdavClient(this.webdavConfig, vaultName); } else if (serviceType === "dropbox") { if (vaultName === undefined || saveUpdatedConfigFunc === undefined) { throw Error( diff --git a/src/remoteForWebdav.ts b/src/remoteForWebdav.ts index 06579f3..5cb7fd5 100644 --- a/src/remoteForWebdav.ts +++ b/src/remoteForWebdav.ts @@ -21,22 +21,35 @@ export const DEFAULT_WEBDAV_CONFIG = { authType: "basic", } as WebdavConfig; -const getWebdavPath = (fileOrFolderPath: string) => { +const getWebdavPath = (fileOrFolderPath: string, vaultName: string) => { + let key = fileOrFolderPath; + if (fileOrFolderPath === "/" || fileOrFolderPath === "") { + // special + key = `/${vaultName}/`; + } if (!fileOrFolderPath.startsWith("/")) { - return `/${fileOrFolderPath}`; + key = `/${vaultName}/${fileOrFolderPath}`; } - return fileOrFolderPath; + return key; }; -const getNormPath = (fileOrFolderPath: string) => { - if (fileOrFolderPath.startsWith("/")) { - return fileOrFolderPath.slice(1); +const getNormPath = (fileOrFolderPath: string, vaultName: string) => { + if ( + !( + fileOrFolderPath === `/${vaultName}` || + fileOrFolderPath.startsWith(`/${vaultName}/`) + ) + ) { + throw Error(`"${fileOrFolderPath}" doesn't starts with "/${vaultName}/"`); } - return fileOrFolderPath; + // if (fileOrFolderPath.startsWith("/")) { + // return fileOrFolderPath.slice(1); + // } + return fileOrFolderPath.slice(`/${vaultName}/`.length); }; -const fromWebdavItemToRemoteItem = (x: FileStat) => { - let key = getNormPath(x.filename); +const fromWebdavItemToRemoteItem = (x: FileStat, vaultName: string) => { + let key = getNormPath(x.filename, vaultName); if (x.type === "directory" && !key.endsWith("/")) { key = `${key}/`; } @@ -49,45 +62,90 @@ const fromWebdavItemToRemoteItem = (x: FileStat) => { } as RemoteItem; }; -export const getWebdavClient = (webdavConfig: WebdavConfig) => { - if (webdavConfig.username !== "" && webdavConfig.password !== "") { - return createClient(webdavConfig.address, { - username: webdavConfig.username, - password: webdavConfig.password, - authType: - webdavConfig.authType === "digest" - ? AuthType.Digest - : AuthType.Password, - }); - } else { - console.log("no password"); - return createClient(webdavConfig.address); +export class WrappedWebdavClient { + webdavConfig: WebdavConfig; + vaultName: string; + client: WebDAVClient; + vaultFolderExists: boolean; + constructor(webdavConfig: WebdavConfig, vaultName: string) { + this.webdavConfig = webdavConfig; + this.vaultName = vaultName; + this.vaultFolderExists = false; } + + init = async () => { + // init client if not inited + if (this.client === undefined) { + if ( + this.webdavConfig.username !== "" && + this.webdavConfig.password !== "" + ) { + this.client = createClient(this.webdavConfig.address, { + username: this.webdavConfig.username, + password: this.webdavConfig.password, + authType: + this.webdavConfig.authType === "digest" + ? AuthType.Digest + : AuthType.Password, + }); + } else { + console.log("no password"); + this.client = createClient(this.webdavConfig.address); + } + } + + // check vault folder + if (this.vaultFolderExists) { + // pass + } else { + const res = await this.client.exists(`/${this.vaultName}`); + if (res) { + console.log("exits!"); + this.vaultFolderExists = true; + } else { + console.log("not exists, creating"); + await this.client.createDirectory(`/${this.vaultName}`); + console.log("created!"); + this.vaultFolderExists = true; + } + } + }; +} + +export const getWebdavClient = ( + webdavConfig: WebdavConfig, + vaultName: string +) => { + return new WrappedWebdavClient(webdavConfig, vaultName); }; export const getRemoteMeta = async ( - client: WebDAVClient, + client: WrappedWebdavClient, fileOrFolderPath: string ) => { - const res = (await client.stat(getWebdavPath(fileOrFolderPath), { + await client.init(); + const remotePath = getWebdavPath(fileOrFolderPath, client.vaultName); + console.log(`remotePath = ${remotePath}`); + const res = (await client.client.stat(remotePath, { details: false, })) as FileStat; - return fromWebdavItemToRemoteItem(res); + return fromWebdavItemToRemoteItem(res, client.vaultName); }; export const uploadToRemote = async ( - client: WebDAVClient, + client: WrappedWebdavClient, fileOrFolderPath: string, vault: Vault, isRecursively: boolean = false, password: string = "", remoteEncryptedKey: string = "" ) => { + await client.init(); let uploadFile = fileOrFolderPath; if (password !== "") { uploadFile = remoteEncryptedKey; } - uploadFile = getWebdavPath(uploadFile); + uploadFile = getWebdavPath(uploadFile, client.vaultName); const isFolder = fileOrFolderPath.endsWith("/"); @@ -97,14 +155,14 @@ export const uploadToRemote = async ( // folder if (password === "") { // if not encrypted, mkdir a remote folder - await client.createDirectory(uploadFile, { + await client.client.createDirectory(uploadFile, { recursive: true, }); const res = await getRemoteMeta(client, uploadFile); return res; } else { // if encrypted, upload a fake file with the encrypted file name - await client.putFileContents(uploadFile, "", { + await client.client.putFileContents(uploadFile, "", { overwrite: true, onUploadProgress: (progress) => { console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`); @@ -124,9 +182,9 @@ export const uploadToRemote = async ( // we need to create folders before uploading const dir = getPathFolder(uploadFile); if (dir !== "/" && dir !== "") { - await client.createDirectory(dir, { recursive: true }); + await client.client.createDirectory(dir, { recursive: true }); } - await client.putFileContents(uploadFile, remoteContent, { + await client.client.putFileContents(uploadFile, remoteContent, { overwrite: true, onUploadProgress: (progress) => { console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`); @@ -137,26 +195,36 @@ export const uploadToRemote = async ( } }; -export const listFromRemote = async (client: WebDAVClient, prefix?: string) => { +export const listFromRemote = async ( + client: WrappedWebdavClient, + prefix?: string +) => { if (prefix !== undefined) { throw Error("prefix not supported"); } - const contents = (await client.getDirectoryContents("/", { - deep: true, - details: false /* no need for verbose details here */, - glob: "/**" /* avoid dot files by using glob */, - })) as FileStat[]; + await client.init(); + const contents = (await client.client.getDirectoryContents( + `/${client.vaultName}`, + { + deep: true, + details: false /* no need for verbose details here */, + glob: "/**" /* avoid dot files by using glob */, + } + )) as FileStat[]; return { - Contents: contents.map((x) => fromWebdavItemToRemoteItem(x)), + Contents: contents.map((x) => + fromWebdavItemToRemoteItem(x, client.vaultName) + ), }; }; const downloadFromRemoteRaw = async ( - client: WebDAVClient, + client: WrappedWebdavClient, fileOrFolderPath: string ) => { - const buff = (await client.getFileContents( - getWebdavPath(fileOrFolderPath) + await client.init(); + const buff = (await client.client.getFileContents( + getWebdavPath(fileOrFolderPath, client.vaultName) )) as BufferLike; if (buff instanceof ArrayBuffer) { return buff; @@ -167,13 +235,15 @@ const downloadFromRemoteRaw = async ( }; export const downloadFromRemote = async ( - client: WebDAVClient, + client: WrappedWebdavClient, fileOrFolderPath: string, vault: Vault, mtime: number, password: string = "", remoteEncryptedKey: string = "" ) => { + await client.init(); + const isFolder = fileOrFolderPath.endsWith("/"); await mkdirpInVault(fileOrFolderPath, vault); @@ -189,7 +259,7 @@ export const downloadFromRemote = async ( if (password !== "") { downloadFile = remoteEncryptedKey; } - downloadFile = getWebdavPath(downloadFile); + downloadFile = getWebdavPath(downloadFile, client.vaultName); const remoteContent = await downloadFromRemoteRaw(client, downloadFile); let localContent = remoteContent; if (password !== "") { @@ -202,7 +272,7 @@ export const downloadFromRemote = async ( }; export const deleteFromRemote = async ( - client: WebDAVClient, + client: WrappedWebdavClient, fileOrFolderPath: string, password: string = "", remoteEncryptedKey: string = "" @@ -214,9 +284,11 @@ export const deleteFromRemote = async ( if (password !== "") { remoteFileName = remoteEncryptedKey; } - remoteFileName = getWebdavPath(remoteFileName); + remoteFileName = getWebdavPath(remoteFileName, client.vaultName); + + await client.init(); try { - await client.deleteFile(remoteFileName); + await client.client.deleteFile(remoteFileName); // console.log(`delete ${remoteFileName} succeeded`); } catch (err) { console.error("some error while deleting"); @@ -224,7 +296,8 @@ export const deleteFromRemote = async ( } }; -export const checkConnectivity = async (client: WebDAVClient) => { +export const checkConnectivity = async (client: WrappedWebdavClient) => { + await client.init(); try { const results = await getRemoteMeta(client, "/"); if (results === undefined) {