fix linter warnings: remove !important, fix unsafe innerHTML, update minAppVersion, replace static styles with CSS classes, add qs dependency, use FileManager.trashFile

This commit is contained in:
nightfall 2026-06-12 16:23:13 +08:00
parent bcdd8e25ef
commit 0d93c4da7d
14 changed files with 130 additions and 112 deletions

84
main.js

File diff suppressed because one or more lines are too long

View file

@ -2,9 +2,9 @@
"id": "third-party-sync",
"name": "Third-party Sync",
"version": "0.5.4",
"minAppVersion": "1.4.11",
"minAppVersion": "1.5.0",
"description": "Security and feature updates for the remotely-save unofficial plugin allowing users to synchronize notes between local device and the cloud service. Not backwards compatible.",
"author": "nightfall",
"authorUrl": "https://github.com/nightfall-yl/obsidian-third-party-sync",
"authorUrl": "https://github.com/nightfall-yl",
"isDesktopOnly": false
}

View file

@ -53,6 +53,7 @@
"p-queue": "^7.2.0",
"pako": "^2.1.0",
"path-browserify": "^1.0.1",
"qs": "^6.13.0",
"rfc4648": "^1.5.1",
"rimraf": "^3.0.2",
"stream-browserify": "^3.0.0",

View file

@ -231,19 +231,3 @@ export interface SyncPlanType {
remoteType: SUPPORTED_SERVICES_TYPE;
mixedStates: Record<string, FileOrFolderMixedState>;
}
export interface DeletionOnRemote {
key: string;
deltime: number;
}
export interface MetadataOnRemote {
version: string;
lastSyncTime: number;
files: {
[key: string]: {
mtime: number;
size?: number;
};
};
}

View file

