mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-16 11:42:19 +08:00
set proxy for Sub-Store and support control whether allow LAN connections to Sub-Store
This commit is contained in:
parent
ea5397f250
commit
710780456a
|
@ -1,8 +1,9 @@
|
|||
### New Features
|
||||
|
||||
- 支持多个订阅切换分别保存选择的节点
|
||||
- 支持在对话框直接拖动窗口
|
||||
- Sub-Store 请求默认经过内核代理
|
||||
- 允许控制是否允许局域网连接 Sub-Store
|
||||
- 全局模式时自动置顶 GLOBAL 代理组
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- 修复订阅显示样式错误
|
||||
- 修复下拉菜单组件样式异常
|
||||
|
|
|
@ -11,6 +11,8 @@ import express from 'express'
|
|||
export let pacPort: number
|
||||
export let subStorePort: number
|
||||
export let subStoreFrontendPort: number
|
||||
let subStoreFrontendServer: http.Server
|
||||
let subStoreBackendWorker: Worker
|
||||
|
||||
const defaultPacScript = `
|
||||
function FindProxyForURL(url, host) {
|
||||
|
@ -56,28 +58,42 @@ export async function startPacServer(): Promise<void> {
|
|||
server.unref()
|
||||
}
|
||||
|
||||
export async function startSubStoreServer(): Promise<void> {
|
||||
export async function startSubStoreFrontendServer(): Promise<void> {
|
||||
const { useSubStore = true, subStoreHost = '127.0.0.1' } = await getAppConfig()
|
||||
if (!useSubStore) return
|
||||
await stopSubStoreFrontendServer()
|
||||
subStoreFrontendPort = await findAvailablePort(14122)
|
||||
const app = express()
|
||||
app.use(express.static(path.join(resourcesFilesDir(), 'sub-store-frontend')))
|
||||
subStoreFrontendServer = app.listen(subStoreFrontendPort, subStoreHost)
|
||||
}
|
||||
|
||||
export async function stopSubStoreFrontendServer(): Promise<void> {
|
||||
if (subStoreFrontendServer) {
|
||||
subStoreFrontendServer.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function startSubStoreBackendServer(): Promise<void> {
|
||||
const {
|
||||
useSubStore = true,
|
||||
useCustomSubStore = false,
|
||||
subStoreHost = '127.0.0.1',
|
||||
subStoreBackendSyncCron = '',
|
||||
subStoreBackendDownloadCron = '',
|
||||
subStoreBackendUploadCron = ''
|
||||
} = await getAppConfig()
|
||||
const { 'mixed-port': port = 7890 } = await getControledMihomoConfig()
|
||||
if (!useSubStore) return
|
||||
if (!subStoreFrontendPort) {
|
||||
subStoreFrontendPort = await findAvailablePort(14122)
|
||||
const app = express()
|
||||
app.use(express.static(path.join(resourcesFilesDir(), 'sub-store-frontend')))
|
||||
app.listen(subStoreFrontendPort)
|
||||
}
|
||||
if (!useCustomSubStore && !subStorePort) {
|
||||
if (!useCustomSubStore) {
|
||||
await stopSubStoreBackendServer()
|
||||
subStorePort = await findAvailablePort(38324)
|
||||
const icon = nativeImage.createFromPath(subStoreIcon)
|
||||
icon.toDataURL()
|
||||
new Worker(path.join(resourcesFilesDir(), 'sub-store.bundle.js'), {
|
||||
subStoreBackendWorker = new Worker(path.join(resourcesFilesDir(), 'sub-store.bundle.js'), {
|
||||
env: {
|
||||
SUB_STORE_BACKEND_API_PORT: subStorePort.toString(),
|
||||
SUB_STORE_BACKEND_API_HOST: subStoreHost,
|
||||
SUB_STORE_DATA_BASE_PATH: subStoreDir(),
|
||||
SUB_STORE_BACKEND_CUSTOM_ICON: icon.toDataURL(),
|
||||
SUB_STORE_BACKEND_CUSTOM_NAME: 'Mihomo Party',
|
||||
|
@ -85,8 +101,17 @@ export async function startSubStoreServer(): Promise<void> {
|
|||
SUB_STORE_BACKEND_DOWNLOAD_CRON: subStoreBackendDownloadCron,
|
||||
SUB_STORE_BACKEND_UPLOAD_CRON: subStoreBackendUploadCron,
|
||||
SUB_STORE_MMDB_COUNTRY_PATH: path.join(mihomoWorkDir(), 'country.mmdb'),
|
||||
SUB_STORE_MMDB_ASN_PATH: path.join(mihomoWorkDir(), 'ASN.mmdb')
|
||||
SUB_STORE_MMDB_ASN_PATH: path.join(mihomoWorkDir(), 'ASN.mmdb'),
|
||||
HTTP_PROXY: `http://127.0.0.1:${port}`,
|
||||
HTTPS_PROXY: `http://127.0.0.1:${port}`,
|
||||
ALL_PROXY: `http://127.0.0.1:${port}`
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function stopSubStoreBackendServer(): Promise<void> {
|
||||
if (subStoreBackendWorker) {
|
||||
subStoreBackendWorker.terminate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,11 @@ import yaml from 'yaml'
|
|||
import { mkdir, writeFile, copyFile, rm, readdir } from 'fs/promises'
|
||||
import { existsSync } from 'fs'
|
||||
import path from 'path'
|
||||
import { startPacServer, startSubStoreServer } from '../resolve/server'
|
||||
import {
|
||||
startPacServer,
|
||||
startSubStoreBackendServer,
|
||||
startSubStoreFrontendServer
|
||||
} from '../resolve/server'
|
||||
import { triggerSysProxy } from '../sys/sysproxy'
|
||||
import {
|
||||
getAppConfig,
|
||||
|
@ -231,7 +235,8 @@ export async function init(): Promise<void> {
|
|||
await initFiles()
|
||||
await cleanup()
|
||||
await startPacServer()
|
||||
await startSubStoreServer()
|
||||
await startSubStoreFrontendServer()
|
||||
await startSubStoreBackendServer()
|
||||
const { sysProxy } = await getAppConfig()
|
||||
try {
|
||||
await triggerSysProxy(sysProxy.enable)
|
||||
|
|
|
@ -43,7 +43,14 @@ import {
|
|||
setOverride,
|
||||
updateOverrideItem
|
||||
} from '../config'
|
||||
import { startSubStoreServer, subStoreFrontendPort, subStorePort } from '../resolve/server'
|
||||
import {
|
||||
startSubStoreFrontendServer,
|
||||
startSubStoreBackendServer,
|
||||
stopSubStoreFrontendServer,
|
||||
stopSubStoreBackendServer,
|
||||
subStoreFrontendPort,
|
||||
subStorePort
|
||||
} from '../resolve/server'
|
||||
import {
|
||||
isEncryptionAvailable,
|
||||
manualGrantCorePermition,
|
||||
|
@ -190,7 +197,13 @@ export function registerIpcMainHandlers(): void {
|
|||
ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) =>
|
||||
ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action)
|
||||
)
|
||||
ipcMain.handle('startSubStoreServer', () => ipcErrorWrapper(startSubStoreServer)())
|
||||
ipcMain.handle('startSubStoreFrontendServer', () =>
|
||||
ipcErrorWrapper(startSubStoreFrontendServer)()
|
||||
)
|
||||
ipcMain.handle('stopSubStoreFrontendServer', () => ipcErrorWrapper(stopSubStoreFrontendServer)())
|
||||
ipcMain.handle('startSubStoreBackendServer', () => ipcErrorWrapper(startSubStoreBackendServer)())
|
||||
ipcMain.handle('stopSubStoreBackendServer', () => ipcErrorWrapper(stopSubStoreBackendServer)())
|
||||
|
||||
ipcMain.handle('subStorePort', () => subStorePort)
|
||||
ipcMain.handle('subStoreFrontendPort', () => subStoreFrontendPort)
|
||||
ipcMain.handle('subStoreSubs', () => ipcErrorWrapper(subStoreSubs)())
|
||||
|
|
|
@ -2,7 +2,12 @@ import React, { useState } from 'react'
|
|||
import SettingCard from '@renderer/components/base/base-setting-card'
|
||||
import SettingItem from '@renderer/components/base/base-setting-item'
|
||||
import { Button, Input, Switch } from '@nextui-org/react'
|
||||
import { startSubStoreServer } from '@renderer/utils/ipc'
|
||||
import {
|
||||
startSubStoreFrontendServer,
|
||||
startSubStoreBackendServer,
|
||||
stopSubStoreFrontendServer,
|
||||
stopSubStoreBackendServer
|
||||
} from '@renderer/utils/ipc'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
import debounce from '@renderer/utils/debounce'
|
||||
import { isValidCron } from 'cron-validator'
|
||||
|
@ -12,6 +17,7 @@ const SubStoreConfig: React.FC = () => {
|
|||
const {
|
||||
useSubStore = true,
|
||||
useCustomSubStore = false,
|
||||
subStoreHost = '127.0.0.1',
|
||||
customSubStoreUrl,
|
||||
subStoreBackendSyncCron,
|
||||
subStoreBackendDownloadCron,
|
||||
|
@ -31,14 +37,20 @@ const SubStoreConfig: React.FC = () => {
|
|||
useState(subStoreBackendUploadCron)
|
||||
return (
|
||||
<SettingCard title="Sub-Store 设置">
|
||||
<SettingItem title="启用 Sub-Store" divider>
|
||||
<SettingItem title="启用 Sub-Store" divider={useSubStore}>
|
||||
<Switch
|
||||
size="sm"
|
||||
isSelected={useSubStore}
|
||||
onValueChange={async (v) => {
|
||||
try {
|
||||
await patchAppConfig({ useSubStore: v })
|
||||
if (v) await startSubStoreServer()
|
||||
if (v) {
|
||||
await startSubStoreFrontendServer()
|
||||
await startSubStoreBackendServer()
|
||||
} else {
|
||||
await stopSubStoreFrontendServer()
|
||||
await stopSubStoreBackendServer()
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
|
@ -46,138 +58,163 @@ const SubStoreConfig: React.FC = () => {
|
|||
/>
|
||||
</SettingItem>
|
||||
{useSubStore && (
|
||||
<SettingItem title="使用自建 Sub-Store 后端" divider>
|
||||
<Switch
|
||||
size="sm"
|
||||
isSelected={useCustomSubStore}
|
||||
onValueChange={async (v) => {
|
||||
try {
|
||||
await patchAppConfig({ useCustomSubStore: v })
|
||||
if (!v) await startSubStoreServer()
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
)}
|
||||
{useCustomSubStore ? (
|
||||
<SettingItem title="自建 Sub-Store 后端地址">
|
||||
<Input
|
||||
size="sm"
|
||||
className="w-[60%]"
|
||||
value={customSubStoreUrlValue}
|
||||
placeholder="必须包含协议头"
|
||||
onValueChange={(v: string) => {
|
||||
setCustomSubStoreUrlValue(v)
|
||||
setCustomSubStoreUrl(v)
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
) : (
|
||||
<>
|
||||
<SettingItem title="定时同步订阅/文件" divider>
|
||||
<div className="flex w-[60%] gap-2">
|
||||
{subStoreBackendSyncCronValue !== subStoreBackendSyncCron && (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
if (
|
||||
!subStoreBackendSyncCronValue ||
|
||||
isValidCron(subStoreBackendSyncCronValue)
|
||||
) {
|
||||
await patchAppConfig({
|
||||
subStoreBackendSyncCron: subStoreBackendSyncCronValue
|
||||
})
|
||||
new Notification('重启应用生效')
|
||||
} else {
|
||||
alert('Cron 表达式无效')
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
<SettingItem title="允许局域网连接" divider>
|
||||
<Switch
|
||||
size="sm"
|
||||
isSelected={subStoreHost === '0.0.0.0'}
|
||||
onValueChange={async (v) => {
|
||||
try {
|
||||
if (v) {
|
||||
await patchAppConfig({ subStoreHost: '0.0.0.0' })
|
||||
} else {
|
||||
await patchAppConfig({ subStoreHost: '127.0.0.1' })
|
||||
}
|
||||
await startSubStoreFrontendServer()
|
||||
await startSubStoreBackendServer()
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
<SettingItem title="使用自建 Sub-Store 后端" divider>
|
||||
<Switch
|
||||
size="sm"
|
||||
isSelected={useCustomSubStore}
|
||||
onValueChange={async (v) => {
|
||||
try {
|
||||
await patchAppConfig({ useCustomSubStore: v })
|
||||
if (v) {
|
||||
await stopSubStoreBackendServer()
|
||||
} else {
|
||||
await startSubStoreBackendServer()
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
{useCustomSubStore ? (
|
||||
<SettingItem title="自建 Sub-Store 后端地址">
|
||||
<Input
|
||||
size="sm"
|
||||
className="flex-grown"
|
||||
value={subStoreBackendSyncCronValue}
|
||||
placeholder="Cron 表达式"
|
||||
className="w-[60%]"
|
||||
value={customSubStoreUrlValue}
|
||||
placeholder="必须包含协议头"
|
||||
onValueChange={(v: string) => {
|
||||
setSubStoreBackendSyncCronValue(v)
|
||||
setCustomSubStoreUrlValue(v)
|
||||
setCustomSubStoreUrl(v)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
<SettingItem title="定时恢复配置" divider>
|
||||
<div className="flex w-[60%] gap-2">
|
||||
{subStoreBackendDownloadCronValue !== subStoreBackendDownloadCron && (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
if (
|
||||
!subStoreBackendDownloadCronValue ||
|
||||
isValidCron(subStoreBackendDownloadCronValue)
|
||||
) {
|
||||
await patchAppConfig({
|
||||
subStoreBackendDownloadCron: subStoreBackendDownloadCronValue
|
||||
})
|
||||
new Notification('重启应用生效')
|
||||
} else {
|
||||
alert('Cron 表达式无效')
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
<Input
|
||||
size="sm"
|
||||
className="flex-grown"
|
||||
value={subStoreBackendDownloadCronValue}
|
||||
placeholder="Cron 表达式"
|
||||
onValueChange={(v: string) => {
|
||||
setSubStoreBackendDownloadCronValue(v)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
<SettingItem title="定时备份配置">
|
||||
<div className="flex w-[60%] gap-2">
|
||||
{subStoreBackendUploadCronValue !== subStoreBackendUploadCron && (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
if (
|
||||
!subStoreBackendUploadCronValue ||
|
||||
isValidCron(subStoreBackendUploadCronValue)
|
||||
) {
|
||||
await patchAppConfig({
|
||||
subStoreBackendUploadCron: subStoreBackendUploadCronValue
|
||||
})
|
||||
new Notification('重启应用生效')
|
||||
} else {
|
||||
alert('Cron 表达式无效')
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
<Input
|
||||
size="sm"
|
||||
className="flex-grown"
|
||||
value={subStoreBackendUploadCronValue}
|
||||
placeholder="Cron 表达式"
|
||||
onValueChange={(v: string) => {
|
||||
setSubStoreBackendUploadCronValue(v)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
</SettingItem>
|
||||
) : (
|
||||
<>
|
||||
<SettingItem title="定时同步订阅/文件" divider>
|
||||
<div className="flex w-[60%] gap-2">
|
||||
{subStoreBackendSyncCronValue !== subStoreBackendSyncCron && (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
if (
|
||||
!subStoreBackendSyncCronValue ||
|
||||
isValidCron(subStoreBackendSyncCronValue)
|
||||
) {
|
||||
await patchAppConfig({
|
||||
subStoreBackendSyncCron: subStoreBackendSyncCronValue
|
||||
})
|
||||
new Notification('重启应用生效')
|
||||
} else {
|
||||
alert('Cron 表达式无效')
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
<Input
|
||||
size="sm"
|
||||
className="flex-grown"
|
||||
value={subStoreBackendSyncCronValue}
|
||||
placeholder="Cron 表达式"
|
||||
onValueChange={(v: string) => {
|
||||
setSubStoreBackendSyncCronValue(v)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
<SettingItem title="定时恢复配置" divider>
|
||||
<div className="flex w-[60%] gap-2">
|
||||
{subStoreBackendDownloadCronValue !== subStoreBackendDownloadCron && (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
if (
|
||||
!subStoreBackendDownloadCronValue ||
|
||||
isValidCron(subStoreBackendDownloadCronValue)
|
||||
) {
|
||||
await patchAppConfig({
|
||||
subStoreBackendDownloadCron: subStoreBackendDownloadCronValue
|
||||
})
|
||||
new Notification('重启应用生效')
|
||||
} else {
|
||||
alert('Cron 表达式无效')
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
<Input
|
||||
size="sm"
|
||||
className="flex-grown"
|
||||
value={subStoreBackendDownloadCronValue}
|
||||
placeholder="Cron 表达式"
|
||||
onValueChange={(v: string) => {
|
||||
setSubStoreBackendDownloadCronValue(v)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
<SettingItem title="定时备份配置">
|
||||
<div className="flex w-[60%] gap-2">
|
||||
{subStoreBackendUploadCronValue !== subStoreBackendUploadCron && (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
if (
|
||||
!subStoreBackendUploadCronValue ||
|
||||
isValidCron(subStoreBackendUploadCronValue)
|
||||
) {
|
||||
await patchAppConfig({
|
||||
subStoreBackendUploadCron: subStoreBackendUploadCronValue
|
||||
})
|
||||
new Notification('重启应用生效')
|
||||
} else {
|
||||
alert('Cron 表达式无效')
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
)}
|
||||
<Input
|
||||
size="sm"
|
||||
className="flex-grown"
|
||||
value={subStoreBackendUploadCronValue}
|
||||
placeholder="Cron 表达式"
|
||||
onValueChange={(v: string) => {
|
||||
setSubStoreBackendUploadCronValue(v)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingItem>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</SettingCard>
|
||||
|
|
|
@ -8,7 +8,12 @@ import { platform } from '@renderer/utils/init'
|
|||
import { FaNetworkWired } from 'react-icons/fa'
|
||||
import { IoMdCloudDownload } from 'react-icons/io'
|
||||
import PubSub from 'pubsub-js'
|
||||
import { mihomoUpgrade, restartCore, triggerSysProxy } from '@renderer/utils/ipc'
|
||||
import {
|
||||
mihomoUpgrade,
|
||||
restartCore,
|
||||
startSubStoreBackendServer,
|
||||
triggerSysProxy
|
||||
} from '@renderer/utils/ipc'
|
||||
import React, { useState } from 'react'
|
||||
import InterfaceModal from '@renderer/components/mihomo/interface-modal'
|
||||
import { MdDeleteForever } from 'react-icons/md'
|
||||
|
@ -129,6 +134,7 @@ const Mihomo: React.FC = () => {
|
|||
className="mr-2"
|
||||
onPress={async () => {
|
||||
await onChangeNeedRestart({ 'mixed-port': mixedPortInput })
|
||||
await startSubStoreBackendServer()
|
||||
if (sysProxy?.enable) {
|
||||
triggerSysProxy(true)
|
||||
}
|
||||
|
|
|
@ -307,8 +307,20 @@ export async function getGistUrl(): Promise<string> {
|
|||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getGistUrl'))
|
||||
}
|
||||
|
||||
export async function startSubStoreServer(): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreServer'))
|
||||
export async function startSubStoreFrontendServer(): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreFrontendServer'))
|
||||
}
|
||||
|
||||
export async function stopSubStoreFrontendServer(): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('stopSubStoreFrontendServer'))
|
||||
}
|
||||
|
||||
export async function startSubStoreBackendServer(): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('startSubStoreBackendServer'))
|
||||
}
|
||||
|
||||
export async function stopSubStoreBackendServer(): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('stopSubStoreBackendServer'))
|
||||
}
|
||||
|
||||
export async function subStorePort(): Promise<number> {
|
||||
|
|
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
|
@ -231,6 +231,7 @@ interface IAppConfig {
|
|||
tunCardStatus?: CardStatus
|
||||
githubToken?: string
|
||||
useSubStore: boolean
|
||||
subStoreHost?: string
|
||||
subStoreBackendSyncCron?: string
|
||||
subStoreBackendDownloadCron?: string
|
||||
subStoreBackendUploadCron?: string
|
||||
|
|
Loading…
Reference in New Issue
Block a user