diff --git a/pro/src/baseTypesPro.ts b/pro/src/baseTypesPro.ts index d882422..4146884 100644 --- a/pro/src/baseTypesPro.ts +++ b/pro/src/baseTypesPro.ts @@ -87,8 +87,12 @@ export interface PCloudConfig { locationid: 1 | 2; remoteBaseDir?: string; credentialsShouldBeDeletedAtTimeMs?: number; - emptyFile: "skip" | "error"; kind: "pcloud"; + + /** + * @deprecated + */ + emptyFile: "skip" | "error"; } /////////////////////////////////////////////////////////// diff --git a/pro/src/fsPCloud.ts b/pro/src/fsPCloud.ts index 5a40953..d80c2bf 100644 --- a/pro/src/fsPCloud.ts +++ b/pro/src/fsPCloud.ts @@ -285,6 +285,10 @@ const fromNestedFolderToEntityListAndCache = ( }; }; +const getPCloudPath = (fileOrFolderPath: string, remoteBaseDir: string) => { + return `/${remoteBaseDir}/${fileOrFolderPath}`; +}; + export class FakeFsPCloud extends FakeFs { kind: string; @@ -464,25 +468,6 @@ export class FakeFsPCloud extends FakeFs { await this._init(); - if (content.byteLength === 0) { - if (this.pCloudConfig.emptyFile === "error") { - throw Error( - `${key}: Empty file is not allowed in pCloud, and please write something in it.` - ); - } else { - return { - key: key, - keyRaw: key, - mtimeSvr: mtime, - mtimeCli: mtime, - size: 0, - sizeRaw: 0, - synthesizedFile: true, - // hash: ?? // TODO - }; - } - } - const prevCachedEntity: PCloudEntity | undefined = this.keyToPCloudEntity[key]; const prevFileID: number | undefined = prevCachedEntity?.id; @@ -522,15 +507,51 @@ export class FakeFsPCloud extends FakeFs { }); const apiUrl = `https://${this.pCloudConfig.hostname}/uploadfile?${params}`; - const rsp = await fetch(apiUrl, { - method: "PUT", - body: content, - }); - const f: StatRawResponse = await rsp.json(); - const entity = fromRawResponseToEntity(f.metadata[0], parentFolderPath); - // console.debug(entity); - this.keyToPCloudEntity[key] = entity; - return entity; + if (content.byteLength > 0) { + const rsp = await fetch(apiUrl, { + method: "PUT", + body: content, + }); + const f: StatRawResponse = await rsp.json(); + const entity = fromRawResponseToEntity(f.metadata[0], parentFolderPath); + // console.debug(entity); + this.keyToPCloudEntity[key] = entity; + return entity; + } else { + // no idea why pcloud doesn't work for empty files + // it can be uploaded successfully but the call doesn't end + // we abort it and stat it manually. + // console.warn(`uploading empty file ${key}`); + const controller = new AbortController(); + const timeoutMs = 300; // just a random reasonable number + const id = setTimeout(() => controller.abort(), timeoutMs); + try { + const rsp = await fetch(apiUrl, { + method: "PUT", + body: content, + signal: controller.signal, + }); + } catch (e) { + // console.warn(`we abort the request of uploading empty file ${key}:`); + // console.warn(e); + } finally { + clearTimeout(id); + } + + // raw stat here + // https://docs.pcloud.com/methods/file/stat.html + const params = new URLSearchParams({ + access_token: await this._getAccessToken(), + path: getPCloudPath(key, this.remoteBaseDir), + }); + const apiUrlStat = `https://${this.pCloudConfig.hostname}/stat?${params}`; + const rsp2 = await fetch(apiUrlStat); + const f = await rsp2.json(); + const entity = fromRawResponseToEntity(f.metadata, parentFolderPath); + // console.warn(entity); + this.keyToPCloudEntity[key] = entity; + return entity; + } } async readFile(key: string): Promise { @@ -602,6 +623,6 @@ export class FakeFsPCloud extends FakeFs { } allowEmptyFile(): boolean { - return false; + return true; } } diff --git a/pro/src/settingsPCloud.ts b/pro/src/settingsPCloud.ts index 56a9e5c..2328fde 100644 --- a/pro/src/settingsPCloud.ts +++ b/pro/src/settingsPCloud.ts @@ -280,20 +280,6 @@ export const generatePCloudSettingsPart = ( }); }); - new Setting(pCloudAllowedToUsedDiv) - .setName(t("settings_pcloud_emptyfile")) - .setDesc(t("settings_pcloud_emptyfile_desc")) - .addDropdown(async (dropdown) => { - dropdown - .addOption("skip", t("settings_pcloud_emptyfile_skip")) - .addOption("error", t("settings_pcloud_emptyfile_error")) - .setValue(plugin.settings.pcloud.emptyFile) - .onChange(async (val) => { - plugin.settings.pcloud.emptyFile = val as any; - await saveUpdatedConfigFunc(); - }); - }); - new Setting(pCloudAllowedToUsedDiv) .setName(t("settings_checkonnectivity")) .setDesc(t("settings_checkonnectivity_desc"))