support custom substore backend

This commit is contained in:
pompurin404 2024-09-04 16:13:59 +08:00
parent 1773be543f
commit a1d3558581
No known key found for this signature in database
5 changed files with 70 additions and 17 deletions

View File

@ -1,14 +1,17 @@
import axios from 'axios' import axios from 'axios'
import { subStorePort } from '../resolve/server' import { subStorePort } from '../resolve/server'
import { getAppConfig } from '../config'
export async function subStoreSubs(): Promise<ISubStoreSub[]> { export async function subStoreSubs(): Promise<ISubStoreSub[]> {
const res = await axios.get(`http://127.0.0.1:${subStorePort}/api/subs`) const { useCustomSubStore = false, customSubStoreUrl = '' } = await getAppConfig()
const baseUrl = useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${subStorePort}`
const res = await axios.get(`${baseUrl}/api/subs`)
return res.data.data as ISubStoreSub[] return res.data.data as ISubStoreSub[]
} }
export async function subStoreCollections(): Promise<ISubStoreSub[]> { export async function subStoreCollections(): Promise<ISubStoreSub[]> {
const res = await axios.get(`http://127.0.0.1:${subStorePort}/api/collections`) const { useCustomSubStore = false, customSubStoreUrl = '' } = await getAppConfig()
const baseUrl = useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${subStorePort}`
const res = await axios.get(`${baseUrl}/api/collections`)
return res.data.data as ISubStoreSub[] return res.data.data as ISubStoreSub[]
} }

View File

@ -53,8 +53,8 @@ export async function startPacServer(): Promise<void> {
} }
export async function startSubStoreServer(): Promise<void> { export async function startSubStoreServer(): Promise<void> {
const { useSubStore = true } = await getAppConfig() const { useSubStore = true, useCustomSubStore = false } = await getAppConfig()
if (!useSubStore) return if (!useSubStore || useCustomSubStore) return
if (subStorePort) return if (subStorePort) return
subStorePort = await findAvailablePort(3000) subStorePort = await findAvailablePort(3000)
new Worker(path.join(resourcesFilesDir(), 'sub-store.bundle.js'), { new Worker(path.join(resourcesFilesDir(), 'sub-store.bundle.js'), {

View File

@ -1,7 +1,7 @@
import React, { Key } from 'react' import React, { Key, useState } from 'react'
import SettingCard from '../base/base-setting-card' import SettingCard from '../base/base-setting-card'
import SettingItem from '../base/base-setting-item' import SettingItem from '../base/base-setting-item'
import { Button, Select, SelectItem, Switch, Tab, Tabs } from '@nextui-org/react' import { Button, Input, Select, SelectItem, Switch, Tab, Tabs } from '@nextui-org/react'
import { BiCopy } from 'react-icons/bi' import { BiCopy } from 'react-icons/bi'
import useSWR from 'swr' import useSWR from 'swr'
import { import {
@ -16,6 +16,7 @@ import {
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { platform } from '@renderer/utils/init' import { platform } from '@renderer/utils/init'
import { useTheme } from 'next-themes' import { useTheme } from 'next-themes'
import debounce from '@renderer/utils/debounce'
const GeneralConfig: React.FC = () => { const GeneralConfig: React.FC = () => {
const { data: enable, mutate: mutateEnable } = useSWR('checkAutoRun', checkAutoRun) const { data: enable, mutate: mutateEnable } = useSWR('checkAutoRun', checkAutoRun)
@ -28,11 +29,17 @@ const GeneralConfig: React.FC = () => {
proxyInTray = true, proxyInTray = true,
useWindowFrame = false, useWindowFrame = false,
useSubStore = true, useSubStore = true,
useCustomSubStore = false,
customSubStoreUrl,
envType = platform === 'win32' ? 'powershell' : 'bash', envType = platform === 'win32' ? 'powershell' : 'bash',
autoCheckUpdate, autoCheckUpdate,
appTheme = 'system' appTheme = 'system'
} = appConfig || {} } = appConfig || {}
const [subStoreUrl, setSubStoreUrl] = useState(customSubStoreUrl)
const setSubStoreUrlDebounce = debounce((v: string) => {
patchAppConfig({ customSubStoreUrl: v })
}, 500)
const onThemeChange = (key: Key, type: 'theme' | 'color'): void => { const onThemeChange = (key: Key, type: 'theme' | 'color'): void => {
const [theme, color] = appTheme.split('-') const [theme, color] = appTheme.split('-')
@ -170,6 +177,36 @@ const GeneralConfig: React.FC = () => {
}} }}
/> />
</SettingItem> </SettingItem>
{useSubStore && (
<SettingItem title="使用自建Substore后端" 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="自建SubStore后端地址" divider>
<Input
size="sm"
className="w-[60%]"
value={subStoreUrl}
placeholder="必须包含协议头"
onValueChange={(v: string) => {
setSubStoreUrl(v)
setSubStoreUrlDebounce(v)
}}
/>
</SettingItem>
)}
<SettingItem title="使用系统标题栏" divider> <SettingItem title="使用系统标题栏" divider>
<Switch <Switch
size="sm" size="sm"

View File

@ -46,7 +46,7 @@ const Profiles: React.FC = () => {
mutateProfileConfig mutateProfileConfig
} = useProfileConfig() } = useProfileConfig()
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { useSubStore = true } = appConfig || {} const { useSubStore = true, useCustomSubStore = false, customSubStoreUrl = '' } = appConfig || {}
const { current, items = [] } = profileConfig || {} const { current, items = [] } = profileConfig || {}
const [sortedItems, setSortedItems] = useState(items) const [sortedItems, setSortedItems] = useState(items)
const [useProxy, setUseProxy] = useState(false) const [useProxy, setUseProxy] = useState(false)
@ -65,12 +65,14 @@ const Profiles: React.FC = () => {
useSubStore ? subStoreCollections : (): undefined => {} useSubStore ? subStoreCollections : (): undefined => {}
) )
const subStoreMenuItems = useMemo(() => { const subStoreMenuItems = useMemo(() => {
console.log(subs, collections)
const items: { icon?: ReactNode; key: string; name: string; divider: boolean }[] = [ const items: { icon?: ReactNode; key: string; name: string; divider: boolean }[] = [
{ {
key: 'open-substore', key: 'open-substore',
name: '访问 SubStore', name: '访问 SubStore',
icon: <SubStoreIcon />, icon: <SubStoreIcon />,
divider: Boolean(subs) || Boolean(collections) divider:
(Boolean(subs) && subs.length > 0) || (Boolean(collections) && collections.length > 0)
} }
] ]
if (subs) { if (subs) {
@ -79,7 +81,7 @@ const Profiles: React.FC = () => {
key: `sub-${sub.name}`, key: `sub-${sub.name}`,
name: sub.displayName, name: sub.displayName,
icon: sub.icon ? <img src={sub.icon} className="h-[18px] w-[18px]" /> : null, icon: sub.icon ? <img src={sub.icon} className="h-[18px] w-[18px]" /> : null,
divider: index === subs.length - 1 && Boolean(collections) divider: index === subs.length - 1 && Boolean(collections) && collections.length > 0
}) })
}) })
} }
@ -263,7 +265,11 @@ const Profiles: React.FC = () => {
onAction={async (key) => { onAction={async (key) => {
if (key === 'open-substore') { if (key === 'open-substore') {
const port = await subStorePort() const port = await subStorePort()
open(`https://sub-store.vercel.app/subs?api=http://127.0.0.1:${port}`) if (useCustomSubStore) {
open(`https://sub-store.vercel.app/subs?api=${customSubStoreUrl}`)
} else {
open(`https://sub-store.vercel.app/subs?api=http://127.0.0.1:${port}`)
}
} else if (key.toString().startsWith('sub-')) { } else if (key.toString().startsWith('sub-')) {
setSubStoreImporting(true) setSubStoreImporting(true)
try { try {
@ -274,7 +280,9 @@ const Profiles: React.FC = () => {
await addProfileItem({ await addProfileItem({
name: sub?.displayName ?? '', name: sub?.displayName ?? '',
type: 'remote', type: 'remote',
url: `http://127.0.0.1:${port}/download/${key.toString().replace('sub-', '')}?target=ClashMeta`, url: useCustomSubStore
? `${customSubStoreUrl}/download/${key.toString().replace('sub-', '')}?target=ClashMeta`
: `http://127.0.0.1:${port}/download/${key.toString().replace('sub-', '')}?target=ClashMeta`,
useProxy useProxy
}) })
} catch (e) { } catch (e) {
@ -286,13 +294,16 @@ const Profiles: React.FC = () => {
setSubStoreImporting(true) setSubStoreImporting(true)
try { try {
const port = await subStorePort() const port = await subStorePort()
const sub = collections.find( const collection = collections.find(
(sub) => sub.name === key.toString().replace('sub-', '') (collection) =>
collection.name === key.toString().replace('collection-', '')
) )
await addProfileItem({ await addProfileItem({
name: sub?.displayName ?? '', name: collection?.displayName ?? '',
type: 'remote', type: 'remote',
url: `http://127.0.0.1:${port}/download/collection/${key.toString().replace('collection-', '')}?target=ClashMeta`, url: useCustomSubStore
? `${customSubStoreUrl}/download/collection/${key.toString().replace('collection-', '')}?target=ClashMeta`
: `http://127.0.0.1:${port}/download/collection/${key.toString().replace('collection-', '')}?target=ClashMeta`,
useProxy useProxy
}) })
} catch (e) { } catch (e) {

View File

@ -220,6 +220,8 @@ interface IAppConfig {
connectionDirection: 'asc' | 'desc' connectionDirection: 'asc' | 'desc'
connectionOrderBy: 'time' | 'upload' | 'download' | 'uploadSpeed' | 'downloadSpeed' connectionOrderBy: 'time' | 'upload' | 'download' | 'uploadSpeed' | 'downloadSpeed'
useSubStore: boolean useSubStore: boolean
useCustomSubStore?: boolean
customSubStoreUrl?: string
autoSetDNS?: boolean autoSetDNS?: boolean
originDNS?: string originDNS?: string
useWindowFrame: boolean useWindowFrame: boolean