diff --git a/changelog.md b/changelog.md index 3864829..082efd2 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,4 @@ ### New Features -- 优化内核管理逻辑 -- 完善错误处理 -- 支持节点搜索 +- 内置 SubStore 服务 diff --git a/scripts/prepare.mjs b/scripts/prepare.mjs index 86673c0..bbcf9b4 100644 --- a/scripts/prepare.mjs +++ b/scripts/prepare.mjs @@ -271,6 +271,12 @@ const resolveRunner = () => file: 'mihomo-party-run.exe', downloadURL: `https://github.com/pompurin404/mihomo-party-run/releases/download/${arch}/mihomo-party-run.exe` }) +const resolveSubstore = () => + resolveResource({ + file: 'sub-store.bundle.js', + downloadURL: + 'https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store.bundle.js' + }) const resolveFont = async () => { const targetPath = path.join(cwd, 'src', 'renderer', 'src', 'assets', 'NotoColorEmoji.ttf') @@ -322,6 +328,11 @@ const tasks = [ func: resolveRunner, retry: 5, winOnly: true + }, + { + name: 'substore', + func: resolveSubstore, + retry: 5 } ] diff --git a/src/main/resolve/backup.ts b/src/main/resolve/backup.ts index 50b61bd..4fd74e8 100644 --- a/src/main/resolve/backup.ts +++ b/src/main/resolve/backup.ts @@ -23,6 +23,7 @@ export async function webdavBackup(): Promise { zip.addLocalFile(overrideConfigPath()) zip.addLocalFolder(profilesDir(), 'profiles') zip.addLocalFolder(overrideDir(), 'override') + zip.addLocalFolder(overrideDir(), 'substore') const date = new Date() const zipFileName = `${process.platform}_${dayjs(date).format('YYYY-MM-DD_HH-mm-ss')}.zip` diff --git a/src/main/resolve/server.ts b/src/main/resolve/server.ts index 50aa452..5dab11c 100644 --- a/src/main/resolve/server.ts +++ b/src/main/resolve/server.ts @@ -1,8 +1,12 @@ import { getAppConfig, getControledMihomoConfig } from '../config' +import { Worker } from 'worker_threads' +import { subStoreDir } from '../utils/dirs' import http from 'http' import net from 'net' +import path from 'path' export let pacPort: number +export let subStorePort: number const defaultPacScript = ` function FindProxyForURL(url, host) { @@ -47,3 +51,16 @@ export async function startPacServer(): Promise { .listen(pacPort) server.unref() } + +export async function startSubStoreServer(): Promise { + const { useSubStore = true } = await getAppConfig() + if (!useSubStore) return + if (subStorePort) return + subStorePort = await findAvailablePort(3000) + new Worker(path.join(subStoreDir(), 'sub-store.bundle.js'), { + env: { + SUB_STORE_BACKEND_API_PORT: subStorePort.toString(), + SUB_STORE_DATA_BASE_PATH: subStoreDir() + } + }) +} diff --git a/src/main/utils/dirs.ts b/src/main/utils/dirs.ts index b213ceb..d4750dd 100644 --- a/src/main/utils/dirs.ts +++ b/src/main/utils/dirs.ts @@ -25,6 +25,10 @@ export function taskDir(): string { return dir } +export function subStoreDir(): string { + return path.join(dataDir(), 'substore') +} + export function exeDir(): string { return path.dirname(exePath()) } diff --git a/src/main/utils/init.ts b/src/main/utils/init.ts index 6ea9493..f92bc54 100644 --- a/src/main/utils/init.ts +++ b/src/main/utils/init.ts @@ -10,7 +10,8 @@ import { profileConfigPath, profilePath, profilesDir, - resourcesFilesDir + resourcesFilesDir, + subStoreDir } from './dirs' import { defaultConfig, @@ -23,7 +24,7 @@ import yaml from 'yaml' import { mkdir, writeFile, copyFile, rm, readdir } from 'fs/promises' import { existsSync } from 'fs' import path from 'path' -import { startPacServer } from '../resolve/server' +import { startPacServer, startSubStoreServer } from '../resolve/server' import { triggerSysProxy } from '../sys/sysproxy' import { getAppConfig } from '../config' import { app } from 'electron' @@ -47,6 +48,9 @@ async function initDirs(): Promise { if (!existsSync(mihomoTestDir())) { await mkdir(mihomoTestDir()) } + if (!existsSync(subStoreDir())) { + await mkdir(subStoreDir()) + } } async function initConfig(): Promise { @@ -79,7 +83,15 @@ async function initFiles(): Promise { await copyFile(sourcePath, testTargrtPath) } } + const copySubStore = async (): Promise => { + const targetPath = path.join(subStoreDir(), 'sub-store.bundle.js') + const sourcePath = path.join(resourcesFilesDir(), 'sub-store.bundle.js') + if (existsSync(sourcePath)) { + await copyFile(sourcePath, targetPath) + } + } await Promise.all([ + copySubStore(), copy('country.mmdb'), copy('geoip.dat'), copy('geosite.dat'), @@ -133,6 +145,7 @@ export async function init(): Promise { await initFiles() await cleanup() await startPacServer() + await startSubStoreServer() const { sysProxy } = await getAppConfig() await triggerSysProxy(sysProxy.enable) diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index f46ea66..31dde19 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -42,6 +42,7 @@ import { setOverride, updateOverrideItem } from '../config' +import { startSubStoreServer, subStorePort } from '../resolve/server' import { isEncryptionAvailable, manualGrantCorePermition, restartCore } from '../core/manager' import { triggerSysProxy } from '../sys/sysproxy' import { checkUpdate, downloadAndInstallUpdate } from '../resolve/autoUpdater' @@ -165,6 +166,8 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) => ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action) ) + ipcMain.handle('startSubStoreServer', () => ipcErrorWrapper(startSubStoreServer)()) + ipcMain.handle('subStorePort', () => subStorePort) ipcMain.handle('setNativeTheme', (_e, theme) => { setNativeTheme(theme) }) diff --git a/src/renderer/src/components/base/substore-icon.tsx b/src/renderer/src/components/base/substore-icon.tsx new file mode 100644 index 0000000..776512c --- /dev/null +++ b/src/renderer/src/components/base/substore-icon.tsx @@ -0,0 +1,83 @@ +import React from 'react' + +const SubStoreIcon: React.FC = (props) => ( + + + + + + + + + + + + + + + + + + + + +) + +export default SubStoreIcon diff --git a/src/renderer/src/components/settings/general-config.tsx b/src/renderer/src/components/settings/general-config.tsx index d84df05..60bdc25 100644 --- a/src/renderer/src/components/settings/general-config.tsx +++ b/src/renderer/src/components/settings/general-config.tsx @@ -10,7 +10,8 @@ import { disableAutoRun, enableAutoRun, relaunchApp, - restartCore + restartCore, + startSubStoreServer } from '@renderer/utils/ipc' import { useAppConfig } from '@renderer/hooks/use-app-config' import { platform } from '@renderer/utils/init' @@ -26,6 +27,7 @@ const GeneralConfig: React.FC = () => { showTraffic = true, proxyInTray = true, useWindowFrame = false, + useSubStore = true, envType = platform === 'win32' ? 'powershell' : 'bash', autoCheckUpdate, appTheme = 'system' @@ -154,6 +156,20 @@ const GeneralConfig: React.FC = () => { )} + + { + try { + await patchAppConfig({ useSubStore: v }) + if (v) await startSubStoreServer() + } catch (e) { + alert(e) + } + }} + /> + { const { @@ -36,6 +38,8 @@ const Profiles: React.FC = () => { changeCurrentProfile, mutateProfileConfig } = useProfileConfig() + const { appConfig } = useAppConfig() + const { useSubStore = true } = appConfig || {} const { current, items = [] } = profileConfig || {} const [sortedItems, setSortedItems] = useState(items) const [useProxy, setUseProxy] = useState(false) @@ -221,6 +225,21 @@ const Profiles: React.FC = () => { 新建 + {useSubStore && ( + + )} diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index d5dc8b5..310e5e2 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -291,6 +291,14 @@ export async function setNativeTheme(theme: 'system' | 'light' | 'dark'): Promis return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme)) } +export async function startSubStoreServer(): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreServer')) +} + +export async function subStorePort(): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('subStorePort')) +} + export async function openFile( type: 'profile' | 'override', id: string, diff --git a/src/shared/types.d.ts b/src/shared/types.d.ts index 80243a1..70d04bf 100644 --- a/src/shared/types.d.ts +++ b/src/shared/types.d.ts @@ -219,6 +219,7 @@ interface IAppConfig { proxyCols: 'auto' | '1' | '2' | '3' | '4' connectionDirection: 'asc' | 'desc' connectionOrderBy: 'time' | 'upload' | 'download' | 'uploadSpeed' | 'downloadSpeed' + useSubStore?: boolean autoSetDNS?: boolean originDNS?: string useWindowFrame: boolean