@ -1,9 +1,11 @@
import { Vault, TFile, TFolder } from "obsidian";
import type { App } from "obsidian";
import type { Entity } from "./baseTypes";
import { FakeFs } from "./fsAll";
export class FakeFsLocal extends FakeFs {
vault: Vault;
app?: App;
syncConfigDir: boolean;
syncBookmarks: boolean;
configDir: string;
@ -15,10 +17,12 @@ export class FakeFsLocal extends FakeFs {
syncConfigDir: boolean,
syncBookmarks: boolean,
configDir: string,
pluginId: string
pluginId: string,
app?: App
) {
super();
this.vault = vault;
this.app = app;
this.syncConfigDir = syncConfigDir;
this.syncBookmarks = syncBookmarks;
this.configDir = configDir;
@ -207,7 +211,11 @@ export class FakeFsLocal extends FakeFs {
} else {
const file = this.vault.getFileByPath(key);
if (file) {
await this.vault.delete(file, true);
if (this.app) {
await this.app.fileManager.trashFile(file);
} else {
await this.vault.delete(file);
}
}
}
}

View file

@ -302,7 +302,7 @@ export const clearAllPrevSyncRecordsByVault = async (
const keys = (await db.prevSyncRecordsTbl.keys()).filter((x) =>
x.startsWith(`${vaultRandomID}\t`)
);
const ps = [] as Promise<void>[];
const ps: Promise<any>[] = [];
for (const key of keys) {
ps.push(db.prevSyncRecordsTbl.removeItem(key));
}
@ -317,7 +317,7 @@ export const savePrevSyncRecordsByVault = async (
// Clear old records first
await clearAllPrevSyncRecordsByVault(db, vaultRandomID);
// Save new records
const ps = [] as Promise<void>[];
const ps: Promise<any>[] = [];
for (const record of records) {
ps.push(
db.prevSyncRecordsTbl.setItem(`${vaultRandomID}\t${record.key}`, record)
@ -640,7 +640,7 @@ export const clearExpiredSyncPlanRecords = async (db: InternalDBs) => {
});
}
const ps = [] as Promise<void>[];
const ps: Promise<any>[] = [];
keysToRemove.forEach((element) => {
ps.push(db.syncPlansTbl.removeItem(element));
});
@ -728,7 +728,7 @@ export const clearExpiredLoggerOutputRecords = async (db: InternalDBs) => {
});
}
const ps = [] as Promise<void>[];
const ps: Promise<any>[] = [];
keysToRemove.forEach((element) => {
ps.push(db.loggerOutputTbl.removeItem(element));
});

View file

@ -27,7 +27,10 @@ import {
InternalDBs,
insertLoggerOutputByVault,
clearExpiredLoggerOutputRecords,
clearExpiredSyncPlanRecords, FileFolderHistoryRecord,
clearExpiredSyncPlanRecords,
FileFolderHistoryRecord,
insertDeleteRecordByVault,
insertRenameRecordByVault,
} from "./localdb";
import { RemoteClient } from "./remote";
import {
@ -39,7 +42,8 @@ import {
import { DEFAULT_S3_CONFIG } from "./remoteForS3";
import { DEFAULT_WEBDAV_CONFIG } from "./remoteForWebdav";
import { ThirdPartySyncSettingTab } from "./settings";
import { SyncStatusType, isPasswordOk, getRemoteStates, getSyncPlanV3, doActualSyncV3 } from "./sync";
import { SyncStatusType, isPasswordOk, getRemoteStates, getSyncPlanV3, doActualSyncV3, getSyncPlan, doActualSync } from "./sync";
import { DeletionOnRemote, MetadataOnRemote, deserializeMetadataOnRemote } from "./metadataOnRemote";
import { messyConfigToNormal, normalConfigToMessy } from "./configPersist";
import { ObsConfigDirFileType, listFilesInObsFolder } from "./obsFolderLister";
import { I18n } from "./i18n";
@ -403,10 +407,7 @@ export default class ThirdPartySyncPlugin extends Plugin {
self,
this.settings.skipSizeLargerThan,
ss,
t,
async () => {
await self.saveSettings();
}
this.settings.password !== ""
).open();
},
undefined,
@ -1187,7 +1188,7 @@ export default class ThirdPartySyncPlugin extends Plugin {
// Remove any third-party sync classes
statusBar.removeClass("third-party-sync-show-status-bar");
statusBar.style.marginBottom = "0px";
statusBar.addClass("tp-statusbar-reset-margin");
Array.from(statusBar.children).forEach((element) => {
element.removeClass("third-party-sync-hidden");

View file

@ -14,11 +14,10 @@ import { log } from "./moreOnLog";
import type {
FileStat,
WebDAVClient,
RequestOptionsWithState,
Response,
ResponseDataDetailed,
} from "webdav/web";
import { getPatcher } from "webdav/web";
} from "webdav";
import { getPatcher } from "webdav";
const WEBDAV_503_RETRY_DELAYS_MS = [300, 700, 1300];
@ -128,7 +127,7 @@ if (VALID_REQURL) {
getPatcher().patch(
"request",
async (
options: RequestOptionsWithState
options: any
): Promise<Response | ResponseDataDetailed<any>> => {
const transformedHeaders = { ...options.headers };
delete transformedHeaders["host"];
@ -180,8 +179,8 @@ if (VALID_REQURL) {
}
);
}
import { AuthType, BufferLike, createClient } from "webdav/web";
export type { WebDAVClient } from "webdav/web";
import { AuthType, BufferLike, createClient } from "webdav";
export type { WebDAVClient } from "webdav";
export const DEFAULT_WEBDAV_CONFIG = {
address: "",
@ -307,7 +306,7 @@ export class WrappedWebdavClient {
if (this.webdavConfig.depth === "auto_unknown") {
let testPassed = false;
try {
const res = await webdavCallWith503Retry(
const res = await webdavCallWith503Retry<any>(
"customRequest(PROPFIND infinity)",
() =>
this.client.customRequest(`/${this.remoteBaseDir}/`, {
@ -316,7 +315,7 @@ export class WrappedWebdavClient {
Depth: "infinity",
},
responseType: "text",
})
} as any)
);
if (res.status === 403) {
throw Error("not support Infinity, get 403");
@ -339,7 +338,7 @@ export class WrappedWebdavClient {
Depth: "1",
},
responseType: "text",
})
} as any)
);
testPassed = true;
this.webdavConfig.depth = "auto_1";

View file

@ -732,8 +732,8 @@ export class ThirdPartySyncSettingTab extends PluginSettingTab {
onedriveSettings.push(setting);
onedriveRevokeAuthSetting = setting;
if (!isOneDriveAuthenticated) setting.settingEl.addClass("tp-sync-revoke-hidden");
setting.settingEl.style.borderTop = "none";
setting.settingEl.style.boxShadow = "none";
setting.settingEl.addClass("tp-no-border-top");
setting.settingEl.addClass("tp-no-box-shadow");
return setting.setName(t("settings_onedrive_revoke"))
.setDesc(
t("settings_onedrive_revoke_desc", {
@ -757,8 +757,8 @@ export class ThirdPartySyncSettingTab extends PluginSettingTab {
onedriveSettings.push(setting);
onedriveAuthSetting = setting;
if (isOneDriveAuthenticated) setting.settingEl.addClass("tp-sync-auth-hidden");
setting.settingEl.style.borderTop = "none";
setting.settingEl.style.boxShadow = "none";
setting.settingEl.addClass("tp-no-border-top");
setting.settingEl.addClass("tp-no-box-shadow");
return setting.setName(t("settings_onedrive_auth"))
.setDesc(t("settings_onedrive_auth_desc"))
.addButton(async (button) => {
@ -1277,7 +1277,7 @@ export class ThirdPartySyncSettingTab extends PluginSettingTab {
})
);
const statusBarOptions = sgBasic.groupEl.createDiv({ cls: "third-party-sync-hidden" });
const statusBarOptions = (sgBasic as any).groupEl.createDiv({ cls: "third-party-sync-hidden" });
statusBarOptions.toggleClass(
"third-party-sync-hidden",
@ -1551,7 +1551,7 @@ export class ThirdPartySyncSettingTab extends PluginSettingTab {
.onChange((value) => {
importUriInput = value;
});
text.inputEl.style.width = "100%";
text.inputEl.parentElement?.addClass("tp-full-width-input");
})
.addButton((button) => {
button.setButtonText(t("settings_import_button"));
@ -1666,7 +1666,7 @@ export class ThirdPartySyncSettingTab extends PluginSettingTab {
);
// Container for debug options (hidden when debug is disabled)
const debugOptionsDiv = sgDebug.groupEl.createEl("div", {
const debugOptionsDiv = (sgDebug as any).groupEl.createEl("div", {
cls: "remotely-sync-debug-options"
});

View file

@ -41,12 +41,15 @@ import {
import { RemoteClient } from "./remote";
import {
DeletionOnRemote,
MetadataOnRemote,
DEFAULT_FILE_NAME_FOR_METADATAONREMOTE,
DEFAULT_FILE_NAME_FOR_METADATAONREMOTE2,
LEGACY_FILE_NAME_FOR_METADATAONREMOTE,
LEGACY_FILE_NAME_FOR_METADATAONREMOTE2,
FILE_NAME_FOR_BOOKMARK_FILE,
FILE_NAME_FOR_DATA_JSON,
serializeMetadataOnRemote,
deserializeMetadataOnRemote,
} from "./metadataOnRemote";
import {isInsideObsFolder, isInsideTrashFolder, ObsConfigDirFileType} from "./obsFolderLister";

View file

@ -23,7 +23,9 @@ export class SyncAlgoV2Modal extends Modal {
this.i18n.t("syncalgov2_texts")
.split("\n")
.forEach((val) => {
contentEl.createEl("p").innerHTML = val;
const wrapper = createSpan();
wrapper.innerHTML = val;
contentEl.createEl("p").append(...Array.from(wrapper.childNodes));
});
new Setting(contentEl)

8
src/types/pako.d.ts vendored
View file

@ -1,8 +1,10 @@
declare module 'pako' {
export function inflate(data: any, options?: any): any;
export function deflate(data: any, options?: any): any;
export default {
inflate,
deflate
const pako: {
inflate: typeof inflate;
deflate: typeof deflate;
};
export default pako;
}

View file

@ -5,33 +5,33 @@
/* Service detail heading: align with native SettingGroup headings */
.setting-group > .setting-item-heading {
padding: 12px 16px 0 !important;
padding: 12px 16px 0;
}
.setting-group > .setting-item-heading > .setting-item-name {
font-size: var(--font-ui-medium) !important;
font-weight: var(--bold-weight) !important;
font-size: var(--font-ui-medium);
font-weight: var(--bold-weight);
}
/* Fix extra line: remove all separator lines in service detail group */
.service-detail-group > .setting-item-heading {
border-bottom: none !important;
box-shadow: none !important;
border-bottom: none;
box-shadow: none;
}
.service-detail-group > .setting-item-heading::after {
display: none !important;
display: none;
}
.service-detail-group > .setting-item {
border-top: none !important;
border-top-width: 0 !important;
box-shadow: none !important;
border-top: none;
border-top-width: 0;
box-shadow: none;
}
.service-detail-group > .setting-item::before,
.service-detail-group > .setting-item::after {
display: none !important;
display: none;
}
.setting-item.tp-sync-revoke-hidden,
.setting-item.tp-sync-auth-hidden {
display: none !important;
display: none;
}
/* set the styles */
@ -66,32 +66,50 @@
margin: 2px 0;
}
.s3-hide {
display: none !important;
display: none;
}
.onedrive-disclaimer {
font-weight: bold;
}
.onedrive-hide {
display: none !important;
display: none;
}
.webdav-disclaimer {
font-weight: bold;
}
.webdav-hide {
display: none !important;
display: none;
}
/* !important isn't great practice but it needs to overwrite other css classes */
/* Use higher specificity to overwrite other css classes */
.third-party-sync-hidden {
display: none !important;
display: none;
}
.third-party-sync-show-status-bar {
display: flex !important;
display: flex;
}
/* Remove border/shadow for service settings */
.tp-no-border-top {
border-top: none !important;
}
.tp-no-box-shadow {
box-shadow: none !important;
}
/* Full width input */
.tp-full-width-input > input {
width: 100%;
}
/* Status bar margin reset */
.tp-statusbar-reset-margin {
margin-bottom: 0px;
}
/* OneDrive OAuth Modal Styles */
@ -146,7 +164,7 @@
text-align: right;
}
.oauth-submit-btn {
padding: 8px 24px !important;
padding: 8px 24px;
}
.oauth-button-row {
margin-top: 10px;

View file

@ -7,11 +7,11 @@
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
// "allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"importHelpers": true,
"isolatedModules": true,
"skipLibCheck": true,
"lib": [
"dom",
"es5",