set proxy for Sub-Store and support control whether allow LAN connections to Sub-Store

This commit is contained in:
pompurin404 2024-10-15 13:50:19 +08:00
parent ea5397f250
commit 710780456a
No known key found for this signature in database
8 changed files with 249 additions and 149 deletions

View File

@ -1,8 +1,9 @@
### New Features
- 支持多个订阅切换分别保存选择的节点
- 支持在对话框直接拖动窗口
- Sub-Store 请求默认经过内核代理
- 允许控制是否允许局域网连接 Sub-Store
- 全局模式时自动置顶 GLOBAL 代理组
### Bug Fixes
- 修复订阅显示样式错误
- 修复下拉菜单组件样式异常

View File

@ -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()
}
}

View File

@ -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)

View File

@ -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)())

View File

@ -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>

View File

@ -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)
}

View File

@ -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> {

View File

@ -231,6 +231,7 @@ interface IAppConfig {
tunCardStatus?: CardStatus
githubToken?: string
useSubStore: boolean
subStoreHost?: string
subStoreBackendSyncCron?: string
subStoreBackendDownloadCron?: string
subStoreBackendUploadCron?: string