refactor: add api module

This commit is contained in:
kunish 2023-09-12 21:33:16 +08:00
parent 34ea1f1c56
commit 71fd8ab22e
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
6 changed files with 235 additions and 135 deletions

167
src/apis/index.ts Normal file
View File

@ -0,0 +1,167 @@
import { createSignal } from 'solid-js'
import { toast } from 'solid-toast'
import { setBackendConfig, useRequest } from '~/signals'
import {
BackendVersion,
Config,
Proxy,
ProxyProvider,
Rule,
RuleProvider,
} from '~/types'
export const closeAllConnectionsAPI = () => {
const request = useRequest()
return request.delete('connections')
}
export const closeSingleConnectionAPI = (id: string) => {
const request = useRequest()
return request.delete(`connections/${id}`)
}
export const [updatingGEODatabases, setUpdatingGEODatabases] =
createSignal(false)
export const [upgradingBackend, setUpgradingBackend] = createSignal(false)
export const [restartingBackend, setRestartingBackend] = createSignal(false)
export const updateGEODatabasesAPI = async () => {
const request = useRequest()
setUpdatingGEODatabases(true)
try {
await request.post('configs/geo')
} catch {}
setUpdatingGEODatabases(false)
}
export const upgradeBackendAPI = async () => {
const request = useRequest()
setUpgradingBackend(true)
try {
await request.post('upgrade')
} catch {}
setUpgradingBackend(false)
}
export const restartBackendAPI = async () => {
const request = useRequest()
setRestartingBackend(true)
try {
await request.post('restart')
} catch {}
setRestartingBackend(false)
}
export const fetchBackendConfigAPI = () => {
const request = useRequest()
return request.get('configs').json<Config>()
}
export const updateBackendConfigAPI = async (
key: keyof Config,
value: Config[keyof Config],
) => {
try {
const request = useRequest()
await request
.patch('configs', {
body: JSON.stringify({
[key]: value,
}),
})
.json<Config>()
const updatedConfig = await fetchBackendConfigAPI()
setBackendConfig(updatedConfig)
} catch (err) {
toast.error((err as Error).message)
}
}
export const fetchBackendVersionAPI = async () => {
const request = useRequest()
const { version } = await request.get('version').json<BackendVersion>()
return version
}
export const fetchProxyProvidersAPI = () => {
const request = useRequest()
return request
.get('providers/proxies')
.json<{ providers: Record<string, ProxyProvider> }>()
}
export const fetchProxiesAPI = () => {
const request = useRequest()
return request.get('proxies').json<{ proxies: Record<string, Proxy> }>()
}
export const updateProxyProviderAPI = (providerName: string) => {
const request = useRequest()
return request.put(`providers/proxies/${providerName}`)
}
export const proxyProviderHealthCheck = (providerName: string) => {
const request = useRequest()
return request.get(`providers/proxies/${providerName}/healthcheck`, {
timeout: 20 * 1000,
})
}
export const selectProxyInGroupAPI = (groupName: string, proxyName: string) => {
const request = useRequest()
return request.put(`proxies/${groupName}`, {
body: JSON.stringify({
name: proxyName,
}),
})
}
export const proxyGroupLatencyTestAPI = (
groupName: string,
url: string,
timeout: number,
) => {
const request = useRequest()
return request
.get(`group/${groupName}/delay`, {
searchParams: {
url,
timeout,
},
})
.json<Record<string, number>>()
}
export const fetchRulesAPI = () => {
const request = useRequest()
return request.get('rules').json<{ rules: Record<string, Rule> }>()
}
export const fetchRuleProvidersAPI = () => {
const request = useRequest()
return request
.get('providers/rules')
.json<{ providers: Record<string, RuleProvider> }>()
}
export const updateRuleProviderAPI = (providerName: string) => {
const request = useRequest()
return request.put(`providers/rules/${providerName}`)
}

View File

