unified frontend and backend types

This commit is contained in:
pompurin404 2024-07-31 09:45:34 +08:00
parent cdad19d29a
commit 6bee06c4af
No known key found for this signature in database
10 changed files with 129 additions and 9 deletions

View File

@ -24,11 +24,13 @@
"@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@nextui-org/react": "^2.4.6", "@nextui-org/react": "^2.4.6",
"axios": "^1.7.2",
"electron-updater": "^6.2.1", "electron-updater": "^6.2.1",
"framer-motion": "^11.3.19", "framer-motion": "^11.3.19",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react-icons": "^5.2.1", "react-icons": "^5.2.1",
"react-router-dom": "^6.25.1" "react-router-dom": "^6.25.1",
"swr": "^2.2.5"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^2.0.0", "@electron-toolkit/eslint-config-prettier": "^2.0.0",

View File

@ -17,6 +17,9 @@ importers:
'@nextui-org/react': '@nextui-org/react':
specifier: ^2.4.6 specifier: ^2.4.6
version: 2.4.6(@types/react@18.3.3)(framer-motion@11.3.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.7) version: 2.4.6(@types/react@18.3.3)(framer-motion@11.3.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.7)
axios:
specifier: ^1.7.2
version: 1.7.2
electron-updater: electron-updater:
specifier: ^6.2.1 specifier: ^6.2.1
version: 6.2.1 version: 6.2.1
@ -32,6 +35,9 @@ importers:
react-router-dom: react-router-dom:
specifier: ^6.25.1 specifier: ^6.25.1
version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
swr:
specifier: ^2.2.5
version: 2.2.5(react@18.3.1)
devDependencies: devDependencies:
'@electron-toolkit/eslint-config-prettier': '@electron-toolkit/eslint-config-prettier':
specifier: ^2.0.0 specifier: ^2.0.0
@ -1959,6 +1965,9 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
axios@1.7.2:
resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@ -2086,6 +2095,9 @@ packages:
resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
engines: {node: '>=8'} engines: {node: '>=8'}
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
cliui@8.0.1: cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -2529,6 +2541,15 @@ packages:
flatted@3.3.1: flatted@3.3.1:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
follow-redirects@1.15.6:
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
for-each@0.3.3: for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@ -3458,6 +3479,9 @@ packages:
prop-types@15.8.1: prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
pump@3.0.0: pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
@ -3822,6 +3846,11 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
swr@2.2.5:
resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
synckit@0.9.1: synckit@0.9.1:
resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@ -4004,6 +4033,11 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
use-sync-external-store@1.2.2:
resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
utf8-byte-length@1.0.5: utf8-byte-length@1.0.5:
resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==}
@ -6973,6 +7007,14 @@ snapshots:
dependencies: dependencies:
possible-typed-array-names: 1.0.0 possible-typed-array-names: 1.0.0
axios@1.7.2:
dependencies:
follow-redirects: 1.15.6
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
base64-js@1.5.1: {} base64-js@1.5.1: {}
@ -7152,6 +7194,8 @@ snapshots:
string-width: 4.2.3 string-width: 4.2.3
optional: true optional: true
client-only@0.0.1: {}
cliui@8.0.1: cliui@8.0.1:
dependencies: dependencies:
string-width: 4.2.3 string-width: 4.2.3
@ -7759,6 +7803,8 @@ snapshots:
flatted@3.3.1: {} flatted@3.3.1: {}
follow-redirects@1.15.6: {}
for-each@0.3.3: for-each@0.3.3:
dependencies: dependencies:
is-callable: 1.2.7 is-callable: 1.2.7
@ -8675,6 +8721,8 @@ snapshots:
object-assign: 4.1.1 object-assign: 4.1.1
react-is: 16.13.1 react-is: 16.13.1
proxy-from-env@1.1.0: {}
pump@3.0.0: pump@3.0.0:
dependencies: dependencies:
end-of-stream: 1.4.4 end-of-stream: 1.4.4
@ -9114,6 +9162,12 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
swr@2.2.5(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
use-sync-external-store: 1.2.2(react@18.3.1)
synckit@0.9.1: synckit@0.9.1:
dependencies: dependencies:
'@pkgr/core': 0.1.1 '@pkgr/core': 0.1.1
@ -9319,6 +9373,10 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.3 '@types/react': 18.3.3
use-sync-external-store@1.2.2(react@18.3.1):
dependencies:
react: 18.3.1
utf8-byte-length@1.0.5: {} utf8-byte-length@1.0.5: {}
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}

