From 383d319172772aca207c543aed317fab5c75ab07 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Mon, 10 Nov 2025 22:04:39 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=B1=87=E5=87=BA?= =?UTF-8?q?=E6=B1=87=E5=85=A5=E4=B8=8D=E4=BE=9D=E7=85=A7=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E6=9C=80=E5=90=8E=E4=BF=AE=E6=94=B9=E6=97=A5=E6=9C=9F=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cloudscript/local.ts | 6 +- packages/filesystem/baidu/baidu.ts | 6 +- packages/filesystem/dropbox/dropbox.ts | 6 +- packages/filesystem/filesystem.ts | 8 +- .../filesystem/googledrive/googledrive.ts | 6 +- packages/filesystem/onedrive/onedrive.ts | 6 +- packages/filesystem/webdav/webdav.ts | 6 +- packages/filesystem/zip/rw.ts | 21 +++-- packages/filesystem/zip/zip.ts | 24 +++--- scripts/pack.js | 6 +- src/app/service/service_worker/client.ts | 11 ++- src/app/service/service_worker/script.ts | 26 +++++-- src/app/service/service_worker/synchronize.ts | 30 ++++---- .../components/CloudScriptPlan/index.tsx | 4 +- src/pages/import/App.tsx | 9 ++- src/pages/install/App.tsx | 6 +- .../options/routes/script/ScriptEditor.tsx | 2 +- src/pkg/backup/backup.test.ts | 19 +++-- src/pkg/backup/export.ts | 21 ++--- src/pkg/backup/import.ts | 14 ++-- src/pkg/backup/struct.ts | 2 + src/pkg/backup/utils.ts | 4 +- src/pkg/utils/jszip-x.ts | 77 +++++++++++++++++++ 23 files changed, 227 insertions(+), 93 deletions(-) create mode 100644 src/pkg/utils/jszip-x.ts diff --git a/packages/cloudscript/local.ts b/packages/cloudscript/local.ts index b21d015ed..dc0e3655c 100644 --- a/packages/cloudscript/local.ts +++ b/packages/cloudscript/local.ts @@ -1,7 +1,7 @@ import { ExtVersion } from "@App/app/const"; import type { Script } from "@App/app/repo/scripts"; import type { Value } from "@App/app/repo/value"; -import type JSZip from "jszip"; +import { type JSZipFile } from "@App/pkg/utils/jszip-x"; import packageTpl from "@App/template/cloudcat-package/package.tpl"; import utilsTpl from "@App/template/cloudcat-package/utils.tpl"; import indexTpl from "@App/template/cloudcat-package/index.tpl"; @@ -10,12 +10,12 @@ import type CloudScript from "./cloudscript"; // 导出到本地,一个可执行到npm包 export default class LocalCloudScript implements CloudScript { - zip: JSZip; + zip: JSZipFile; params: ExportParams; constructor(params: ExportParams) { - this.zip = params.zip! as JSZip; + this.zip = params.zip! as JSZipFile; this.params = params; } diff --git a/packages/filesystem/baidu/baidu.ts b/packages/filesystem/baidu/baidu.ts index 26614fafb..88f013150 100644 --- a/packages/filesystem/baidu/baidu.ts +++ b/packages/filesystem/baidu/baidu.ts @@ -1,6 +1,6 @@ import { AuthVerify } from "../auth"; import type FileSystem from "../filesystem"; -import type { File, FileReader, FileWriter } from "../filesystem"; +import type { File, FileCreateOptions, FileReader, FileWriter } from "../filesystem"; import { joinPath } from "../utils"; import { BaiduFileReader, BaiduFileWriter } from "./rw"; @@ -29,11 +29,11 @@ export default class BaiduFileSystem implements FileSystem { return new BaiduFileSystem(joinPath(this.path, path), this.accessToken); } - async create(path: string): Promise { + async create(path: string, _opts?: FileCreateOptions): Promise { return new BaiduFileWriter(this, joinPath(this.path, path)); } - async createDir(dir: string): Promise { + async createDir(dir: string, _opts?: FileCreateOptions): Promise { dir = joinPath(this.path, dir); const urlencoded = new URLSearchParams(); urlencoded.append("path", dir); diff --git a/packages/filesystem/dropbox/dropbox.ts b/packages/filesystem/dropbox/dropbox.ts index 0bae19c96..79b97a57a 100644 --- a/packages/filesystem/dropbox/dropbox.ts +++ b/packages/filesystem/dropbox/dropbox.ts @@ -1,6 +1,6 @@ import { AuthVerify } from "../auth"; import type FileSystem from "../filesystem"; -import type { File, FileReader, FileWriter } from "../filesystem"; +import type { File, FileCreateOptions, FileReader, FileWriter } from "../filesystem"; import { joinPath } from "../utils"; import { DropboxFileReader, DropboxFileWriter } from "./rw"; @@ -32,11 +32,11 @@ export default class DropboxFileSystem implements FileSystem { return Promise.resolve(new DropboxFileSystem(joinPath(this.path, path), this.accessToken)); } - create(path: string): Promise { + create(path: string, _opts?: FileCreateOptions): Promise { return Promise.resolve(new DropboxFileWriter(this, joinPath(this.path, path))); } - async createDir(dir: string): Promise { + async createDir(dir: string, _opts?: FileCreateOptions): Promise { if (!dir) { return Promise.resolve(); } diff --git a/packages/filesystem/filesystem.ts b/packages/filesystem/filesystem.ts index 0ee1847ac..d5b74f77a 100644 --- a/packages/filesystem/filesystem.ts +++ b/packages/filesystem/filesystem.ts @@ -27,6 +27,10 @@ export interface FileWriter { export type FileReadWriter = FileReader & FileWriter; +export type FileCreateOptions = { + modifiedDate?: number; +}; + // 文件读取 export default interface FileSystem { // 授权验证 @@ -36,9 +40,9 @@ export default interface FileSystem { // 打开目录 openDir(path: string): Promise; // 创建文件 - create(path: string): Promise; + create(path: string, opts?: FileCreateOptions): Promise; // 创建目录 - createDir(dir: string): Promise; + createDir(dir: string, opts?: FileCreateOptions): Promise; // 删除文件 delete(path: string): Promise; // 文件列表 diff --git a/packages/filesystem/googledrive/googledrive.ts b/packages/filesystem/googledrive/googledrive.ts index f0b41ec4c..d9072fb66 100644 --- a/packages/filesystem/googledrive/googledrive.ts +++ b/packages/filesystem/googledrive/googledrive.ts @@ -1,6 +1,6 @@ import { AuthVerify } from "../auth"; import type FileSystem from "../filesystem"; -import type { File, FileReader, FileWriter } from "../filesystem"; +import type { File, FileCreateOptions, FileReader, FileWriter } from "../filesystem"; import { joinPath } from "../utils"; import { GoogleDriveFileReader, GoogleDriveFileWriter } from "./rw"; @@ -31,10 +31,10 @@ export default class GoogleDriveFileSystem implements FileSystem { return Promise.resolve(new GoogleDriveFileSystem(joinPath(this.path, path), this.accessToken)); } - create(path: string): Promise { + create(path: string, _opts?: FileCreateOptions): Promise { return Promise.resolve(new GoogleDriveFileWriter(this, joinPath(this.path, path))); } - async createDir(dir: string): Promise { + async createDir(dir: string, _opts?: FileCreateOptions): Promise { if (!dir) { return Promise.resolve(); } diff --git a/packages/filesystem/onedrive/onedrive.ts b/packages/filesystem/onedrive/onedrive.ts index 78d8bae1b..43005476d 100644 --- a/packages/filesystem/onedrive/onedrive.ts +++ b/packages/filesystem/onedrive/onedrive.ts @@ -1,5 +1,5 @@ import { AuthVerify } from "../auth"; -import type { File, FileReader, FileWriter } from "../filesystem"; +import type { File, FileCreateOptions, FileReader, FileWriter } from "../filesystem"; import type FileSystem from "../filesystem"; import { joinPath } from "../utils"; import { OneDriveFileReader, OneDriveFileWriter } from "./rw"; @@ -31,11 +31,11 @@ export default class OneDriveFileSystem implements FileSystem { return new OneDriveFileSystem(joinPath(this.path, path), this.accessToken); } - async create(path: string): Promise { + async create(path: string, _opts?: FileCreateOptions): Promise { return new OneDriveFileWriter(this, joinPath(this.path, path)); } - async createDir(dir: string): Promise { + async createDir(dir: string, _opts?: FileCreateOptions): Promise { if (dir && dir.startsWith("ScriptCat")) { dir = dir.substring(9); if (dir.startsWith("/")) { diff --git a/packages/filesystem/webdav/webdav.ts b/packages/filesystem/webdav/webdav.ts index f446533ec..11454fc38 100644 --- a/packages/filesystem/webdav/webdav.ts +++ b/packages/filesystem/webdav/webdav.ts @@ -1,7 +1,7 @@ import type { AuthType, FileStat, WebDAVClient } from "webdav"; import { createClient } from "webdav"; import type FileSystem from "../filesystem"; -import type { File, FileReader, FileWriter } from "../filesystem"; +import type { File, FileCreateOptions, FileReader, FileWriter } from "../filesystem"; import { joinPath } from "../utils"; import { WebDAVFileReader, WebDAVFileWriter } from "./rw"; import { WarpTokenError } from "../error"; @@ -47,11 +47,11 @@ export default class WebDAVFileSystem implements FileSystem { return new WebDAVFileSystem(this.client, joinPath(this.basePath, path), this.url); } - async create(path: string): Promise { + async create(path: string, _opts?: FileCreateOptions): Promise { return new WebDAVFileWriter(this.client, joinPath(this.basePath, path)); } - async createDir(path: string): Promise { + async createDir(path: string, _opts?: FileCreateOptions): Promise { try { await this.client.createDirectory(joinPath(this.basePath, path)); } catch (e: any) { diff --git a/packages/filesystem/zip/rw.ts b/packages/filesystem/zip/rw.ts index 1d022e73d..04bd30530 100644 --- a/packages/filesystem/zip/rw.ts +++ b/packages/filesystem/zip/rw.ts @@ -1,6 +1,6 @@ import type { JSZipObject } from "jszip"; -import type JSZip from "jszip"; -import type { FileReader, FileWriter } from "../filesystem"; +import type { JSZipFileOptions, JSZipFile } from "@App/pkg/utils/jszip-x"; +import type { FileCreateOptions, FileReader, FileWriter } from "../filesystem"; export class ZipFileReader implements FileReader { zipObject: JSZipObject; @@ -15,16 +15,27 @@ export class ZipFileReader implements FileReader { } export class ZipFileWriter implements FileWriter { - zip: JSZip; + zip: JSZipFile; path: string; - constructor(zip: JSZip, path: string) { + modifiedDate: number | undefined; + + constructor(zip: JSZipFile, path: string, opts?: FileCreateOptions) { this.zip = zip; this.path = path; + if (opts && opts.modifiedDate) { + this.modifiedDate = opts.modifiedDate; + } } async write(content: string): Promise { - this.zip.file(this.path, content); + const opts = {} as JSZipFileOptions; + if (this.modifiedDate) { + const date = new Date(this.modifiedDate); + const dateWithOffset = new Date(date.getTime() - date.getTimezoneOffset() * 60000); + opts.date = dateWithOffset; + } + this.zip.file(this.path, content, opts); } } diff --git a/packages/filesystem/zip/zip.ts b/packages/filesystem/zip/zip.ts index b182f8666..34356a3af 100644 --- a/packages/filesystem/zip/zip.ts +++ b/packages/filesystem/zip/zip.ts @@ -1,15 +1,15 @@ -import type JSZip from "jszip"; -import type { File, FileReader, FileWriter } from "@Packages/filesystem/filesystem"; +import { type JSZipFile } from "@App/pkg/utils/jszip-x"; +import type { File, FileCreateOptions, FileReader, FileWriter } from "@Packages/filesystem/filesystem"; import type FileSystem from "@Packages/filesystem/filesystem"; import { ZipFileReader, ZipFileWriter } from "./rw"; export default class ZipFileSystem implements FileSystem { - zip: JSZip; + zip: JSZipFile; basePath: string; // zip为空时,创建一个空的zip - constructor(zip: JSZip, basePath?: string) { + constructor(zip: JSZipFile, basePath?: string) { this.zip = zip; this.basePath = basePath || ""; } @@ -31,11 +31,11 @@ export default class ZipFileSystem implements FileSystem { return new ZipFileSystem(this.zip, path); } - async create(path: string): Promise { - return new ZipFileWriter(this.zip, path); + async create(path: string, opts?: FileCreateOptions): Promise { + return new ZipFileWriter(this.zip, path, opts); } - async createDir(): Promise { + async createDir(_path: string, _opts?: FileCreateOptions): Promise { // do nothing } @@ -45,15 +45,17 @@ export default class ZipFileSystem implements FileSystem { async list(): Promise { const files: File[] = []; - for (const [filename, details] of Object.entries(this.zip.files)) { - const time = details.date.getTime(); + for (const [filename, jsZipObject] of Object.entries(this.zip.files)) { + const date = jsZipObject.date; // the last modification date + const dateWithOffset = new Date(date.getTime() + date.getTimezoneOffset() * 60000); + const lastModificationDate = dateWithOffset.getTime(); files.push({ name: filename, path: filename, size: 0, digest: "", - createtime: time, - updatetime: time, + createtime: lastModificationDate, + updatetime: lastModificationDate, }); } return files; diff --git a/scripts/pack.js b/scripts/pack.js index a72bb081c..925120e15 100644 --- a/scripts/pack.js +++ b/scripts/pack.js @@ -1,7 +1,7 @@ /* global process */ import { promises as fs } from "fs"; import { createWriteStream } from "fs"; -import JSZip from "jszip"; +import { createJSZip } from "@App/pkg/utils/jszip-x"; import ChromeExtension from "crx"; import { execSync } from "child_process"; import manifest from "../src/manifest.json" with { type: "json" }; @@ -90,8 +90,8 @@ firefoxManifest.commands = { _execute_action: {}, }; -const chrome = new JSZip(); -const firefox = new JSZip(); +const chrome = createJSZip(); +const firefox = createJSZip(); async function addDir(zip, localDir, toDir, filters) { const sub = async (localDir, toDir) => { diff --git a/src/app/service/service_worker/client.ts b/src/app/service/service_worker/client.ts index fa841ecf9..3a841f6db 100644 --- a/src/app/service/service_worker/client.ts +++ b/src/app/service/service_worker/client.ts @@ -52,8 +52,15 @@ export class ScriptClient extends Client { return this.do<[boolean, ScriptInfo]>("getInstallInfo", uuid); } - install(script: Script, code: string, upsertBy: InstallSource = "user"): Promise<{ update: boolean }> { - return this.doThrow("install", { script, code, upsertBy }); + install(params: { + details: Script; + code: string; + upsertBy?: InstallSource; + createtime?: number; + updatetime?: number; + }): Promise<{ update: boolean }> { + if (!params.upsertBy) params.upsertBy = "user"; + return this.doThrow("install", { ...params }); } // delete(uuid: string) { diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index fd669b14e..ed6d5140f 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -234,7 +234,7 @@ export class ScriptService { const { script } = await prepareScriptByCode(code, url, uuid); script.subscribeUrl = subscribeUrl; await this.installScript({ - script, + details: script, code, upsertBy: source, }); @@ -246,7 +246,7 @@ export class ScriptService { const { code, upsertBy, uuid } = param; const { script } = await prepareScriptByCode(code, "", uuid, true); await this.installScript({ - script, + details: script, code, upsertBy, }); @@ -266,9 +266,15 @@ export class ScriptService { } // 安装脚本 / 更新腳本 - async installScript(param: { script: Script; code: string; upsertBy: InstallSource }) { + async installScript(param: { + details: Script; + code: string; + upsertBy?: InstallSource; + createtime?: number; + updatetime?: number; + }) { param.upsertBy = param.upsertBy || "user"; - const { script, upsertBy } = param; + const { details: script, upsertBy, createtime, updatetime } = param; // 删 storage cache const compiledResourceUpdatePromise = this.compiledResourceDAO.delete(script.uuid); const logger = this.logger.with({ @@ -286,6 +292,14 @@ export class ScriptService { script.selfMetadata = oldScript.selfMetadata; } if (script.ignoreVersion) script.ignoreVersion = ""; + if (createtime) { + script.createtime = createtime; + } + if (updatetime) { + script.updatetime = updatetime; + } + console.log(12388, createtime, updatetime); + console.log(new Error().stack); return this.scriptDAO .save(script) .then(async () => { @@ -720,7 +734,7 @@ export class ScriptService { if (checkSilenceUpdate(oldScript!.metadata, script.metadata)) { logger?.info("silence update script"); await this.installScript({ - script, + details: script, code, upsertBy, }); @@ -903,7 +917,7 @@ export class ScriptService { const { script } = await prepareScriptByCode(code, url, uuid); console.log("slienceUpdate", script.name); await this.installScript({ - script, + details: script, code, upsertBy: "system", }); diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index da3a295a8..7779ab497 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -12,7 +12,7 @@ import { isWarpTokenError } from "@Packages/filesystem/error"; import type { Group } from "@Packages/message/server"; import type { MessageSend } from "@Packages/message/types"; import { type IMessageQueue } from "@Packages/message/message_queue"; -import JSZip from "jszip"; +import { createJSZip } from "@App/pkg/utils/jszip-x"; import { type ValueService } from "./value"; import { type ResourceService } from "./resource"; import { createObjectURL } from "../offscreen/client"; @@ -114,6 +114,10 @@ export class SynchronizeService { if (!code) { throw new Error(`Script ${script.uuid} code not found`); } + const storage: ValueStorage = { + data: {}, + ts: Date.now(), + }; const ret = { code: code.code, options: { @@ -126,20 +130,18 @@ export class SynchronizeService { name: script.name, uuid: script.uuid, sc_uuid: script.uuid, - modified: script.updatetime, - file_url: script.downloadUrl, + modified: script.updatetime!, + file_url: script.downloadUrl!, subscribe_url: script.subscribeUrl, }, }, // storage, - requires: [], - requiresCss: [], - resources: [], - } as unknown as ScriptBackupData; - const storage: ValueStorage = { - data: {}, - ts: Date.now(), - }; + requires: [] as ResourceBackup[], + requiresCss: [] as ResourceBackup[], + resources: [] as ResourceBackup[], + storage, + lastModificationDate: script.updatetime || script.createtime || undefined, + } satisfies ScriptBackupData; const values = await this.value.getScriptValue(script); for (const key of Object.keys(values)) { storage.data[key] = values[key]; @@ -240,7 +242,7 @@ export class SynchronizeService { // 请求导出文件 async requestExport(uuids?: string[]) { - const zip = new JSZip(); + const zip = createJSZip(); const fs = new ZipFileSystem(zip); await this.backup(fs, uuids); // 生成文件,并下载 @@ -264,7 +266,7 @@ export class SynchronizeService { // 备份到云端 async backupToCloud({ type, params }: { type: FileSystemType; params: any }) { // 首先生成zip文件 - const zip = new JSZip(); + const zip = createJSZip(); const fs = new ZipFileSystem(zip); await this.backup(fs); this.logger.info("backup to cloud"); @@ -589,7 +591,7 @@ export class SynchronizeService { ); script.origin = script.origin || metaObj.origin; this.script.installScript({ - script, + details: script, code, upsertBy: "sync", }); diff --git a/src/pages/components/CloudScriptPlan/index.tsx b/src/pages/components/CloudScriptPlan/index.tsx index 698f28877..09d2b73d9 100644 --- a/src/pages/components/CloudScriptPlan/index.tsx +++ b/src/pages/components/CloudScriptPlan/index.tsx @@ -9,7 +9,7 @@ import { IconQuestionCircleFill } from "@arco-design/web-react/icon"; import type { ExportParams } from "@Packages/cloudscript/cloudscript"; import { parseExportCookie, parseExportValue } from "@Packages/cloudscript/cloudscript"; import CloudScriptFactory from "@Packages/cloudscript/factory"; -import JSZip from "jszip"; +import { createJSZip } from "@App/pkg/utils/jszip-x"; import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; @@ -117,7 +117,7 @@ const CloudScriptPlan: React.FC<{ const values = await parseExportValue(script, params.exportValue); const cookies = await parseExportCookie(params.exportCookie); if (cloudScriptType === "local") { - const jszip = new JSZip(); + const jszip = createJSZip(); const cloudScript = CloudScriptFactory.create("local", { zip: jszip, ...params, diff --git a/src/pages/import/App.tsx b/src/pages/import/App.tsx index 7f7f4dc6a..2162a200c 100644 --- a/src/pages/import/App.tsx +++ b/src/pages/import/App.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { Button, Card, Checkbox, Divider, List, Message, Space, Switch, Typography } from "@arco-design/web-react"; import { useTranslation } from "react-i18next"; // 导入react-i18next的useTranslation钩子 -import JSZip from "jszip"; +import { loadAsyncJSZip } from "@App/pkg/utils/jszip-x"; import type { ScriptOptions, ScriptData, SubscribeData } from "@App/pkg/backup/struct"; import { prepareScriptByCode } from "@App/pkg/utils/script"; import { SCRIPT_STATUS_DISABLE, SCRIPT_STATUS_ENABLE, ScriptDAO } from "@App/app/repo/scripts"; @@ -93,7 +93,7 @@ function App() { const resp = await cacheInstance.get<{ filename: string; url: string }>(cacheKey); if (!resp) throw new Error("fetchData failed"); const filedata = await fetch(resp.url).then((resp) => resp.blob()); - const zip = await JSZip.loadAsync(filedata); + const zip = await loadAsyncJSZip(filedata); const backData = await parseBackupZipFile(zip); const backDataScript = backData.script as ScriptData[]; @@ -169,7 +169,10 @@ function App() { if (item.script?.script) { if (item.script.script.ignoreVersion) item.script.script.ignoreVersion = ""; } - await scriptClient.install(item.script!.script!, item.code); + const scriptDetails = item.script!.script!; + const createtime = item.lastModificationDate; + const updatetime = item.lastModificationDate; + await scriptClient.install({ details: scriptDetails, code: item.code, createtime, updatetime }); await Promise.all([ (async () => { // 导入资源 diff --git a/src/pages/install/App.tsx b/src/pages/install/App.tsx index cf12356d2..82b75f0e2 100644 --- a/src/pages/install/App.tsx +++ b/src/pages/install/App.tsx @@ -61,7 +61,7 @@ function App() { const installOrUpdateScript = async (newScript: Script, code: string) => { if (newScript.ignoreVersion) newScript.ignoreVersion = ""; - await scriptClient.install(newScript, code); + await scriptClient.install({ details: newScript, code }); const metadata = newScript.metadata; setScriptInfo((prev) => (prev ? { ...prev, code, metadata } : prev)); setOldScriptVersion(metadata!.version![0]); @@ -312,7 +312,7 @@ function App() { (upsertScript as Script).checkUpdate = false; } // 故意只安装或执行,不改变显示内容 - await scriptClient.install(upsertScript as Script, scriptCode); + await scriptClient.install({ details: upsertScript as Script, code: scriptCode }); if (isUpdate) { Message.success(t("install.update_success")!); setBtnText(t("install.update_success")!); @@ -324,7 +324,7 @@ function App() { } if ((upsertScript as Script).ignoreVersion) (upsertScript as Script).ignoreVersion = ""; // 故意只安装或执行,不改变显示内容 - await scriptClient.install(upsertScript as Script, scriptCode); + await scriptClient.install({ details: upsertScript as Script, code: scriptCode }); if (isUpdate) { Message.success(t("install.update_success")!); setBtnText(t("install.update_success")!); diff --git a/src/pages/options/routes/script/ScriptEditor.tsx b/src/pages/options/routes/script/ScriptEditor.tsx index 4e095c201..f8aa841ec 100644 --- a/src/pages/options/routes/script/ScriptEditor.tsx +++ b/src/pages/options/routes/script/ScriptEditor.tsx @@ -246,7 +246,7 @@ function ScriptEditor() { } if (script.ignoreVersion) script.ignoreVersion = ""; return scriptClient - .install(script, code) + .install({ details: script, code }) .then((update): Script => { if (!update) { Message.success(t("create_success_note")); diff --git a/src/pkg/backup/backup.test.ts b/src/pkg/backup/backup.test.ts index 596e82cd3..42913fa6f 100644 --- a/src/pkg/backup/backup.test.ts +++ b/src/pkg/backup/backup.test.ts @@ -1,4 +1,4 @@ -import JSZip from "jszip"; +import { createJSZip } from "@App/pkg/utils/jszip-x"; import BackupExport from "./export"; import { parseBackupZipFile } from "./utils"; import type { BackupData } from "./struct"; @@ -7,7 +7,7 @@ import ZipFileSystem from "@Packages/filesystem/zip/zip"; describe.concurrent("backup", () => { it.concurrent("empty", async () => { - const zipFile = new JSZip(); + const zipFile = createJSZip(); const fs = new ZipFileSystem(zipFile); await new BackupExport(fs).export({ script: [], @@ -21,7 +21,7 @@ describe.concurrent("backup", () => { }); it.concurrent("export and import script - basic", async () => { - const zipFile = new JSZip(); + const zipFile = createJSZip(); const fs = new ZipFileSystem(zipFile); const data: BackupData = { script: [ @@ -77,6 +77,7 @@ describe.concurrent("backup", () => { bool: false, }, }, + lastModificationDate: expect.any(Number), }, ], subscribe: [ @@ -97,6 +98,7 @@ describe.concurrent("backup", () => { url: "", }, }, + lastModificationDate: expect.any(Number), }, ], } as unknown as BackupData; @@ -112,7 +114,7 @@ describe.concurrent("backup", () => { }); it.concurrent("export and import script - name and version only", async () => { - const zipFile = new JSZip(); + const zipFile = createJSZip(); const fs = new ZipFileSystem(zipFile); const data: BackupData = { script: [ @@ -164,6 +166,7 @@ describe.concurrent("backup", () => { bool: false, }, }, + lastModificationDate: expect.any(Number), }, ], subscribe: [], @@ -180,7 +183,7 @@ describe.concurrent("backup", () => { }); it.concurrent("export and import script - 2 scripts", async () => { - const zipFile = new JSZip(); + const zipFile = createJSZip(); const fs = new ZipFileSystem(zipFile); const data: BackupData = { script: [ @@ -232,6 +235,7 @@ describe.concurrent("backup", () => { bool: false, }, }, + lastModificationDate: expect.any(Number), }, { code: `// ==UserScript== @@ -277,6 +281,7 @@ describe.concurrent("backup", () => { ts: 1, data: {}, }, + lastModificationDate: expect.any(Number), }, ], subscribe: [], @@ -293,7 +298,7 @@ describe.concurrent("backup", () => { }); it.concurrent("export and import script - 30 scripts + 20 subscribes", async () => { - const zipFile = new JSZip(); + const zipFile = createJSZip(); const fs = new ZipFileSystem(zipFile); const data: BackupData = { script: Array.from({ length: 30 }, (v, i) => { @@ -341,6 +346,7 @@ describe.concurrent("backup", () => { ts: 1, data: {}, }, + lastModificationDate: expect.any(Number), }; }), subscribe: Array.from({ length: 20 }, (v, i) => { @@ -361,6 +367,7 @@ describe.concurrent("backup", () => { url: "", }, }, + lastModificationDate: expect.any(Number), }; }), } as unknown as BackupData; diff --git a/src/pkg/backup/export.ts b/src/pkg/backup/export.ts index 57a30b439..6797648db 100644 --- a/src/pkg/backup/export.ts +++ b/src/pkg/backup/export.ts @@ -3,6 +3,7 @@ import { base64ToBlob } from "../utils/utils"; import { toStorageValueStr } from "../utils/utils"; import type { BackupData, ResourceBackup, ScriptBackupData, SubscribeBackupData } from "./struct"; import { md5OfText } from "../utils/crypto"; +import type { FileCreateOptions } from "@Packages/filesystem/filesystem"; export default class BackupExport { fs: FileSystem; @@ -42,24 +43,26 @@ export default class BackupExport { } const wrtieStorage = JSON.stringify(storage); + const fileOpts = { modifiedDate: script.lastModificationDate } as FileCreateOptions; return [ // 写脚本文件 - this.fs.create(`${filename}.user.js`).then((fileWriter) => fileWriter.write(writeCode)), + this.fs.create(`${filename}.user.js`, fileOpts).then((fileWriter) => fileWriter.write(writeCode)), // 写入脚本options.json - this.fs.create(`${filename}.options.json`).then((fileWriter) => fileWriter.write(writeOptions)), + this.fs.create(`${filename}.options.json`, fileOpts).then((fileWriter) => fileWriter.write(writeOptions)), // 写入脚本storage.json - this.fs.create(`${filename}.storage.json`).then((fileWriter) => fileWriter.write(wrtieStorage)), + this.fs.create(`${filename}.storage.json`, fileOpts).then((fileWriter) => fileWriter.write(wrtieStorage)), // 写入脚本资源文件 - ...this.writeResource(filename, script.resources, "resources"), - ...this.writeResource(filename, script.requires, "requires"), - ...this.writeResource(filename, script.requiresCss, "requires.css"), + ...this.writeResource(filename, script.resources, "resources", fileOpts), + ...this.writeResource(filename, script.requires, "requires", fileOpts), + ...this.writeResource(filename, script.requiresCss, "requires.css", fileOpts), ]; } writeResource( filename: string, resources: ResourceBackup[], - type: "resources" | "requires" | "requires.css" + type: "resources" | "requires" | "requires.css", + fileOpts: FileCreateOptions ): Promise[] { return resources.flatMap((item) => { // md5是tm的导出规则 @@ -68,10 +71,10 @@ export default class BackupExport { const writeMeta = JSON.stringify(item.meta); return [ this.fs - .create(`${filename}.user.js-${md5}-${item.meta.name}`) + .create(`${filename}.user.js-${md5}-${item.meta.name}`, fileOpts) .then((fileWriter) => fileWriter.write(writeSource)), this.fs - .create(`${filename}.user.js-${md5}-${item.meta.name}.${type}.json`) + .create(`${filename}.user.js-${md5}-${item.meta.name}.${type}.json`, fileOpts) .then((fileWriter) => fileWriter.write(writeMeta)), ]; }); diff --git a/src/pkg/backup/import.ts b/src/pkg/backup/import.ts index 347bba60f..690698848 100644 --- a/src/pkg/backup/import.ts +++ b/src/pkg/backup/import.ts @@ -49,8 +49,8 @@ export default class BackupImport { // 解析出备份数据 async parse(): Promise { - const map = new Map(); - const subscribe = new Map(); + const map = new Map & ScriptBackupData>(); + const subscribe = new Map & SubscribeBackupData>(); let files = await this.fs.list(); // 处理订阅 @@ -62,7 +62,8 @@ export default class BackupImport { const key = name.substring(0, name.length - 12); const subData = { source: await this.getFileContent(file, false), - } as SubscribeBackupData; + lastModificationDate: file.updatetime, + } satisfies Partial & SubscribeBackupData; subscribe.set(key, subData); return true; }); @@ -92,7 +93,8 @@ export default class BackupImport { requires: [], requiresCss: [], resources: [], - } as ScriptBackupData; + lastModificationDate: file.updatetime, + } satisfies Partial & ScriptBackupData; map.set(key, backupData); return true; }); @@ -229,8 +231,8 @@ export default class BackupImport { // 将map转化为数组 return { - script: Array.from(map.values()), - subscribe: Array.from(subscribe.values()), + script: [...map.values()] as ScriptData[], + subscribe: [...subscribe.values()] as SubscribeData[], }; } diff --git a/src/pkg/backup/struct.ts b/src/pkg/backup/struct.ts index 08488d5cf..e05b8f50f 100644 --- a/src/pkg/backup/struct.ts +++ b/src/pkg/backup/struct.ts @@ -74,6 +74,7 @@ export type ScriptBackupData = { resources: ResourceBackup[]; // 为了兼容暴力猴而设置的字段 enabled?: boolean; + lastModificationDate?: number; }; export type ScriptData = ScriptBackupData & { @@ -107,6 +108,7 @@ export type SubscribeOptionsFile = { export type SubscribeBackupData = { source: string; options?: SubscribeOptionsFile; + lastModificationDate?: number; }; export type BackupData = { diff --git a/src/pkg/backup/utils.ts b/src/pkg/backup/utils.ts index dc6ef0696..65db5290d 100644 --- a/src/pkg/backup/utils.ts +++ b/src/pkg/backup/utils.ts @@ -1,9 +1,9 @@ import ZipFileSystem from "@Packages/filesystem/zip/zip"; -import type JSZip from "jszip"; +import { type JSZipFile } from "@App/pkg/utils/jszip-x"; import BackupImport from "./import"; // 解析备份文件 -export function parseBackupZipFile(zip: JSZip) { +export function parseBackupZipFile(zip: JSZipFile) { const fs = new ZipFileSystem(zip); // 解析文件 return new BackupImport(fs).parse(); diff --git a/src/pkg/utils/jszip-x.ts b/src/pkg/utils/jszip-x.ts new file mode 100644 index 000000000..bea81a717 --- /dev/null +++ b/src/pkg/utils/jszip-x.ts @@ -0,0 +1,77 @@ +/** + * + * JSZIP 由于不再更新,问题只能手改。 + * + * UTC时间问题 + * https://github.com/Stuk/jszip/issues/369#issuecomment-546204220 + * https://blog.csdn.net/weixin_45410246/article/details/150015478 + * + * Typescript: Fix missing types for JSZip.defaults + * https://github.com/Stuk/jszip/pull/927 + * https://github.com/Stuk/jszip/issues/690 + * + * + * 日后应考虑 fork 一下加入以下PR + * + * 修正单一档案不能大于 2GB + * https://github.com/Stuk/jszip/pull/791 + * + * + * 其他参考: + * https://greasyfork.org/scripts/526002-gitzip-lite/code + * + */ +import JSZip from "jszip"; + +type Compression = "STORE" | "DEFLATE"; + +interface CompressionOptions { + level: number; +} + +interface InputByType { + base64: string; + string: string; + text: string; + binarystring: string; + array: number[]; + uint8array: Uint8Array; + arraybuffer: ArrayBuffer; + blob: Blob; + stream: NodeJS.ReadableStream; +} + +type InputFileFormat = InputByType[keyof InputByType] | Promise; + +interface JSZipDefaults { + base64: boolean; // default false + binary: boolean; // default false + dir: boolean; // default false + createFolders: boolean; // default true + date: Date; // default null + compression: Compression | null; // default null + compressionOptions: CompressionOptions | null; // default null + comment: string | null; // default null + unixPermissions: number | string | null; // default null + dosPermissions: number | null; // default null +} + +type JSZipWithDefaults = typeof JSZip & { defaults: JSZipDefaults }; + +const JSZipX = JSZip as JSZipWithDefaults; + +export const createJSZip = () => { + const currDate = new Date(); + const dateWithOffset = new Date(currDate.getTime() - currDate.getTimezoneOffset() * 60000); + // replace the default date with dateWithOffset + JSZipX.defaults.date = dateWithOffset; + return new JSZipX(); +}; + +export const loadAsyncJSZip = (content: InputFileFormat, options?: JSZip.JSZipLoadOptions): Promise => { + return createJSZip().loadAsync(content, options) as Promise; +}; + +export type JSZipFile = typeof JSZipX; + +export type JSZipFileOptions = JSZip.JSZipFileOptions; From 8c79d4a3696f0237cbf1fb92b0059eec845ce058 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Mon, 10 Nov 2025 23:16:11 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20ValueStorage=20ts=20?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/synchronize.ts | 30 +++++++------------ src/pkg/backup/export.ts | 2 +- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index 7779ab497..57b0aac66 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -114,9 +114,14 @@ export class SynchronizeService { if (!code) { throw new Error(`Script ${script.uuid} code not found`); } + const lastModificationDate = script.updatetime || script.createtime || undefined; + const values = await this.value.getScriptValue(script); + const requires = await this.resource.getResourceByType(script, "require", false); + const requiresCss = await this.resource.getResourceByType(script, "require-css", false); + const resources = await this.resource.getResourceByType(script, "resource", false); const storage: ValueStorage = { - data: {}, - ts: Date.now(), + data: { ...values }, + ts: lastModificationDate || Date.now(), }; const ret = { code: code.code, @@ -136,26 +141,13 @@ export class SynchronizeService { }, }, // storage, - requires: [] as ResourceBackup[], - requiresCss: [] as ResourceBackup[], - resources: [] as ResourceBackup[], + requires: this.resourceToBackdata(requires), + requiresCss: this.resourceToBackdata(requiresCss), + resources: this.resourceToBackdata(resources), storage, - lastModificationDate: script.updatetime || script.createtime || undefined, + lastModificationDate, } satisfies ScriptBackupData; - const values = await this.value.getScriptValue(script); - for (const key of Object.keys(values)) { - storage.data[key] = values[key]; - } - - const requires = await this.resource.getResourceByType(script, "require", false); - const requiresCss = await this.resource.getResourceByType(script, "require-css", false); - const resources = await this.resource.getResourceByType(script, "resource", false); - - ret.requires = this.resourceToBackdata(requires); - ret.requiresCss = this.resourceToBackdata(requiresCss); - ret.resources = this.resourceToBackdata(resources); - ret.storage = storage; return ret; } diff --git a/src/pkg/backup/export.ts b/src/pkg/backup/export.ts index 6797648db..8ddfa548b 100644 --- a/src/pkg/backup/export.ts +++ b/src/pkg/backup/export.ts @@ -37,7 +37,7 @@ export default class BackupExport { // 写入脚本storage.json // 不想兼容tm的导出规则了,直接写入storage.json const storage = { ...script.storage }; - const { data } = storage; + const data = storage.data; for (const key of Object.keys(data)) { data[key] = toStorageValueStr(data[key]); } From c515f2f462e55d26f7405fc7538f366501c3b538 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Tue, 11 Nov 2025 06:45:58 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E8=AA=BF=E6=95=B4=20setValues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/synchronize.ts | 4 ++-- src/app/service/service_worker/value.ts | 12 +++++++++--- src/pages/import/App.tsx | 3 ++- src/pkg/backup/backup.test.ts | 11 ++++++----- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index 57b0aac66..a27093e02 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -115,13 +115,13 @@ export class SynchronizeService { throw new Error(`Script ${script.uuid} code not found`); } const lastModificationDate = script.updatetime || script.createtime || undefined; - const values = await this.value.getScriptValue(script); + const [values, valueRet] = await this.value.getScriptValueDetails(script); const requires = await this.resource.getResourceByType(script, "require", false); const requiresCss = await this.resource.getResourceByType(script, "require-css", false); const resources = await this.resource.getResourceByType(script, "resource", false); const storage: ValueStorage = { data: { ...values }, - ts: lastModificationDate || Date.now(), + ts: valueRet?.updatetime || lastModificationDate || Date.now(), }; const ret = { code: code.code, diff --git a/src/app/service/service_worker/value.ts b/src/app/service/service_worker/value.ts index 802b2a657..9e2cec286 100644 --- a/src/app/service/service_worker/value.ts +++ b/src/app/service/service_worker/value.ts @@ -39,7 +39,7 @@ export class ValueService { this.valueDAO.enableCache(); } - async getScriptValue(script: Script) { + async getScriptValueDetails(script: Script) { let data: { [key: string]: any } = {}; const ret = await this.valueDAO.get(getStorageName(script)); if (ret) { @@ -68,7 +68,11 @@ export class ValueService { } } } - return newValues; + return [newValues, ret] as const; + } + + getScriptValue(script: Script): Promise> { + return this.getScriptValueDetails(script).then((res) => res[0]); } // 推送值到tab @@ -116,10 +120,12 @@ export class ValueService { runFlag: "user", tabId: -2, }; + // 查询出脚本 const script = await this.scriptDAO.get(uuid); if (!script) { throw new Error("script not found"); } + // 查询老的值 const storageName = getStorageName(script); let oldValueRecord: { [key: string]: any } = {}; const cacheKey = `${CACHE_KEY_SET_VALUE}${storageName}`; @@ -127,7 +133,7 @@ export class ValueService { const _flag = await stackAsyncTask(cacheKey, async () => { let valueModel: Value | undefined = await this.valueDAO.get(storageName); if (!valueModel) { - const now = Date.now(); + const now = ts || Date.now(); const dataModel: { [key: string]: any } = {}; for (const [key, rTyped1] of keyValuePairs) { const value = decodeRValue(rTyped1); diff --git a/src/pages/import/App.tsx b/src/pages/import/App.tsx index 2162a200c..2fe857c8a 100644 --- a/src/pages/import/App.tsx +++ b/src/pages/import/App.tsx @@ -189,6 +189,7 @@ function App() { (async () => { // 导入数据 const { data } = item.storage; + const ts = item.storage.ts || 0; const entries = Object.entries(data); if (entries.length === 0) return; await sleep(((Math.random() * 600) | 0) + 200); @@ -197,7 +198,7 @@ function App() { for (const [key, value] of entries) { keyValuePairs.push([key, encodeRValue(value)]); } - await valueClient.setScriptValues({ uuid: uuid, keyValuePairs, isReplace: false, ts: Date.now() }); + await valueClient.setScriptValues({ uuid: uuid, keyValuePairs, isReplace: false, ts: ts }); })(), ]); setInstallNum((prev) => [prev[0] + 1, prev[1]]); diff --git a/src/pkg/backup/backup.test.ts b/src/pkg/backup/backup.test.ts index 42913fa6f..079db6362 100644 --- a/src/pkg/backup/backup.test.ts +++ b/src/pkg/backup/backup.test.ts @@ -5,6 +5,7 @@ import type { BackupData } from "./struct"; import { describe, expect, it } from "vitest"; import ZipFileSystem from "@Packages/filesystem/zip/zip"; +const ts0 = Date.now() - 5000; describe.concurrent("backup", () => { it.concurrent("empty", async () => { const zipFile = createJSZip(); @@ -70,7 +71,7 @@ describe.concurrent("backup", () => { }, ], storage: { - ts: 1, + ts: ts0 + 1, data: { num: 1, str: "data", @@ -159,7 +160,7 @@ describe.concurrent("backup", () => { }, ], storage: { - ts: 1, + ts: ts0 + 2, data: { num: 1, str: "data", @@ -228,7 +229,7 @@ describe.concurrent("backup", () => { }, ], storage: { - ts: 1, + ts: ts0 + 3, data: { num: 1, str: "data", @@ -278,7 +279,7 @@ describe.concurrent("backup", () => { }, ], storage: { - ts: 1, + ts: ts0 + 4, data: {}, }, lastModificationDate: expect.any(Number), @@ -343,7 +344,7 @@ describe.concurrent("backup", () => { }, ], storage: { - ts: 1, + ts: ts0 + 5, data: {}, }, lastModificationDate: expect.any(Number), From 3a4bc2a7453cf9f0f6e0df73e3ef8e6fed8f2d33 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:47:07 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E5=B0=8F=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/resource.ts | 10 ++++++---- src/app/service/service_worker/script.ts | 2 -- src/app/service/service_worker/value.ts | 2 +- src/pkg/backup/export.ts | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/app/service/service_worker/resource.ts b/src/app/service/service_worker/resource.ts index 9e06eb89b..3c2d69e35 100644 --- a/src/app/service/service_worker/resource.ts +++ b/src/app/service/service_worker/resource.ts @@ -299,7 +299,8 @@ export class ResourceService { if (!data.source) { return undefined; } - const time = Date.now(); + const now = Date.now(); + const ts = data.meta.ts || 0; let res = await this.resourceDAO.get(data.meta.url); if (!res) { // 新增资源 @@ -313,12 +314,13 @@ export class ResourceService { base64, link: {}, type, - createtime: time, - updatetime: time, + createtime: ts || Math.min(ts, now), + updatetime: ts || Math.min(ts, now), }; + } else { + res.updatetime = now; } res.link[uuid] = true; - res.updatetime = time; return await this.resourceDAO.update(data.meta.url, res); } diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index ed6d5140f..489f834a7 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -298,8 +298,6 @@ export class ScriptService { if (updatetime) { script.updatetime = updatetime; } - console.log(12388, createtime, updatetime); - console.log(new Error().stack); return this.scriptDAO .save(script) .then(async () => { diff --git a/src/app/service/service_worker/value.ts b/src/app/service/service_worker/value.ts index 9e2cec286..06bf3fdca 100644 --- a/src/app/service/service_worker/value.ts +++ b/src/app/service/service_worker/value.ts @@ -133,7 +133,7 @@ export class ValueService { const _flag = await stackAsyncTask(cacheKey, async () => { let valueModel: Value | undefined = await this.valueDAO.get(storageName); if (!valueModel) { - const now = ts || Date.now(); + const now = Date.now(); const dataModel: { [key: string]: any } = {}; for (const [key, rTyped1] of keyValuePairs) { const value = decodeRValue(rTyped1); diff --git a/src/pkg/backup/export.ts b/src/pkg/backup/export.ts index 8ddfa548b..8b35268a4 100644 --- a/src/pkg/backup/export.ts +++ b/src/pkg/backup/export.ts @@ -44,13 +44,14 @@ export default class BackupExport { const wrtieStorage = JSON.stringify(storage); const fileOpts = { modifiedDate: script.lastModificationDate } as FileCreateOptions; + const fileOptsStorage = { modifiedDate: storage.ts || script.lastModificationDate } as FileCreateOptions; return [ // 写脚本文件 this.fs.create(`${filename}.user.js`, fileOpts).then((fileWriter) => fileWriter.write(writeCode)), // 写入脚本options.json this.fs.create(`${filename}.options.json`, fileOpts).then((fileWriter) => fileWriter.write(writeOptions)), // 写入脚本storage.json - this.fs.create(`${filename}.storage.json`, fileOpts).then((fileWriter) => fileWriter.write(wrtieStorage)), + this.fs.create(`${filename}.storage.json`, fileOptsStorage).then((fileWriter) => fileWriter.write(wrtieStorage)), // 写入脚本资源文件 ...this.writeResource(filename, script.resources, "resources", fileOpts), ...this.writeResource(filename, script.requires, "requires", fileOpts), From af4f346e8d10a0151b58f8a2f23e21a9eecc0474 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:03:07 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/resource.ts | 4 ++-- src/app/service/service_worker/value.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/service/service_worker/resource.ts b/src/app/service/service_worker/resource.ts index 3c2d69e35..ce4d681f2 100644 --- a/src/app/service/service_worker/resource.ts +++ b/src/app/service/service_worker/resource.ts @@ -314,8 +314,8 @@ export class ResourceService { base64, link: {}, type, - createtime: ts || Math.min(ts, now), - updatetime: ts || Math.min(ts, now), + createtime: ts ? Math.min(ts, now) : now, + updatetime: ts ? Math.min(ts, now) : now, }; } else { res.updatetime = now; diff --git a/src/app/service/service_worker/value.ts b/src/app/service/service_worker/value.ts index 06bf3fdca..5e0f44cd1 100644 --- a/src/app/service/service_worker/value.ts +++ b/src/app/service/service_worker/value.ts @@ -148,8 +148,8 @@ export class ValueService { uuid: uuid, storageName: storageName, data: dataModel, - createtime: ts || Math.min(ts!, now), - updatetime: ts || Math.min(ts!, now), + createtime: ts ? Math.min(ts, now) : now, + updatetime: ts ? Math.min(ts, now) : now, }; } else { let changed = false; From 73d58967d67774b4d943ac6650fc60051f766e5a Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:07:38 +0900 Subject: [PATCH 6/7] details -> script --- src/app/service/service_worker/client.ts | 2 +- src/app/service/service_worker/script.ts | 12 ++++++------ src/app/service/service_worker/synchronize.ts | 2 +- src/pages/import/App.tsx | 2 +- src/pages/options/routes/script/ScriptEditor.tsx | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/service/service_worker/client.ts b/src/app/service/service_worker/client.ts index 3a841f6db..e0c0f42dd 100644 --- a/src/app/service/service_worker/client.ts +++ b/src/app/service/service_worker/client.ts @@ -53,7 +53,7 @@ export class ScriptClient extends Client { } install(params: { - details: Script; + script: Script; code: string; upsertBy?: InstallSource; createtime?: number; diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index 489f834a7..4a283589c 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -234,7 +234,7 @@ export class ScriptService { const { script } = await prepareScriptByCode(code, url, uuid); script.subscribeUrl = subscribeUrl; await this.installScript({ - details: script, + script, code, upsertBy: source, }); @@ -246,7 +246,7 @@ export class ScriptService { const { code, upsertBy, uuid } = param; const { script } = await prepareScriptByCode(code, "", uuid, true); await this.installScript({ - details: script, + script, code, upsertBy, }); @@ -267,14 +267,14 @@ export class ScriptService { // 安装脚本 / 更新腳本 async installScript(param: { - details: Script; + script: Script; code: string; upsertBy?: InstallSource; createtime?: number; updatetime?: number; }) { param.upsertBy = param.upsertBy || "user"; - const { details: script, upsertBy, createtime, updatetime } = param; + const { script, upsertBy, createtime, updatetime } = param; // 删 storage cache const compiledResourceUpdatePromise = this.compiledResourceDAO.delete(script.uuid); const logger = this.logger.with({ @@ -732,7 +732,7 @@ export class ScriptService { if (checkSilenceUpdate(oldScript!.metadata, script.metadata)) { logger?.info("silence update script"); await this.installScript({ - details: script, + script, code, upsertBy, }); @@ -915,7 +915,7 @@ export class ScriptService { const { script } = await prepareScriptByCode(code, url, uuid); console.log("slienceUpdate", script.name); await this.installScript({ - details: script, + script, code, upsertBy: "system", }); diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index a27093e02..8e3644e2c 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -583,7 +583,7 @@ export class SynchronizeService { ); script.origin = script.origin || metaObj.origin; this.script.installScript({ - details: script, + script, code, upsertBy: "sync", }); diff --git a/src/pages/import/App.tsx b/src/pages/import/App.tsx index 2fe857c8a..1bc9c9c55 100644 --- a/src/pages/import/App.tsx +++ b/src/pages/import/App.tsx @@ -172,7 +172,7 @@ function App() { const scriptDetails = item.script!.script!; const createtime = item.lastModificationDate; const updatetime = item.lastModificationDate; - await scriptClient.install({ details: scriptDetails, code: item.code, createtime, updatetime }); + await scriptClient.install({ script: scriptDetails, code: item.code, createtime, updatetime }); await Promise.all([ (async () => { // 导入资源 diff --git a/src/pages/options/routes/script/ScriptEditor.tsx b/src/pages/options/routes/script/ScriptEditor.tsx index f8aa841ec..50ac9f2f4 100644 --- a/src/pages/options/routes/script/ScriptEditor.tsx +++ b/src/pages/options/routes/script/ScriptEditor.tsx @@ -246,7 +246,7 @@ function ScriptEditor() { } if (script.ignoreVersion) script.ignoreVersion = ""; return scriptClient - .install({ details: script, code }) + .install({ script, code }) .then((update): Script => { if (!update) { Message.success(t("create_success_note")); From bff734328836333836f5f1a126d3352bd2568c0b Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:24:42 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=E5=90=8D=E5=AD=97=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E4=B8=BA=20zipFile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/synchronize.ts | 12 ++++++------ src/pages/components/CloudScriptPlan/index.tsx | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index 8e3644e2c..8d69439e1 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -234,11 +234,11 @@ export class SynchronizeService { // 请求导出文件 async requestExport(uuids?: string[]) { - const zip = createJSZip(); - const fs = new ZipFileSystem(zip); + const zipFile = createJSZip(); + const fs = new ZipFileSystem(zipFile); await this.backup(fs, uuids); // 生成文件,并下载 - const files = await zip.generateAsync({ + const files = await zipFile.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { @@ -258,8 +258,8 @@ export class SynchronizeService { // 备份到云端 async backupToCloud({ type, params }: { type: FileSystemType; params: any }) { // 首先生成zip文件 - const zip = createJSZip(); - const fs = new ZipFileSystem(zip); + const zipFile = createJSZip(); + const fs = new ZipFileSystem(zipFile); await this.backup(fs); this.logger.info("backup to cloud"); // 然后创建云端文件系统 @@ -270,7 +270,7 @@ export class SynchronizeService { // 云端文件系统写入文件 const file = await cloudFs.create(`scriptcat-backup-${dayFormat(new Date(), "YYYY-MM-DDTHH-mm-ss")}.zip`); await file.write( - await zip.generateAsync({ + await zipFile.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { diff --git a/src/pages/components/CloudScriptPlan/index.tsx b/src/pages/components/CloudScriptPlan/index.tsx index 09d2b73d9..3eae70a29 100644 --- a/src/pages/components/CloudScriptPlan/index.tsx +++ b/src/pages/components/CloudScriptPlan/index.tsx @@ -117,9 +117,9 @@ const CloudScriptPlan: React.FC<{ const values = await parseExportValue(script, params.exportValue); const cookies = await parseExportCookie(params.exportCookie); if (cloudScriptType === "local") { - const jszip = createJSZip(); + const zipFile = createJSZip(); const cloudScript = CloudScriptFactory.create("local", { - zip: jszip, + zip: zipFile, ...params, }); const code = await new ScriptCodeDAO().findByUUID(script.uuid); @@ -129,7 +129,7 @@ const CloudScriptPlan: React.FC<{ } cloudScript.exportCloud(script, code.code, values, cookies); // 生成文件,并下载 - const files = await jszip.generateAsync({ + const files = await zipFile.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: {