@ -11,6 +11,17 @@ import {
onMount, onMount,
} from 'solid-js' } from 'solid-js'
import { z } from 'zod' import { z } from 'zod'
import {
fetchBackendConfigAPI,
fetchBackendVersionAPI,
restartBackendAPI,
restartingBackend,
updateBackendConfigAPI,
updateGEODatabasesAPI,
updatingGEODatabases,
upgradeBackendAPI,
upgradingBackend,
} from '~/apis'
import { Button } from '~/components' import { Button } from '~/components'
import { import {
LANG, LANG,
@ -28,7 +39,6 @@ import {
backendConfig, backendConfig,
favDayTheme, favDayTheme,
favNightTheme, favNightTheme,
fetchBackendConfig,
latencyTestTimeoutDuration, latencyTestTimeoutDuration,
proxiesOrderingType, proxiesOrderingType,
proxiesPreviewType, proxiesPreviewType,
@ -47,12 +57,11 @@ import {
setTwemoji, setTwemoji,
setUrlForLatencyTest, setUrlForLatencyTest,
tableSize, tableSize,
updateBackendConfig,
urlForLatencyTest, urlForLatencyTest,
useRequest, useRequest,
useTwemoji, useTwemoji,
} from '~/signals' } from '~/signals'
import type { BackendVersion, DNSQuery } from '~/types' import type { DNSQuery } from '~/types'
const dnsQueryFormSchema = z.object({ const dnsQueryFormSchema = z.object({
name: z.string(), name: z.string(),
@ -130,7 +139,6 @@ const configFormSchema = z.object({
const ConfigForm = () => { const ConfigForm = () => {
const [t] = useI18n() const [t] = useI18n()
const request = useRequest()
const portsList = [ const portsList = [
{ {
@ -160,46 +168,18 @@ const ConfigForm = () => {
>({ extend: validator({ schema: configFormSchema }) }) >({ extend: validator({ schema: configFormSchema }) })
onMount(async () => { onMount(async () => {
const configs = await fetchBackendConfig() const configs = await fetchBackendConfigAPI()
setBackendConfig(configs) setBackendConfig(configs)
setInitialValues(configs) setInitialValues(configs)
reset() reset()
}) })
const [updatingGEODatabases, setUpdatingGEODatabases] = createSignal(false)
const [upgrading, setUpgrading] = createSignal(false)
const [restarting, setRestarting] = createSignal(false)
const onUpdateGEODatabases = async () => {
setUpdatingGEODatabases(true)
try {
await request.post('configs/geo')
} catch {}
setUpdatingGEODatabases(false)
}
const onUpgrade = async () => {
setUpgrading(true)
try {
await request.post('upgrade')
} catch {}
setUpgrading(false)
}
const onRestart = async () => {
setRestarting(true)
try {
await request.post('restart')
} catch {}
setRestarting(false)
}
return ( return (
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<select <select
class="select select-bordered" class="select select-bordered"
value={backendConfig()?.mode} value={backendConfig()?.mode}
onChange={(e) => updateBackendConfig('mode', e.target.value)} onChange={(e) => updateBackendConfigAPI('mode', e.target.value)}
> >
<option value={MODE_OPTIONS.Global}>{t('global')}</option> <option value={MODE_OPTIONS.Global}>{t('global')}</option>
<option value={MODE_OPTIONS.Rule}>{t('rule')}</option> <option value={MODE_OPTIONS.Rule}>{t('rule')}</option>
@ -226,15 +206,18 @@ const ConfigForm = () => {
</form> </form>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<Button loading={updatingGEODatabases()} onClick={onUpdateGEODatabases}> <Button
loading={updatingGEODatabases()}
onClick={updateGEODatabasesAPI}
>
{t('updateGEODatabases')} {t('updateGEODatabases')}
</Button> </Button>
<Button loading={upgrading()} onClick={onUpgrade}> <Button loading={upgradingBackend()} onClick={upgradeBackendAPI}>
{t('upgradeCore')} {t('upgradeCore')}
</Button> </Button>
<Button loading={restarting()} onClick={onRestart}> <Button loading={restartingBackend()} onClick={restartBackendAPI}>
{t('restartCore')} {t('restartCore')}
</Button> </Button>
</div> </div>
@ -332,12 +315,12 @@ const ConfigForXd = () => {
const checkboxList = [ const checkboxList = [
{ {
label: 'renderInTwoColumns', label: t('renderInTwoColumns'),
value: renderInTwoColumns, value: renderInTwoColumns,
onChange: setRenderInTwoColumns, onChange: setRenderInTwoColumns,
}, },
{ {
label: 'autoSwitchTheme', label: t('autoSwitchTheme'),
value: autoSwitchTheme, value: autoSwitchTheme,
onChange: (value: boolean) => { onChange: (value: boolean) => {
setAutoSwitchTheme(value) setAutoSwitchTheme(value)
@ -346,7 +329,7 @@ const ConfigForXd = () => {
subChild: autoSwitchThemeSubChild, subChild: autoSwitchThemeSubChild,
}, },
{ {
label: 'useTwemoji', label: t('useTwemoji'),
value: useTwemoji, value: useTwemoji,
onChange: setTwemoji, onChange: setTwemoji,
}, },
@ -358,7 +341,7 @@ const ConfigForXd = () => {
{(checkbox) => ( {(checkbox) => (
<> <>
<div class="flex flex-col"> <div class="flex flex-col">
<ConfigTitle>{t(checkbox.label)}</ConfigTitle> <ConfigTitle>{checkbox.label}</ConfigTitle>
<input <input
type="checkbox" type="checkbox"
@ -444,12 +427,10 @@ const ConfigForXd = () => {
} }
const Versions = () => { const Versions = () => {
const request = useRequest()
const [backendVersion, setBackendVersion] = createSignal('') const [backendVersion, setBackendVersion] = createSignal('')
onMount(async () => { onMount(async () => {
const { version } = await request.get('version').json<BackendVersion>() const version = await fetchBackendVersionAPI()
setBackendVersion(version) setBackendVersion(version)
}) })

View File

@ -30,6 +30,7 @@ import byteSize from 'byte-size'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { For, Index, createMemo, createSignal } from 'solid-js' import { For, Index, createMemo, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { closeAllConnectionsAPI, closeSingleConnectionAPI } from '~/apis'
import { import {
Button, Button,
ConnectionsTableDetailsModal, ConnectionsTableDetailsModal,
@ -41,12 +42,7 @@ import {
CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY, CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY,
} from '~/constants' } from '~/constants'
import { formatTimeFromNow } from '~/helpers' import { formatTimeFromNow } from '~/helpers'
import { import { tableSize, tableSizeClassName, useConnections } from '~/signals'
tableSize,
tableSizeClassName,
useConnections,
useRequest,
} from '~/signals'
import type { Connection } from '~/types' import type { Connection } from '~/types'
type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>> type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>>
@ -72,14 +68,10 @@ const fuzzyFilter: FilterFn<Connection> = (row, columnId, value, addMeta) => {
export default () => { export default () => {
const [t] = useI18n() const [t] = useI18n()
const request = useRequest()
const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections) const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections)
const { activeConnections, closedConnections, paused, setPaused } = const { activeConnections, closedConnections, paused, setPaused } =
useConnections() useConnections()
const onAllConnectionsClose = () => request.delete('connections')
const onSingleConnectionClose = (id: string) =>
request.delete(`connections/${id}`)
const [globalFilter, setGlobalFilter] = createSignal('') const [globalFilter, setGlobalFilter] = createSignal('')
const [columnVisibility, setColumnVisibility] = makePersisted( const [columnVisibility, setColumnVisibility] = makePersisted(
@ -137,7 +129,7 @@ export default () => {
<div class="flex h-4 items-center"> <div class="flex h-4 items-center">
<Button <Button
class="btn-circle btn-xs" class="btn-circle btn-xs"
onClick={() => onSingleConnectionClose(row.original.id)} onClick={() => closeSingleConnectionAPI(row.original.id)}
> >
<IconCircleX size="16" /> <IconCircleX size="16" />
</Button> </Button>
@ -352,9 +344,9 @@ export default () => {
if (table.getState().globalFilter) { if (table.getState().globalFilter) {
table table
.getFilteredRowModel() .getFilteredRowModel()
.rows.forEach(({ id }) => onSingleConnectionClose(id)) .rows.forEach(({ id }) => closeSingleConnectionAPI(id))
} else { } else {
onAllConnectionsClose() closeAllConnectionsAPI()
} }
}} }}
> >

View File

@ -1,6 +1,5 @@
import { makePersisted } from '@solid-primitives/storage' import { makePersisted } from '@solid-primitives/storage'
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { toast } from 'solid-toast'
import { import {
LATENCY_QUALITY_MAP_HTTP, LATENCY_QUALITY_MAP_HTTP,
LATENCY_QUALITY_MAP_HTTPS, LATENCY_QUALITY_MAP_HTTPS,
@ -8,7 +7,7 @@ import {
PROXIES_PREVIEW_TYPE, PROXIES_PREVIEW_TYPE,
TAILWINDCSS_SIZE, TAILWINDCSS_SIZE,
} from '~/constants' } from '~/constants'
import { setCurTheme, useRequest } from '~/signals' import { setCurTheme } from '~/signals'
import { Config } from '~/types' import { Config } from '~/types'
export const [proxiesPreviewType, setProxiesPreviewType] = makePersisted( export const [proxiesPreviewType, setProxiesPreviewType] = makePersisted(
@ -107,32 +106,3 @@ export const useAutoSwitchTheme = () => {
} }
export const [backendConfig, setBackendConfig] = createSignal<Config>() export const [backendConfig, setBackendConfig] = createSignal<Config>()
export const fetchBackendConfig = () => {
const request = useRequest()
return request.get('configs').json<Config>()
}
export const updateBackendConfig = async (
key: keyof Config,
value: Config[keyof Config],
) => {
try {
const request = useRequest()
await request
.patch('configs', {
body: JSON.stringify({
[key]: value,
}),
})
.json<Config>()
const updatedConfig = await fetchBackendConfig()
setBackendConfig(updatedConfig)
} catch (err) {
toast.error((err as Error).message)
}
}

View File

@ -1,16 +1,22 @@
import { createSignal, untrack } from 'solid-js' import { createSignal, untrack } from 'solid-js'
import {
closeSingleConnectionAPI,
fetchProxiesAPI,
fetchProxyProvidersAPI,
proxyGroupLatencyTestAPI,
proxyProviderHealthCheck,
selectProxyInGroupAPI,
updateProxyProviderAPI,
} from '~/apis'
import { import {
autoCloseConns, autoCloseConns,
latencyTestTimeoutDuration, latencyTestTimeoutDuration,
urlForLatencyTest,
useRequest,
} from '~/signals'
import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
import {
latestConnectionMsg, latestConnectionMsg,
mergeAllConnections, mergeAllConnections,
restructRawMsgToConnection, restructRawMsgToConnection,
} from './connections' urlForLatencyTest,
} from '~/signals'
import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
type ProxyInfo = { type ProxyInfo = {
name: string name: string
@ -50,14 +56,10 @@ const setProxiesInfo = (proxies: (Proxy | ProxyNode)[]) => {
} }
export const useProxies = () => { export const useProxies = () => {
const request = useRequest()
const updateProxies = async () => { const updateProxies = async () => {
const [{ providers }, { proxies }] = await Promise.all([ const [{ providers }, { proxies }] = await Promise.all([
request fetchProxyProvidersAPI(),
.get('providers/proxies') fetchProxiesAPI(),
.json<{ providers: Record<string, ProxyProvider> }>(),
request.get('proxies').json<{ proxies: Record<string, Proxy> }>(),
]) ])
const sortIndex = [...(proxies['GLOBAL'].all ?? []), 'GLOBAL'] const sortIndex = [...(proxies['GLOBAL'].all ?? []), 'GLOBAL']
@ -85,15 +87,11 @@ export const useProxies = () => {
const proxyGroupList = proxies().slice() const proxyGroupList = proxies().slice()
const proxyGroup = proxyGroupList.find((i) => i.name === proxy.name)! const proxyGroup = proxyGroupList.find((i) => i.name === proxy.name)!
await request.put(`proxies/${proxy.name}`, { await selectProxyInGroupAPI(proxy.name, proxyName)
body: JSON.stringify({
name: proxyName,
}),
})
if (autoCloseConns()) { if (autoCloseConns()) {
// we dont use activeConns from useConnection here for better performance // we don't use activeConns from useConnection here for better performance,
// and we use empty array to restruct msg because they are closed and they won't have speed anyway // and we use empty array to restruct msg because they are closed, they won't have speed anyway
untrack(() => { untrack(() => {
const activeConns = restructRawMsgToConnection( const activeConns = restructRawMsgToConnection(
latestConnectionMsg()?.connections ?? [], latestConnectionMsg()?.connections ?? [],
@ -103,7 +101,7 @@ export const useProxies = () => {
if (activeConns.length > 0) { if (activeConns.length > 0) {
activeConns.forEach(({ id, chains }) => { activeConns.forEach(({ id, chains }) => {
if (chains.includes(proxy.name)) { if (chains.includes(proxy.name)) {
request.delete(`connections/${id}`) closeSingleConnectionAPI(id)
} }
}) })
mergeAllConnections(activeConns) mergeAllConnections(activeConns)
@ -116,14 +114,11 @@ export const useProxies = () => {
} }
const latencyTestByProxyGroupName = async (proxyGroupName: string) => { const latencyTestByProxyGroupName = async (proxyGroupName: string) => {
const data: Record<string, number> = await request const data = await proxyGroupLatencyTestAPI(
.get(`group/${proxyGroupName}/delay`, { proxyGroupName,
searchParams: { urlForLatencyTest(),
url: urlForLatencyTest(), latencyTestTimeoutDuration(),
timeout: latencyTestTimeoutDuration(), )
},
})
.json()
setLatencyMap({ setLatencyMap({
...latencyMap(), ...latencyMap(),
@ -131,26 +126,22 @@ export const useProxies = () => {
}) })
} }
const updateProviderByProviderName = async (proxyProviderName: string) => { const updateProviderByProviderName = async (providerName: string) => {
try { try {
await request.put(`providers/proxies/${proxyProviderName}`) await updateProxyProviderAPI(providerName)
} catch {} } catch {}
await updateProxies() await updateProxies()
} }
const updateAllProvider = async () => { const updateAllProvider = async () => {
await Promise.allSettled( await Promise.allSettled(
proxyProviders().map((provider) => proxyProviders().map((provider) => updateProxyProviderAPI(provider.name)),
request.put(`providers/proxies/${provider.name}`),
),
) )
await updateProxies() await updateProxies()
} }
const healthCheckByProviderName = async (providerName: string) => { const healthCheckByProviderName = async (providerName: string) => {
await request.get(`providers/proxies/${providerName}/healthcheck`, { await proxyProviderHealthCheck(providerName)
timeout: 20 * 1000,
})
await updateProxies() await updateProxies()
} }

View File

@ -1,18 +1,19 @@
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { useRequest } from '~/signals' import {
fetchRuleProvidersAPI,
fetchRulesAPI,
updateRuleProviderAPI,
} from '~/apis'
import type { Rule, RuleProvider } from '~/types' import type { Rule, RuleProvider } from '~/types'
export const useRules = () => { export const useRules = () => {
const request = useRequest()
const [rules, setRules] = createSignal<Rule[]>([]) const [rules, setRules] = createSignal<Rule[]>([])
const [ruleProviders, setRuleProviders] = createSignal<RuleProvider[]>([]) const [ruleProviders, setRuleProviders] = createSignal<RuleProvider[]>([])
const updateRules = async () => { const updateRules = async () => {
const [{ rules }, { providers }] = await Promise.all([ const [{ rules }, { providers }] = await Promise.all([
request.get('rules').json<{ rules: Record<string, Rule> }>(), fetchRulesAPI(),
request fetchRuleProvidersAPI(),
.get('providers/rules')
.json<{ providers: Record<string, RuleProvider> }>(),
]) ])
setRules(Object.values(rules)) setRules(Object.values(rules))
@ -21,15 +22,13 @@ export const useRules = () => {
const updateAllRuleProvider = async () => { const updateAllRuleProvider = async () => {
await Promise.all( await Promise.all(
ruleProviders().map((provider) => { ruleProviders().map((provider) => updateRuleProviderAPI(provider.name)),
return request.put(`providers/rules/${provider.name}`)
}),
) )
await updateRules() await updateRules()
} }
const updateRuleProviderByName = async (name: string) => { const updateRuleProviderByName = async (providerName: string) => {
await request.put(`providers/rules/${name}`) await updateRuleProviderAPI(providerName)
await updateRules() await updateRules()
} }