From a469e3fe5ba31287a33953ae042c175cf403be1c Mon Sep 17 00:00:00 2001 From: DIYgod Date: Sun, 29 Jun 2025 11:35:02 +0800 Subject: [PATCH 1/6] feat(desktop): set contextIsolation false --- apps/desktop/layer/main/src/manager/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/layer/main/src/manager/window.ts b/apps/desktop/layer/main/src/manager/window.ts index 79c501095..c4aa24972 100644 --- a/apps/desktop/layer/main/src/manager/window.ts +++ b/apps/desktop/layer/main/src/manager/window.ts @@ -298,7 +298,7 @@ class WindowManagerStatic { webviewTag: true, webSecurity: !DEV, nodeIntegration: true, - contextIsolation: true, + contextIsolation: false, }, ...this.getPlatformSpecificWindowConfig(), } From c48d211e229145e17d70d14757544e58a3769cc4 Mon Sep 17 00:00:00 2001 From: Innei Date: Sun, 29 Jun 2025 11:44:33 +0800 Subject: [PATCH 2/6] hotfix: i18n Signed-off-by: Innei --- apps/desktop/layer/renderer/src/lib/load-language.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/desktop/layer/renderer/src/lib/load-language.ts b/apps/desktop/layer/renderer/src/lib/load-language.ts index 7745398ec..b572d0507 100644 --- a/apps/desktop/layer/renderer/src/lib/load-language.ts +++ b/apps/desktop/layer/renderer/src/lib/load-language.ts @@ -74,6 +74,7 @@ export const loadLanguageAndApply = async (lang: string) => { } EventBus.dispatch("I18N_UPDATE", "") } else { + if (ELECTRON) return let importFilePath = "" if (IN_ELECTRON) { From 440392fa43c126995518242131a8115e5fd71796 Mon Sep 17 00:00:00 2001 From: Innei Date: Sun, 29 Jun 2025 12:03:17 +0800 Subject: [PATCH 3/6] chore: remove fast-folder-size Signed-off-by: Innei --- apps/desktop/forge.config.cts | 2 +- apps/desktop/layer/main/package.json | 1 - .../layer/main/src/ipc/services/app.ts | 76 ++++++- apps/desktop/layer/main/src/lib/cleaner.ts | 44 +++- .../modules/settings/tabs/data-control.tsx | 14 +- locales/settings/zh-CN.json | 2 +- pnpm-lock.yaml | 200 ------------------ 7 files changed, 116 insertions(+), 223 deletions(-) diff --git a/apps/desktop/forge.config.cts b/apps/desktop/forge.config.cts index 8d8a56db6..d2fe92d60 100644 --- a/apps/desktop/forge.config.cts +++ b/apps/desktop/forge.config.cts @@ -35,7 +35,7 @@ const ymlMapsMap = { win32: "latest.yml", } -const keepModules = new Set(["font-list", "vscode-languagedetection", "fast-folder-size"]) +const keepModules = new Set(["font-list", "vscode-languagedetection"]) const keepLanguages = new Set(["en", "en_GB", "en-US", "en_US"]) // remove folders & files not to be included in the app diff --git a/apps/desktop/layer/main/package.json b/apps/desktop/layer/main/package.json index a847c30fa..ef4f2823b 100644 --- a/apps/desktop/layer/main/package.json +++ b/apps/desktop/layer/main/package.json @@ -36,7 +36,6 @@ "electron-store": "10.1.0", "electron-updater": "6.6.2", "es-toolkit": "1.39.3", - "fast-folder-size": "2.4.0", "font-list": "1.5.1", "i18next": "25.2.1", "js-yaml": "4.1.0", diff --git a/apps/desktop/layer/main/src/ipc/services/app.ts b/apps/desktop/layer/main/src/ipc/services/app.ts index 5650ec723..88eaf68b9 100644 --- a/apps/desktop/layer/main/src/ipc/services/app.ts +++ b/apps/desktop/layer/main/src/ipc/services/app.ts @@ -1,13 +1,16 @@ +import fsp from "node:fs/promises" import path from "node:path" import { fileURLToPath } from "node:url" import { callWindowExpose } from "@follow/shared/bridge" import { DEV } from "@follow/shared/constants" -import { app, BrowserWindow, clipboard, dialog } from "electron" +import { app, BrowserWindow, clipboard, dialog, shell } from "electron" +import { getCacheSize } from "~/lib/cleaner" import { i18n } from "~/lib/i18n" +import { store, StoreKey } from "~/lib/store" import { registerAppTray } from "~/lib/tray" -import { logger } from "~/logger" +import { logger, revealLogFile } from "~/logger" import { AppManager } from "~/manager/app" import { WindowManager } from "~/manager/window" import { cleanupOldRender, loadDynamicRenderEntry } from "~/updater/hot-updater" @@ -179,4 +182,73 @@ export class AppService extends IpcService { return path.join(app.getAppPath(), input) } + + @IpcMethod() + openCacheFolder(_context: IpcContext): void { + const dir = path.join(app.getPath("userData"), "cache") + shell.openPath(dir) + } + + @IpcMethod() + getCacheLimit(_context: IpcContext): number { + return store.get(StoreKey.CacheSizeLimit) || 0 + } + + @IpcMethod() + async clearCache(_context: IpcContext): Promise { + const cachePath = path.join(app.getPath("userData"), "cache", "Cache_Data") + if (process.platform === "win32") { + // Request elevation on Windows + + try { + // Create a bat file to delete cache with elevated privileges + const batPath = path.join(app.getPath("temp"), "clear_cache.bat") + await fsp.writeFile(batPath, `@echo off\nrd /s /q "${cachePath}"\ndel "%~f0"`, "utf-8") + + // Execute the bat file with admin privileges + await shell.openPath(batPath) + return + } catch (err) { + logger.error("Failed to clear cache with elevation", { error: err }) + } + } + await fsp.rm(cachePath, { recursive: true, force: true }).catch(() => { + logger.error("Failed to clear cache") + }) + } + + // getCacheLimit: t.procedure.action(async () => { + // return store.get(StoreKey.CacheSizeLimit) + // }), + + // clearCache: t.procedure.action(async () => { + + // }), + + // limitCacheSize: t.procedure.input().action(async ({ input }) => { + // logger.info("set limitCacheSize", input) + // if (input === 0) { + // store.delete(StoreKey.CacheSizeLimit) + // } else { + // store.set(StoreKey.CacheSizeLimit, input) + // } + // }), + @IpcMethod() + limitCacheSize(_context: IpcContext, input: number): void { + if (input === 0) { + store.delete(StoreKey.CacheSizeLimit) + } else { + store.set(StoreKey.CacheSizeLimit, input) + } + } + + @IpcMethod() + revealLogFile(_context: IpcContext) { + return revealLogFile() + } + + @IpcMethod() + getCacheSize(_context: IpcContext) { + return getCacheSize() + } } diff --git a/apps/desktop/layer/main/src/lib/cleaner.ts b/apps/desktop/layer/main/src/lib/cleaner.ts index 5b6a02f6f..6f95feda2 100644 --- a/apps/desktop/layer/main/src/lib/cleaner.ts +++ b/apps/desktop/layer/main/src/lib/cleaner.ts @@ -1,8 +1,6 @@ import { statSync } from "node:fs" import fsp from "node:fs/promises" -import { createRequire } from "node:module" import path from "node:path" -import { promisify } from "node:util" import { callWindowExpose } from "@follow/shared/bridge" import { app, dialog } from "electron" @@ -14,13 +12,37 @@ import { WindowManager } from "~/manager/window" import { t } from "./i18n" import { store, StoreKey } from "./store" -const esModuleInterop = (module: any) => { - return module.default || module +const getFolderSize = async (dir: string): Promise => { + try { + const files = await fsp.readdir(dir, { withFileTypes: true }) + const sizes = await Promise.all( + files.map(async (file) => { + const filePath = path.join(dir, file.name) + + if (file.isSymbolicLink()) { + return 0 + } + + if (file.isDirectory()) { + return await getFolderSize(filePath) + } + + if (file.isFile()) { + try { + const { size } = await fsp.stat(filePath) + return size + } catch { + return 0 + } + } + return 0 + }), + ) + return sizes.reduce((acc, size) => acc + size, 0) + } catch { + return 0 + } } -const require = createRequire(import.meta.url) -const fastFolderSize = esModuleInterop( - require("fast-folder-size"), -) as typeof import("fast-folder-size").default export const clearAllDataAndConfirm = async () => { const win = WindowManager.getMainWindow() @@ -71,12 +93,12 @@ export const clearAllData = async () => { caller.toast.error(`Error resetting app data: ${error.message}`) } } -const fastFolderSizeAsync = promisify(fastFolderSize) + export const getCacheSize = async () => { const cachePath = path.join(app.getPath("userData"), "cache") // Size is in bytes - const sizeInBytes = await fastFolderSizeAsync(cachePath).catch((error) => { + const sizeInBytes = await getFolderSize(cachePath).catch((error) => { logger.error(error) }) return sizeInBytes || 0 @@ -155,7 +177,7 @@ export const clearCacheCronJob = () => { export const checkAndCleanCodeCache = async () => { const cachePath = path.join(app.getPath("userData"), "Code Cache") - const size = await fastFolderSizeAsync(cachePath).catch((error) => { + const size = await getFolderSize(cachePath).catch((error) => { logger.error(error) }) diff --git a/apps/desktop/layer/renderer/src/modules/settings/tabs/data-control.tsx b/apps/desktop/layer/renderer/src/modules/settings/tabs/data-control.tsx index d4ea2e4e3..42698adea 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/tabs/data-control.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/tabs/data-control.tsx @@ -112,7 +112,7 @@ export const SettingDataControl = () => { description: t("general.log_file.description"), buttonText: t("general.log_file.button"), action: () => { - ;(ipcServices as any)?.revealLogFile?.() + ipcServices?.app.revealLogFile?.() }, }, ]} @@ -244,7 +244,7 @@ const CleanElectronCache = () => { {t("data_control.clean_cache.button")} { - ;(ipcServices as any)?.openCacheFolder?.() + ipcServices?.app.openCacheFolder?.() }} className="center flex" > @@ -253,7 +253,7 @@ const CleanElectronCache = () => { } action={async () => { - await (ipcServices as any)?.clearCache?.() + await ipcServices?.app.clearCache?.() queryClient.setQueryData(["app", "cache", "size"], 0) }} buttonText={t("data_control.clean_cache.button")} @@ -267,7 +267,7 @@ const AppCacheLimit = () => { const { data: cacheSize, isLoading: isLoadingCacheSize } = useQuery({ queryKey: ["app", "cache", "size"], queryFn: async () => { - const byteSize = (await (ipcServices as any)?.getCacheSize?.()) ?? 0 + const byteSize = (await ipcServices?.app.getCacheSize?.()) ?? 0 return Math.round(byteSize / 1024 / 1024) }, refetchOnMount: "always", @@ -279,13 +279,13 @@ const AppCacheLimit = () => { } = useQuery({ queryKey: ["app", "cache", "limit"], queryFn: async () => { - const size = (await (ipcServices as any)?.getCacheLimit?.()) ?? 0 + const size = (await ipcServices?.app.getCacheLimit?.()) ?? 0 return size }, }) const onChange = (value: number[]) => { - ;(ipcServices as any)?.limitCacheSize?.(value[0]!) + ipcServices?.app.limitCacheSize?.(value[0]!) refetchCacheLimit() } @@ -294,7 +294,7 @@ const AppCacheLimit = () => { const InfinitySymbol = return ( -
+