support sync runtime config to github gist

This commit is contained in:
pompurin404 2024-09-22 20:03:02 +08:00
parent 80f22e719a
commit 4c238e1bdf
No known key found for this signature in database
7 changed files with 147 additions and 1 deletions

View File

@ -1,6 +1,7 @@
### Features ### Features
- 支持在特定WiFi SSID下直连 - 支持在特定WiFi SSID下直连
- 支持同步运行时配置到GitHub Gist
### Bug Fixes ### Bug Fixes

View File

@ -33,6 +33,7 @@ import { promisify } from 'util'
import { mainWindow } from '..' import { mainWindow } from '..'
import path from 'path' import path from 'path'
import { existsSync } from 'fs' import { existsSync } from 'fs'
import { uploadRuntimeConfig } from '../resolve/gistApi'
chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => { chokidar.watch(path.join(mihomoCoreDir(), 'meta-update'), {}).on('unlinkDir', async () => {
try { try {
@ -137,6 +138,7 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
if (data.toString().includes('Start initial Compatible provider default')) { if (data.toString().includes('Start initial Compatible provider default')) {
try { try {
mainWindow?.webContents.send('coreRestart') mainWindow?.webContents.send('coreRestart')
await uploadRuntimeConfig()
} catch { } catch {
// ignore // ignore
} }

View File

@ -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<string, { filename: string; raw_url: string }>
}
async function listGists(token: string): Promise<GistInfo[]> {
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<void> {
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<void> {
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<string> {
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<void> {
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)
}
}

View File

@ -77,6 +77,7 @@ import { subStoreCollections, subStoreSubs } from '../core/subStoreApi'
import { logDir } from './dirs' import { logDir } from './dirs'
import path from 'path' import path from 'path'
import v8 from 'v8' import v8 from 'v8'
import { getGistUrl } from '../resolve/gistApi'
function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -188,6 +189,7 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort) ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort)
ipcMain.handle('subStoreSubs', () => ipcErrorWrapper(subStoreSubs)()) ipcMain.handle('subStoreSubs', () => ipcErrorWrapper(subStoreSubs)())
ipcMain.handle('subStoreCollections', () => ipcErrorWrapper(subStoreCollections)()) ipcMain.handle('subStoreCollections', () => ipcErrorWrapper(subStoreCollections)())
ipcMain.handle('getGistUrl', ipcErrorWrapper(getGistUrl))
ipcMain.handle('setNativeTheme', (_e, theme) => { ipcMain.handle('setNativeTheme', (_e, theme) => {
setNativeTheme(theme) setNativeTheme(theme)
}) })

View File

@ -4,8 +4,9 @@ import SettingItem from '../base/base-setting-item'
import { Button, Input, Select, SelectItem, Switch } from '@nextui-org/react' import { Button, Input, Select, SelectItem, Switch } from '@nextui-org/react'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import debounce from '@renderer/utils/debounce' 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 { MdDeleteForever } from 'react-icons/md'
import { BiCopy } from 'react-icons/bi'
const MihomoConfig: React.FC = () => { const MihomoConfig: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
@ -13,6 +14,7 @@ const MihomoConfig: React.FC = () => {
controlDns = true, controlDns = true,
controlSniff = true, controlSniff = true,
delayTestTimeout, delayTestTimeout,
githubToken = '',
autoCloseConnection = true, autoCloseConnection = true,
pauseSSID = [], pauseSSID = [],
delayTestUrl, delayTestUrl,
@ -66,6 +68,41 @@ const MihomoConfig: React.FC = () => {
}} }}
/> />
</SettingItem> </SettingItem>
<SettingItem
title="同步运行时配置到 Gist"
actions={
<Button
title="复制 Gist URL"
isIconOnly
size="sm"
variant="light"
onPress={async () => {
try {
const url = await getGistUrl()
if (url !== '') {
await navigator.clipboard.writeText(url)
}
} catch (e) {
alert(e)
}
}}
>
<BiCopy className="text-lg" />
</Button>
}
divider
>
<Input
type="password"
size="sm"
className="w-[60%]"
value={githubToken}
placeholder="GitHub Token"
onValueChange={(v) => {
patchAppConfig({ githubToken: v })
}}
/>
</SettingItem>
<SettingItem title="代理节点展示列数" divider> <SettingItem title="代理节点展示列数" divider>
<Select <Select
className="w-[150px]" className="w-[150px]"

View File

@ -295,6 +295,10 @@ export async function setNativeTheme(theme: 'system' | 'light' | 'dark'): Promis
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme)) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme))
} }
export async function getGistUrl(): Promise<string> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getGistUrl'))
}
export async function startSubStoreServer(): Promise<void> { export async function startSubStoreServer(): Promise<void> {
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreServer')) return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreServer'))
} }

View File

@ -224,6 +224,7 @@ interface IAppConfig {
substoreCardStatus?: CardStatus substoreCardStatus?: CardStatus
sysproxyCardStatus?: CardStatus sysproxyCardStatus?: CardStatus
tunCardStatus?: CardStatus tunCardStatus?: CardStatus
githubToken?: string
useSubStore: boolean useSubStore: boolean
subStoreBackendSyncCron?: string subStoreBackendSyncCron?: string
subStoreBackendDownloadCron?: string subStoreBackendDownloadCron?: string