View File

@ -1,7 +1,9 @@
import { app, shell, BrowserWindow, Tray, Menu } from 'electron' import { app, shell, BrowserWindow, Tray, Menu } from 'electron'
import { join } from 'path' import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset' import pngIcon from '../../resources/icon.png?asset'
import icoIcon from '../../resources/icon.ico?asset'
import { registerIpcMainHandlers } from './mihomo-api'
let window: BrowserWindow | null = null let window: BrowserWindow | null = null
let tray: Tray | null = null let tray: Tray | null = null
@ -16,7 +18,7 @@ function createWindow(): void {
height: 600, height: 600,
show: false, show: false,
autoHideMenuBar: true, autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}), ...(process.platform === 'linux' ? { icon: pngIcon } : {}),
webPreferences: { webPreferences: {
preload: join(__dirname, '../preload/index.js'), preload: join(__dirname, '../preload/index.js'),
sandbox: false sandbox: false
@ -48,7 +50,11 @@ function createWindow(): void {
} }
function createTray(): void { function createTray(): void {
tray = new Tray(icon) if (process.platform === 'linux') {
tray = new Tray(pngIcon)
} else {
tray = new Tray(icoIcon)
}
trayContextMenu = Menu.buildFromTemplate([ trayContextMenu = Menu.buildFromTemplate([
{ {
label: '显示窗口', label: '显示窗口',
@ -92,7 +98,7 @@ app.whenReady().then(() => {
app.on('browser-window-created', (_, window) => { app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window) optimizer.watchWindowShortcuts(window)
}) })
registerIpcMainHandlers()
createWindow() createWindow()
createTray() createTray()

31
src/main/mihomo-api.ts Normal file
View File

@ -0,0 +1,31 @@
import { ipcMain } from 'electron'
import axios, { AxiosInstance } from 'axios'
let axiosIns: AxiosInstance = null!
/// initialize some information
/// enable force update axiosIns
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
if (axiosIns && !force) return axiosIns
const server = '127.0.0.1:9097'
const secret = ''
axiosIns = axios.create({
baseURL: `http://${server}`,
proxy: false,
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
timeout: 15000
})
axiosIns.interceptors.response.use((r) => r.data)
return axiosIns
}
async function mihomoVersion(): Promise<IMihomoVersion> {
const instance = await getAxios()
return instance.get('/version') as Promise<IMihomoVersion>
}
export function registerIpcMainHandlers(): void {
ipcMain.handle('mihomoVersion', mihomoVersion)
}

View File

@ -1,3 +1,17 @@
import { Button } from '@nextui-org/react'
import { mihomoVersion } from '@renderer/utils/api'
import useSWR from 'swr'
export default function Settings(): JSX.Element { export default function Settings(): JSX.Element {
return <div>Settings</div> const { data, error, isLoading, mutate } = useSWR('mihomoVersion', mihomoVersion)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
return (
<div>
{data?.version}
<Button onPress={() => mutate()}>mutate</Button>
</div>
)
} }

View File

@ -0,0 +1,3 @@
export async function mihomoVersion(): Promise<IMihomoVersion> {
return await window.electron.ipcRenderer.invoke('mihomoVersion')
}

View File

@ -1 +0,0 @@
type OutboundMode = 'rule' | 'global' | 'direct'

6
src/shared/types.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
type OutboundMode = 'rule' | 'global' | 'direct'
interface IMihomoVersion {
version: string
meta: boolean
}

View File

@ -1,6 +1,6 @@
{ {
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json", "extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
"include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"], "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*", "src/shared/**/*.d.ts"],
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"types": ["electron-vite/node"] "types": ["electron-vite/node"]

View File

@ -4,7 +4,8 @@
"src/renderer/src/utils/env.d.ts", "src/renderer/src/utils/env.d.ts",
"src/renderer/src/**/*", "src/renderer/src/**/*",
"src/renderer/src/**/*.tsx", "src/renderer/src/**/*.tsx",
"src/preload/*.d.ts" "src/preload/*.d.ts",
"src/shared/**/*.d.ts"
], ],
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,