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

View File

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

View File

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

View File

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