Improve substore integration
Some checks are pending
Build / windows (arm64) (push) Waiting to run
Build / windows (ia32) (push) Waiting to run
Build / windows (x64) (push) Waiting to run
Build / linux (arm64) (push) Waiting to run
Build / linux (x64) (push) Waiting to run
Build / macos (arm64) (push) Waiting to run
Build / macos (x64) (push) Waiting to run
Build / updater (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-bin) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron-bin) (push) Blocked by required conditions
Build / aur-git-updater (push) Waiting to run

This commit is contained in:
pompurin404 2024-09-04 18:45:48 +08:00
parent 317b9e8218
commit 73835b7bd3
No known key found for this signature in database
10 changed files with 252 additions and 102 deletions

View File

@ -26,7 +26,7 @@ import { existsSync } from 'fs'
import path from 'path'
import { startPacServer, startSubStoreServer } from '../resolve/server'
import { triggerSysProxy } from '../sys/sysproxy'
import { getAppConfig } from '../config'
import { getAppConfig, patchAppConfig } from '../config'
import { app } from 'electron'
async function initDirs(): Promise<void> {
@ -119,6 +119,31 @@ async function cleanup(): Promise<void> {
}
}
async function migration(): Promise<void> {
const {
siderOrder = [
'sysproxy',
'tun',
'profile',
'proxy',
'mihomo',
'connection',
'dns',
'sniff',
'log',
'rule',
'resource',
'override',
'substore'
],
useSubStore = true
} = await getAppConfig()
// add substore sider card
if (useSubStore && !siderOrder.includes('substore')) {
await patchAppConfig({ siderOrder: [...siderOrder, 'substore'] })
}
}
function initDeeplink(): void {
if (process.defaultApp) {
if (process.argv.length >= 2) {
@ -134,6 +159,7 @@ function initDeeplink(): void {
export async function init(): Promise<void> {
await initDirs()
await initConfig()
await migration()
await initFiles()
await cleanup()
await startPacServer()

View File

@ -30,7 +30,8 @@ export const defaultConfig: IAppConfig = {
'log',
'rule',
'resource',
'override'
'override',
'substore'
],
sysProxy: { enable: false, mode: 'manual' }
}

View File

@ -6,7 +6,7 @@
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data:;"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data:; frame-src https://sub-store.vercel.app;"
/>
</head>

View File

@ -27,10 +27,11 @@ import LogCard from '@renderer/components/sider/log-card'
import MihomoCoreCard from '@renderer/components/sider/mihomo-core-card'
import ResourceCard from '@renderer/components/sider/resource-card'
import UpdaterButton from '@renderer/components/updater/updater-button'
import { useAppConfig } from './hooks/use-app-config'
import { setNativeTheme, setTitleBarOverlay } from './utils/ipc'
import { platform } from './utils/init'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import { setNativeTheme, setTitleBarOverlay } from '@renderer/utils/ipc'
import { platform } from '@renderer/utils/init'
import { TitleBarOverlayOptions } from 'electron'
import SubStoreCard from '@renderer/components/sider/substore-card'
const App: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig()
@ -38,6 +39,7 @@ const App: React.FC = () => {
appTheme = 'system',
controlDns = true,
controlSniff = true,
useSubStore = true,
useWindowFrame = false,
siderOrder = [
'sysproxy',
@ -51,7 +53,8 @@ const App: React.FC = () => {
'log',
'rule',
'resource',
'override'
'override',
'substore'
]
} = appConfig || {}
const [order, setOrder] = useState(siderOrder)
@ -127,7 +130,8 @@ const App: React.FC = () => {
log: <LogCard key="log" />,
rule: <RuleCard key="rule" />,
resource: <ResourceCard key="resource" />,
override: <OverrideCard key="override" />
override: <OverrideCard key="override" />,
substore: <SubStoreCard key="substore" />
}
return (
@ -167,6 +171,7 @@ const App: React.FC = () => {
{order.map((key: string) => {
if (key === 'dns' && controlDns === false) return null
if (key === 'sniff' && controlSniff === false) return null
if (key === 'substore' && useSubStore === false) return null
return componentMap[key]
})}
</SortableContext>

View File

@ -1,83 +1,88 @@
import React from 'react'
const SubStoreIcon: React.FC = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 192 192"
width="20"
height="20"
{...props}
>
<g stroke-width="2" fill="none" stroke-linecap="butt" stroke="#ffffff" data-c-stroke="9bf6e1">
<path d="M 68.39 29.61 L 66.04 44.57" data-c-stroke="9cf7dc" />
<path d="M 49.10 42.15 Q 50.32 34.82 49.38 27.02" data-c-stroke="98f3ed" />
<path
d="M139 26.36 139.01 40.67M56.72 151.84 56.05 166.25"
data-c-stroke="9bf6e1"
stroke="#ffffff"
/>
<path d="M 139.28 166.07 L 138.77 151.74" data-c-stroke="98f3ed" />
<path d="M 121.23 143.44 L 119.51 159.49" data-c-stroke="9cf7dc" />
</g>
<path
fill="currentColor"
d="M 68.39 29.61 L 66.04 44.57 Q 58.35 38.29 49.10 42.15 Q 50.32 34.82 49.38 27.02 Q 59.70 24.72 68.39 29.61 Z"
data-c-fill="9af5e5"
/>
<path
fill="currentColor"
d="M 139.00 26.36 L 139.01 40.67 Q 129.55 40.19 122.90 47.18 Q 118.96 51.31 117.05 53.18 Q 116.67 53.55 116.30 53.17 L 107.39 44.27 A 0.43 0.43 0.0 0 1 107.39 43.67 Q 110.90 40.03 114.45 36.45 C 121.01 29.80 129.51 25.72 139.00 26.36 Z"
data-c-fill="97f2ee"
/>
<path
fill="currentColor"
d="M 139.00 26.36 C 149.79 27.49 158.88 35.13 163.59 44.55 C 168.66 54.70 165.55 66.43 158.45 74.97 Q 155.25 78.82 149.02 85.23 Q 148.70 85.57 148.37 85.24 L 139.73 76.60 Q 139.30 76.16 139.71 75.70 Q 142.64 72.41 146.67 67.43 Q 149.30 64.19 150.73 59.99 C 153.81 50.98 148.48 42.41 139.01 40.67 L 139.00 26.36 Z"
data-c-fill="9ef9d3"
/>
<path
fill="currentColor"
d="M 49.38 27.02 Q 50.32 34.82 49.10 42.15 Q 40.00 46.95 40.92 56.52 C 41.75 65.26 47.53 69.89 53.42 75.42 A 0.48 0.47 -46.3 0 1 53.43 76.10 L 44.44 85.08 Q 44.18 85.35 43.90 85.09 C 36.16 77.74 27.62 70.06 26.59 58.80 Q 25.43 46.09 34.03 36.28 Q 40.19 29.26 49.38 27.02 Z"
data-c-fill="95f0f5"
/>
<path
fill="currentColor"
d="M 66.04 44.57 L 68.39 29.61 Q 75.36 34.19 80.49 39.03 Q 90.49 48.44 105.30 63.79 Q 105.67 64.17 105.29 64.55 L 96.67 73.17 Q 96.31 73.53 95.95 73.17 Q 82.37 59.67 71.05 48.70 Q 69.90 47.58 66.04 44.57 Z"
data-c-fill="9ef9d3"
/>
<path
fill="currentColor"
fill-opacity=".961"
d="M74.45 96.86 86.25 108.66Q86.65 109.06 86.25 109.47L77.35 118.37Q77.11 118.61 76.86 118.37L55.28 96.78Q54.82 96.33 55.27 95.87 67.38 83.29 76.25 75.52 77.32 74.58 78.33 75.59L86.23 83.48Q86.55 83.81 86.23 84.14L74.45 95.91Q73.98 96.39 74.45 96.86ZM117.92 96.06 106.07 84.21Q105.67 83.81 106.07 83.4L113.56 75.91Q114.99 74.49 116.47 75.85 127.14 85.6 137.26 96.16 137.42 96.32 137.26 96.48L115.35 118.39Q115.13 118.61 114.91 118.39L106.11 109.59Q105.58 109.06 106.11 108.53L117.92 96.72A.47.47 0 0 0 117.92 96.06Z"
data-c-fill="ffffff"
data-o-fill=".961"
/>
<path
fill="currentColor"
d="M 56.72 151.84 L 56.05 166.25 C 43.95 166.03 33.55 157.74 28.69 147.07 C 23.99 136.75 27.53 125.24 34.82 116.79 Q 38.65 112.35 43.53 107.39 Q 43.98 106.93 44.43 107.38 L 53.06 116.01 Q 53.37 116.32 53.08 116.64 C 47.94 122.46 42.28 128.07 41.22 135.90 C 39.98 145.11 47.70 152.27 56.72 151.84 Z"
data-c-fill="9ef9d3"
/>
<path
fill="currentColor"
d="M 139.28 166.07 L 138.77 151.74 Q 148.28 149.97 150.90 141.99 C 152.98 135.69 150.90 128.82 146.49 124.19 Q 144.17 121.74 139.51 117.31 Q 138.89 116.72 139.50 116.10 L 148.09 107.52 Q 148.46 107.14 148.85 107.50 C 154.93 113.23 158.56 116.62 162.48 122.95 C 166.31 129.12 167.21 137.50 165.26 144.01 Q 160.45 160.01 144.46 165.22 Q 142.72 165.78 139.28 166.07 Z"
data-c-fill="95f0f5"
/>
<path
fill="currentColor"
d="M 121.23 143.44 L 119.51 159.49 Q 113.86 155.23 108.76 150.22 Q 97.69 139.36 87.37 128.74 Q 86.96 128.32 87.38 127.91 L 96.15 119.13 A 0.30 0.29 44.3 0 1 96.56 119.13 L 121.23 143.44 Z"
data-c-fill="9ef9d3"
/>
<path
fill="currentColor"
d="M 56.05 166.25 L 56.72 151.84 Q 62.78 151.21 66.28 148.29 Q 71.06 144.32 75.45 139.32 A 0.62 0.62 0.0 0 1 76.35 139.29 L 85.16 148.10 Q 85.45 148.40 85.18 148.71 Q 79.76 155.07 74.12 159.60 Q 66.32 165.87 56.05 166.25 Z"
data-c-fill="97f2ee"
/>
<path
fill="currentColor"
d="M 121.23 143.44 C 126.74 148.19 130.95 152.38 138.77 151.74 L 139.28 166.07 Q 128.14 166.99 119.51 159.49 L 121.23 143.44 Z"
data-c-fill="9af5e5"
/>
</svg>
)
import { GenIcon } from 'react-icons'
function SubStoreIcon(props): JSX.Element {
return GenIcon({
tag: 'svg',
attr: { viewBox: '0 0 192 192' },
child: [
{
tag: 'path',
attr: {
d: 'M 68.39 29.61 L 66.04 44.57 Q 58.35 38.29 49.10 42.15 Q 50.32 34.82 49.38 27.02 Q 59.70 24.72 68.39 29.61 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 139.00 26.36 L 139.01 40.67 Q 129.55 40.19 122.90 47.18 Q 118.96 51.31 117.05 53.18 Q 116.67 53.55 116.30 53.17 L 107.39 44.27 A 0.43 0.43 0.0 0 1 107.39 43.67 Q 110.90 40.03 114.45 36.45 C 121.01 29.80 129.51 25.72 139.00 26.36 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 139.00 26.36 C 149.79 27.49 158.88 35.13 163.59 44.55 C 168.66 54.70 165.55 66.43 158.45 74.97 Q 155.25 78.82 149.02 85.23 Q 148.70 85.57 148.37 85.24 L 139.73 76.60 Q 139.30 76.16 139.71 75.70 Q 142.64 72.41 146.67 67.43 Q 149.30 64.19 150.73 59.99 C 153.81 50.98 148.48 42.41 139.01 40.67 L 139.00 26.36 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 49.38 27.02 Q 50.32 34.82 49.10 42.15 Q 40.00 46.95 40.92 56.52 C 41.75 65.26 47.53 69.89 53.42 75.42 A 0.48 0.47 -46.3 0 1 53.43 76.10 L 44.44 85.08 Q 44.18 85.35 43.90 85.09 C 36.16 77.74 27.62 70.06 26.59 58.80 Q 25.43 46.09 34.03 36.28 Q 40.19 29.26 49.38 27.02 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 66.04 44.57 L 68.39 29.61 Q 75.36 34.19 80.49 39.03 Q 90.49 48.44 105.30 63.79 Q 105.67 64.17 105.29 64.55 L 96.67 73.17 Q 96.31 73.53 95.95 73.17 Q 82.37 59.67 71.05 48.70 Q 69.90 47.58 66.04 44.57 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M74.45 96.86 86.25 108.66Q86.65 109.06 86.25 109.47L77.35 118.37Q77.11 118.61 76.86 118.37L55.28 96.78Q54.82 96.33 55.27 95.87 67.38 83.29 76.25 75.52 77.32 74.58 78.33 75.59L86.23 83.48Q86.55 83.81 86.23 84.14L74.45 95.91Q73.98 96.39 74.45 96.86ZM117.92 96.06 106.07 84.21Q105.67 83.81 106.07 83.4L113.56 75.91Q114.99 74.49 116.47 75.85 127.14 85.6 137.26 96.16 137.42 96.32 137.26 96.48L115.35 118.39Q115.13 118.61 114.91 118.39L106.11 109.59Q105.58 109.06 106.11 108.53L117.92 96.72A.47.47 0 0 0 117.92 96.06Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 56.72 151.84 L 56.05 166.25 C 43.95 166.03 33.55 157.74 28.69 147.07 C 23.99 136.75 27.53 125.24 34.82 116.79 Q 38.65 112.35 43.53 107.39 Q 43.98 106.93 44.43 107.38 L 53.06 116.01 Q 53.37 116.32 53.08 116.64 C 47.94 122.46 42.28 128.07 41.22 135.90 C 39.98 145.11 47.70 152.27 56.72 151.84 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 139.28 166.07 L 138.77 151.74 Q 148.28 149.97 150.90 141.99 C 152.98 135.69 150.90 128.82 146.49 124.19 Q 144.17 121.74 139.51 117.31 Q 138.89 116.72 139.50 116.10 L 148.09 107.52 Q 148.46 107.14 148.85 107.50 C 154.93 113.23 158.56 116.62 162.48 122.95 C 166.31 129.12 167.21 137.50 165.26 144.01 Q 160.45 160.01 144.46 165.22 Q 142.72 165.78 139.28 166.07 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 121.23 143.44 L 119.51 159.49 Q 113.86 155.23 108.76 150.22 Q 97.69 139.36 87.37 128.74 Q 86.96 128.32 87.38 127.91 L 96.15 119.13 A 0.30 0.29 44.3 0 1 96.56 119.13 L 121.23 143.44 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 56.05 166.25 L 56.72 151.84 Q 62.78 151.21 66.28 148.29 Q 71.06 144.32 75.45 139.32 A 0.62 0.62 0.0 0 1 76.35 139.29 L 85.16 148.10 Q 85.45 148.40 85.18 148.71 Q 79.76 155.07 74.12 159.60 Q 66.32 165.87 56.05 166.25 Z'
},
child: []
},
{
tag: 'path',
attr: {
d: 'M 121.23 143.44 C 126.74 148.19 130.95 152.38 138.77 151.74 L 139.28 166.07 Q 128.14 166.99 119.51 159.49 L 121.23 143.44 Z'
},
child: []
}
]
})(props)
}
export default SubStoreIcon

View File

@ -163,7 +163,7 @@ const GeneralConfig: React.FC = () => {
</SettingItem>
</>
)}
<SettingItem title="启用SubStore" divider>
<SettingItem title="启用Sub-Store" divider>
<Switch
size="sm"
isSelected={useSubStore}
@ -178,7 +178,7 @@ const GeneralConfig: React.FC = () => {
/>
</SettingItem>
{useSubStore && (
<SettingItem title="使用自建Substore后端" divider>
<SettingItem title="使用自建Sub-Store后端" divider>
<Switch
size="sm"
isSelected={useCustomSubStore}
@ -194,7 +194,7 @@ const GeneralConfig: React.FC = () => {
</SettingItem>
)}
{useCustomSubStore && (
<SettingItem title="自建SubStore后端地址" divider>
<SettingItem title="自建Sub-Store后端地址" divider>
<Input
size="sm"
className="w-[60%]"

View File

@ -0,0 +1,61 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import SubStoreIcon from '../base/substore-icon'
const SubStoreCard: React.FC = () => {
const navigate = useNavigate()
const location = useLocation()
const match = location.pathname.includes('/substore')
const {
attributes,
listeners,
setNodeRef,
transform: tf,
transition,
isDragging
} = useSortable({
id: 'substore'
})
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
return (
<div
style={{
position: 'relative',
transform: CSS.Transform.toString(transform),
transition,
zIndex: isDragging ? 'calc(infinity)' : undefined
}}
className="col-span-1"
>
<Card
fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
isPressable
onPress={() => navigate('/substore')}
>
<CardBody className="pb-1 pt-0 px-0">
<div ref={setNodeRef} {...attributes} {...listeners} className="flex justify-between">
<Button
isIconOnly
className="bg-transparent pointer-events-none"
variant="flat"
color="default"
>
<SubStoreIcon
className={`${match ? 'text-white' : 'text-foreground'} text-[24px] font-bold`}
/>
</Button>
</div>
</CardBody>
<CardFooter className="pt-1">
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
Sub-Store
</h3>
</CardFooter>
</Card>
</div>
)
}
export default SubStoreCard

View File

@ -34,6 +34,7 @@ import { FaPlus } from 'react-icons/fa6'
import { IoMdRefresh } from 'react-icons/io'
import SubStoreIcon from '@renderer/components/base/substore-icon'
import useSWR from 'swr'
import { useNavigate } from 'react-router-dom'
const Profiles: React.FC = () => {
const {
@ -48,6 +49,7 @@ const Profiles: React.FC = () => {
const { appConfig } = useAppConfig()
const { useSubStore = true, useCustomSubStore = false, customSubStoreUrl = '' } = appConfig || {}
const { current, items = [] } = profileConfig || {}
const navigate = useNavigate()
const [sortedItems, setSortedItems] = useState(items)
const [useProxy, setUseProxy] = useState(false)
const [subStoreImporting, setSubStoreImporting] = useState(false)
@ -65,11 +67,10 @@ const Profiles: React.FC = () => {
useSubStore ? subStoreCollections : (): undefined => {}
)
const subStoreMenuItems = useMemo(() => {
console.log(subs, collections)
const items: { icon?: ReactNode; key: string; name: string; divider: boolean }[] = [
{
key: 'open-substore',
name: '访问 SubStore',
name: '访问 Sub-Store',
icon: <SubStoreIcon />,
divider:
(Boolean(subs) && subs.length > 0) || (Boolean(collections) && collections.length > 0)
@ -258,18 +259,13 @@ const Profiles: React.FC = () => {
isIconOnly
color="primary"
>
<SubStoreIcon />
<SubStoreIcon className="text-lg" />
</Button>
</DropdownTrigger>
<DropdownMenu
onAction={async (key) => {
if (key === 'open-substore') {
const port = await subStorePort()
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}`)
}
navigate('/substore')
} else if (key.toString().startsWith('sub-')) {
setSubStoreImporting(true)
try {

View File

@ -0,0 +1,52 @@
import { Button } from '@nextui-org/react'
import BasePage from '@renderer/components/base/base-page'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import { subStorePort } from '@renderer/utils/ipc'
import React, { useEffect, useState } from 'react'
import { HiExternalLink } from 'react-icons/hi'
const SubStore: React.FC = () => {
const { appConfig } = useAppConfig()
const { useCustomSubStore, customSubStoreUrl } = appConfig || {}
const [port, setPort] = useState<number | undefined>()
const getPort = async (): Promise<void> => {
setPort(await subStorePort())
}
useEffect(() => {
if (!useCustomSubStore) {
getPort()
}
}, [useCustomSubStore])
if (!useCustomSubStore && !port) return null
return (
<>
<BasePage
title="Sub-Store"
header={
<Button
title="在浏览器中打开"
isIconOnly
size="sm"
className="app-nodrag"
variant="light"
onPress={() => {
open(
`https://sub-store.vercel.app/subs?api=${useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${port}`}`
)
}}
>
<HiExternalLink className="text-lg" />
</Button>
}
>
<iframe
className="w-full h-full"
src={`https://sub-store.vercel.app/subs?api=${useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${port}`}`}
/>
</BasePage>
</>
)
}
export default SubStore

View File

@ -12,7 +12,7 @@ import Tun from '@renderer/pages/tun'
import Resources from '@renderer/pages/resources'
import DNS from '@renderer/pages/dns'
import Sniffer from '@renderer/pages/sniffer'
import SubStore from '@renderer/pages/substore'
const routes = [
{
path: '/mihomo',
@ -66,6 +66,10 @@ const routes = [
path: '/settings',
element: <Settings />
},
{
path: '/substore',
element: <SubStore />
},
{
path: '/',
element: <Navigate to="/proxies" />