diff --git a/changelog.md b/changelog.md index 09dfe88..1c7174d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ ### Features - 支持在特定WiFi SSID下直连 +- 支持同步运行时配置到GitHub Gist ### Bug Fixes diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index bedfc1f..9194b3d 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -33,6 +33,7 @@ import { promisify } from 'util' import { mainWindow } from '..' import path from 'path' import { existsSync } from 'fs' +import { uploadRuntimeConfig } from '../resolve/gistApi' chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => { try { @@ -137,6 +138,7 @@ export async function startCore(detached = false): Promise[]> { if (data.toString().includes('Start initial Compatible provider default')) { try { mainWindow?.webContents.send('coreRestart') + await uploadRuntimeConfig() } catch { // ignore } diff --git a/src/main/resolve/gistApi.ts b/src/main/resolve/gistApi.ts new file mode 100644 index 0000000..be86a3a --- /dev/null +++ b/src/main/resolve/gistApi.ts @@ -0,0 +1,99 @@ +import axios from 'axios' +import { getAppConfig, getControledMihomoConfig } from '../config' +import { getRuntimeConfigStr } from '../core/factory' + +interface GistInfo { + id: string + description: string + files: Record +} + +async function listGists(token: string): Promise { + const { 'mixed-port': port = 7890 } = await getControledMihomoConfig() + const res = await axios.get('https://api.github.com/gists', { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${token}`, + 'X-GitHub-Api-Version': '2022-11-28' + }, + proxy: { + protocol: 'http', + host: '127.0.0.1', + port + } + }) + return res.data as GistInfo[] +} + +async function createGist(token: string, content: string): Promise { + const { 'mixed-port': port = 7890 } = await getControledMihomoConfig() + return await axios.post( + 'https://api.github.com/gists', + { + description: 'Auto Synced Mihomo Party Runtime Config', + public: false, + files: { 'mihomo-party.yaml': { content } } + }, + { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${token}`, + 'X-GitHub-Api-Version': '2022-11-28' + }, + proxy: { + protocol: 'http', + host: '127.0.0.1', + port + } + } + ) +} + +async function updateGist(token: string, id: string, content: string): Promise { + const { 'mixed-port': port = 7890 } = await getControledMihomoConfig() + return await axios.patch( + `https://api.github.com/gists/${id}`, + { + description: 'Auto Synced Mihomo Party Runtime Config', + files: { 'mihomo-party.yaml': { content } } + }, + { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${token}`, + 'X-GitHub-Api-Version': '2022-11-28' + }, + proxy: { + protocol: 'http', + host: '127.0.0.1', + port + } + } + ) +} + +export async function getGistUrl(): Promise { + const { githubToken } = await getAppConfig() + if (!githubToken) return '' + const gists = await listGists(githubToken) + const gist = gists.find((gist) => gist.description === 'Auto Synced Mihomo Party Runtime Config') + if (gist) { + return gist.files['mihomo-party.yaml'].raw_url + } else { + throw new Error('Gist not found') + } +} + +export async function uploadRuntimeConfig(): Promise { + const { githubToken } = await getAppConfig() + if (!githubToken) return + const gists = await listGists(githubToken) + console.log(gists) + const gist = gists.find((gist) => gist.description === 'Auto Synced Mihomo Party Runtime Config') + const config = await getRuntimeConfigStr() + if (gist) { + await updateGist(githubToken, gist.id, config) + } else { + await createGist(githubToken, config) + } +} diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index d6a19c4..080248e 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -77,6 +77,7 @@ import { subStoreCollections, subStoreSubs } from '../core/subStoreApi' import { logDir } from './dirs' import path from 'path' import v8 from 'v8' +import { getGistUrl } from '../resolve/gistApi' function ipcErrorWrapper( // eslint-disable-next-line @typescript-eslint/no-explicit-any fn: (...args: any[]) => Promise // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -188,6 +189,7 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort) ipcMain.handle('subStoreSubs', () => ipcErrorWrapper(subStoreSubs)()) ipcMain.handle('subStoreCollections', () => ipcErrorWrapper(subStoreCollections)()) + ipcMain.handle('getGistUrl', ipcErrorWrapper(getGistUrl)) ipcMain.handle('setNativeTheme', (_e, theme) => { setNativeTheme(theme) }) diff --git a/src/renderer/src/components/settings/mihomo-config.tsx b/src/renderer/src/components/settings/mihomo-config.tsx index 1532a57..c1d267d 100644 --- a/src/renderer/src/components/settings/mihomo-config.tsx +++ b/src/renderer/src/components/settings/mihomo-config.tsx @@ -4,8 +4,9 @@ import SettingItem from '../base/base-setting-item' import { Button, Input, Select, SelectItem, Switch } from '@nextui-org/react' import { useAppConfig } from '@renderer/hooks/use-app-config' import debounce from '@renderer/utils/debounce' -import { patchControledMihomoConfig, restartCore } from '@renderer/utils/ipc' +import { getGistUrl, patchControledMihomoConfig, restartCore } from '@renderer/utils/ipc' import { MdDeleteForever } from 'react-icons/md' +import { BiCopy } from 'react-icons/bi' const MihomoConfig: React.FC = () => { const { appConfig, patchAppConfig } = useAppConfig() @@ -13,6 +14,7 @@ const MihomoConfig: React.FC = () => { controlDns = true, controlSniff = true, delayTestTimeout, + githubToken = '', autoCloseConnection = true, pauseSSID = [], delayTestUrl, @@ -66,6 +68,41 @@ const MihomoConfig: React.FC = () => { }} /> + { + try { + const url = await getGistUrl() + if (url !== '') { + await navigator.clipboard.writeText(url) + } + } catch (e) { + alert(e) + } + }} + > + + + } + divider + > + { + patchAppConfig({ githubToken: v }) + }} + /> +