diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..d058846e --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,28 @@ +name: Build and Deploy + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - uses: pnpm/action-setup@v4 + + - name: Install and Build 🔧 + run: | + pnpm i + pnpm run build + + - name: Deploy 🚀 + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./dist \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json index 74d529ba..0f9f7db4 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,9 @@ { + "plugins": [ + "prettier-plugin-organize-imports", + "prettier-plugin-tailwindcss" + ], "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleAttributePerLine": true, diff --git a/package.json b/package.json index a1a3fb90..458b5b5a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "name": "sing-box-dashboard", "version": "1.0.0", - "private": true, "type": "module", "scripts": { "dev": "vite", @@ -13,11 +12,14 @@ "format": "prettier --write src/" }, "dependencies": { + "@eslint/plugin-kit": "^0.2.3", "@heroicons/vue": "^2.1.5", "@vueuse/core": "^11.2.0", "axios": "^1.7.7", "dayjs": "^1.11.13", "lodash": "^4.17.21", + "prettier-plugin-organize-imports": "^4.1.0", + "prettier-plugin-tailwindcss": "^0.6.9", "pretty-bytes": "^6.1.1", "theme-change": "^2.5.0", "uuid": "^11.0.3", @@ -48,5 +50,6 @@ "vite": "^5.4.10", "vite-plugin-vue-devtools": "^7.5.4", "vue-tsc": "^2.1.10" - } + }, + "packageManager": "pnpm@9.12.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5f4950a..f0e0a296 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@eslint/plugin-kit': + specifier: ^0.2.3 + version: 0.2.3 '@heroicons/vue': specifier: ^2.1.5 version: 2.1.5(vue@3.5.12(typescript@5.6.3)) @@ -23,6 +26,12 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + prettier-plugin-organize-imports: + specifier: ^4.1.0 + version: 4.1.0(prettier@3.3.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)) + prettier-plugin-tailwindcss: + specifier: ^0.6.9 + version: 0.6.9(prettier-plugin-organize-imports@4.1.0(prettier@3.3.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)))(prettier@3.3.3) pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -426,8 +435,8 @@ packages: resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.2': - resolution: {integrity: sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==} + '@eslint/plugin-kit@0.2.3': + resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@heroicons/vue@2.1.5': @@ -1633,6 +1642,71 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==, tarball: https://r2.cnpmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz} engines: {node: '>=6.0.0'} + prettier-plugin-organize-imports@4.1.0: + resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==} + peerDependencies: + prettier: '>=2.0' + typescript: '>=2.9' + vue-tsc: ^2.1.0 + peerDependenciesMeta: + vue-tsc: + optional: true + + prettier-plugin-tailwindcss@0.6.9: + resolution: {integrity: sha512-r0i3uhaZAXYP0At5xGfJH876W3HHGHDp+LCRUJrs57PBeQ6mYHMwr25KH8NPX44F2yGTvdnH7OqCshlQx183Eg==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig-melody': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig-melody': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier@3.3.3: resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} @@ -2314,7 +2388,7 @@ snapshots: '@eslint/object-schema@2.1.4': {} - '@eslint/plugin-kit@0.2.2': + '@eslint/plugin-kit@0.2.3': dependencies: levn: 0.4.1 @@ -3023,7 +3097,7 @@ snapshots: '@eslint/core': 0.7.0 '@eslint/eslintrc': 3.1.0 '@eslint/js': 9.14.0 - '@eslint/plugin-kit': 0.2.2 + '@eslint/plugin-kit': 0.2.3 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.1 @@ -3508,6 +3582,19 @@ snapshots: dependencies: fast-diff: 1.3.0 + prettier-plugin-organize-imports@4.1.0(prettier@3.3.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)): + dependencies: + prettier: 3.3.3 + typescript: 5.6.3 + optionalDependencies: + vue-tsc: 2.1.10(typescript@5.6.3) + + prettier-plugin-tailwindcss@0.6.9(prettier-plugin-organize-imports@4.1.0(prettier@3.3.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)))(prettier@3.3.3): + dependencies: + prettier: 3.3.3 + optionalDependencies: + prettier-plugin-organize-imports: 4.1.0(prettier@3.3.3)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)) + prettier@3.3.3: {} pretty-bytes@6.1.1: {} diff --git a/src/App.vue b/src/App.vue index 677b7e32..fe8b4d10 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,12 +1,15 @@ \ No newline at end of file + diff --git a/src/components/ProxyNodeCard.vue b/src/components/ProxyNodeCard.vue index 17a047d9..fe4434b5 100644 --- a/src/components/ProxyNodeCard.vue +++ b/src/components/ProxyNodeCard.vue @@ -1,24 +1,32 @@ diff --git a/src/components/sidebar/CommonCtrl.vue b/src/components/sidebar/CommonCtrl.vue index 43a94599..ff2e5987 100644 --- a/src/components/sidebar/CommonCtrl.vue +++ b/src/components/sidebar/CommonCtrl.vue @@ -1,42 +1,104 @@ \ No newline at end of file + diff --git a/src/components/sidebar/ConnectionCtrl.vue b/src/components/sidebar/ConnectionCtrl.vue index 97314378..63275ef4 100644 --- a/src/components/sidebar/ConnectionCtrl.vue +++ b/src/components/sidebar/ConnectionCtrl.vue @@ -1,43 +1,96 @@ \ No newline at end of file + diff --git a/src/components/sidebar/LogsCtrl.vue b/src/components/sidebar/LogsCtrl.vue index 9dd61278..8186b312 100644 --- a/src/components/sidebar/LogsCtrl.vue +++ b/src/components/sidebar/LogsCtrl.vue @@ -1,15 +1,41 @@ \ No newline at end of file +import { initLogs, isPaused, LOG_LEVEL, logFilter, logLevel } from '@/store/logs' +import { PauseIcon, PlayIcon } from '@heroicons/vue/24/outline' + diff --git a/src/components/sidebar/ProxiesCtrl.vue b/src/components/sidebar/ProxiesCtrl.vue index 2d672444..87f0490c 100644 --- a/src/components/sidebar/ProxiesCtrl.vue +++ b/src/components/sidebar/ProxiesCtrl.vue @@ -1,21 +1,37 @@ \ No newline at end of file + diff --git a/src/helper/index.ts b/src/helper/index.ts index a9cd4da3..3753f6ee 100644 --- a/src/helper/index.ts +++ b/src/helper/index.ts @@ -1,5 +1,5 @@ -import { useWindowSize } from "@vueuse/core" -import { computed } from "vue" +import { useWindowSize } from '@vueuse/core' +import { computed } from 'vue' const windowSize = useWindowSize() diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 81359b15..bf60efa8 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -25,4 +25,7 @@ export default { sortBy: 'Sort By', rule: 'Rule', sourceIP: 'Source IP', + activeConnections: 'Active Connections', + closedConnections: 'Closed Connections', + logLevel: 'Log Level', } diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 91851852..c776b46a 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,12 +1,12 @@ +import { LANG, language } from '@/store/config' import { createI18n } from 'vue-i18n' import en from './en' import zh from './zh' -import { LANG, language } from '@/store/config' export const i18n = createI18n({ locale: language.value, messages: { [LANG.EN_US]: en, - [LANG.ZH_CN]: zh - } -}) \ No newline at end of file + [LANG.ZH_CN]: zh, + }, +}) diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index 585210f9..7834b17e 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -25,4 +25,7 @@ export default { sortBy: '排序方式', rule: '规则', sourceIP: '源IP', + activeConnections: '活跃连接', + closedConnections: '已关闭连接', + logLevel: '日志等级', } diff --git a/src/main.ts b/src/main.ts index 127864e4..ef6830f5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,11 @@ -import './assets/main.css' +import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' +import relativeTime from 'dayjs/plugin/relativeTime' import { createApp } from 'vue' import App from './App.vue' -import router from './router' -import dayjs from 'dayjs' -import relativeTime from 'dayjs/plugin/relativeTime' +import './assets/main.css' import { i18n } from './i18n' +import router from './router' const app = createApp(App) dayjs.extend(relativeTime) diff --git a/src/router/index.ts b/src/router/index.ts index 1d4a8e28..d4cbeb42 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -35,9 +35,9 @@ const router = createRouter({ component: RulesPage, }, { - path: "/:catchAll(.*)", + path: '/:catchAll(.*)', redirect: ROUTE_NAME.proxies, - } + }, ], }) diff --git a/src/store/config.ts b/src/store/config.ts index 3ce2889b..23bc0f44 100644 --- a/src/store/config.ts +++ b/src/store/config.ts @@ -1,15 +1,18 @@ -import { getConfigsAPI, patchConfigsAPI } from "@/api"; -import { useStorage } from "@vueuse/core"; -import { ref } from "vue"; +import { getConfigsAPI, patchConfigsAPI } from '@/api' +import { useStorage } from '@vueuse/core' +import { ref } from 'vue' export enum LANG { EN_US = 'en-US', - ZH_CN = 'zh-CN' + ZH_CN = 'zh-CN', } +export const theme = useStorage('config/theme', 'default') export const language = useStorage('config/language', LANG.EN_US) -export const isDark = useStorage('config/dark', false) -export const speedtestUrl = useStorage('config/speedtest-url', 'http://www.gstatic.com/generate_204') +export const speedtestUrl = useStorage( + 'config/speedtest-url', + 'http://www.gstatic.com/generate_204', +) export const speedtestTimeout = useStorage('config/speedtest-timeout', 5000) export const compactConnectionCard = useStorage('config/compact-connection-card', false) export const hostLabelMap = useStorage>('config/host-label-map', {}) diff --git a/src/store/connections.ts b/src/store/connections.ts index 9b9c2d8c..9246fab8 100644 --- a/src/store/connections.ts +++ b/src/store/connections.ts @@ -1,17 +1,21 @@ -import { fetchConnectionsAPI } from "@/api"; -import type { Connection, ConnectionRawMessage } from "@/types"; -import { useStorage } from "@vueuse/core"; -import { computed, ref, watch } from "vue"; +import { fetchConnectionsAPI } from '@/api' +import type { Connection, ConnectionRawMessage } from '@/types' +import { useStorage } from '@vueuse/core' +import { differenceWith } from 'lodash' +import { computed, ref, watch } from 'vue' export const activeConnections = ref([]) +export const closedConnections = ref([]) + export const downloadTotal = ref(0) export const uploadTotal = ref(0) export const memory = ref(0) export const quickFilterRegex = useStorage('config/quick-filter-regex', 'dns|direct') export const quickFilterEnabled = useStorage('config/quick-filter-enabled', false) +export const showActiveConnections = ref(true) -export enum sortType { +export enum SORT_TYPE { HOST = 'host', RULE = 'rule', CHAINS = 'chains', @@ -22,61 +26,74 @@ export enum sortType { SOURCE_IP = 'sourceIP', } -const sortFunctionMap: Record number> = { - [sortType.HOST]: (a: Connection, b: Connection) => { - return (a.metadata.host || a.metadata.destinationIP).localeCompare((b.metadata.host || b.metadata.destinationIP)) +const sortFunctionMap: Record number> = { + [SORT_TYPE.HOST]: (a: Connection, b: Connection) => { + return (a.metadata.host || a.metadata.destinationIP).localeCompare( + b.metadata.host || b.metadata.destinationIP, + ) }, - [sortType.RULE]: (a: Connection, b: Connection) => { + [SORT_TYPE.RULE]: (a: Connection, b: Connection) => { return a.rule.localeCompare(b.rule) }, - [sortType.CHAINS]: (a: Connection, b: Connection) => { + [SORT_TYPE.CHAINS]: (a: Connection, b: Connection) => { return a.chains.join('').localeCompare(b.chains.join('')) }, - [sortType.DOWNLOAD]: (a: Connection, b: Connection) => { + [SORT_TYPE.DOWNLOAD]: (a: Connection, b: Connection) => { return b.download - a.download }, - [sortType.DOWNLOAD_SPEED]: (a: Connection, b: Connection) => { + [SORT_TYPE.DOWNLOAD_SPEED]: (a: Connection, b: Connection) => { return b.downloadSpeed - a.downloadSpeed }, - [sortType.UPLOAD]: (a: Connection, b: Connection) => { + [SORT_TYPE.UPLOAD]: (a: Connection, b: Connection) => { return b.upload - a.upload }, - [sortType.UPLOAD_SPEED]: (a: Connection, b: Connection) => { + [SORT_TYPE.UPLOAD_SPEED]: (a: Connection, b: Connection) => { return b.uploadSpeed - a.uploadSpeed }, - [sortType.SOURCE_IP]: (a: Connection, b: Connection) => { + [SORT_TYPE.SOURCE_IP]: (a: Connection, b: Connection) => { return a.metadata.sourceIP.localeCompare(b.metadata.sourceIP) }, } -export const connectionSortType = useStorage('config/connection-sort-type', sortType.HOST) +export const connectionSortType = useStorage( + 'config/connection-sort-type', + SORT_TYPE.HOST, +) export const connectionFilter = ref('') export const isPaused = ref(false) export const renderConnections = computed(() => { - return activeConnections.value.filter((conn) => { - if (quickFilterEnabled.value && quickFilterRegex.value) { - const regex = new RegExp(quickFilterRegex.value) - - return !regex.test(conn.chains.join('')) - } + return (showActiveConnections.value ? activeConnections.value : closedConnections.value) + .filter((conn) => { + if (quickFilterEnabled.value && quickFilterRegex.value) { + const regex = new RegExp(quickFilterRegex.value) + + return !( + regex.test(conn.chains.join('')) || + regex.test(conn.metadata.host) || + regex.test(conn.metadata.destinationIP) + ) + } - return true - }).filter(conn => { - if (connectionFilter.value) { - return [ - conn.metadata.host, - conn.metadata.destinationIP, - conn.metadata.destinationPort, - conn.chains, - conn.rule, - ].some(i => i?.includes(connectionFilter.value)) - } + return true + }) + .filter((conn) => { + if (connectionFilter.value) { + return [ + conn.metadata.host, + conn.metadata.destinationIP, + conn.metadata.destinationPort, + conn.chains, + conn.rule, + ].some((i) => i?.includes(connectionFilter.value)) + } - return true - }).sort((a, b) => { - return a.id.localeCompare(b.id) - }).sort(sortFunctionMap[connectionSortType.value]) + return true + }) + .sort((a, b) => { + return a.id.localeCompare(b.id) + }) + .sort(sortFunctionMap[connectionSortType.value]) }) let cancel: () => void @@ -84,7 +101,7 @@ let cancel: () => void export const initConnections = () => { cancel?.() activeConnections.value = [] - + const ws = fetchConnectionsAPI() const unwatch = watch(ws.data, (data) => { if (!data) return @@ -103,19 +120,28 @@ export const initConnections = () => { if (isPaused.value) { return } - activeConnections.value = parsedData.connections?.map((connection) => { - const preConnection = activeConnections.value.find((c) => c.id === connection.id) ?? { download: 0, upload: 0 } - return { - ...connection, - downloadSpeed: connection.download - preConnection.download, - uploadSpeed: connection.upload - preConnection.upload - } - }) ?? [] + closedConnections.value = [ + ...closedConnections.value, + ...differenceWith(activeConnections.value, parsedData.connections, (a, b) => a.id === b.id), + ] + activeConnections.value = + parsedData.connections?.map((connection) => { + const preConnection = activeConnections.value.find((c) => c.id === connection.id) ?? { + download: 0, + upload: 0, + } + + return { + ...connection, + downloadSpeed: connection.download - preConnection.download, + uploadSpeed: connection.upload - preConnection.upload, + } + }) ?? [] }) cancel = () => { unwatch() ws.close() } -} \ No newline at end of file +} diff --git a/src/store/logs.ts b/src/store/logs.ts index 1cdb670a..0f74bb48 100644 --- a/src/store/logs.ts +++ b/src/store/logs.ts @@ -1,10 +1,20 @@ -import { fetchLogsAPI } from "@/api"; -import type { Log, LogWithSeq } from "@/types"; -import { ref, watch } from "vue"; +import { fetchLogsAPI } from '@/api' +import type { Log, LogWithSeq } from '@/types' +import { useStorage } from '@vueuse/core' +import { ref, watch } from 'vue' + +export enum LOG_LEVEL { + Info = 'info', + Error = 'error', + Warning = 'warning', + Debug = 'debug', + Silent = 'silent', +} export const logs = ref([]) export const logFilter = ref('') export const isPaused = ref(false) +export const logLevel = useStorage('config/log-level', LOG_LEVEL.Info) let cancel: () => void @@ -13,7 +23,9 @@ export const initLogs = () => { logs.value = [] let idx = 1 - const ws = fetchLogsAPI() + const ws = fetchLogsAPI({ + level: logLevel.value, + }) const unwatch = watch(ws.data, (data) => { if (!data) return @@ -26,7 +38,7 @@ export const initLogs = () => { logs.value.unshift({ ...parsedData, - seq: idx++ + seq: idx++, }) logs.value = logs.value.slice(0, 1000) @@ -36,4 +48,4 @@ export const initLogs = () => { unwatch() ws.close() } -} \ No newline at end of file +} diff --git a/src/store/proxies.ts b/src/store/proxies.ts index 34b5384c..ac65b342 100644 --- a/src/store/proxies.ts +++ b/src/store/proxies.ts @@ -1,9 +1,15 @@ -import { disconnectByIdAPI, fetchProxiesAPI, fetchProxyGroupLatencyAPI, fetchProxyLatencyAPI, selectProxyAPI } from "@/api"; -import type { Proxy, ProxyGroup } from "@/types"; -import { ref } from "vue"; +import { + disconnectByIdAPI, + fetchProxiesAPI, + fetchProxyGroupLatencyAPI, + fetchProxyLatencyAPI, + selectProxyAPI, +} from '@/api' +import type { Proxy, ProxyGroup } from '@/types' import { last } from 'lodash' -import { activeConnections } from "./connections"; -import { speedtestTimeout, speedtestUrl } from "./config"; +import { ref } from 'vue' +import { speedtestTimeout, speedtestUrl } from './config' +import { activeConnections } from './connections' export const proxyGroups = ref([]) export const proxyMap = ref>({}) @@ -16,14 +22,18 @@ export const getLatencyByName = (proxyName: string) => { export const fetchProxies = async () => { const { data } = await fetchProxiesAPI() const sortIndex = data.proxies['GLOBAL'].all ?? [] - const proxies = Object.values(data.proxies).filter((proxy) => proxy.all?.length && proxy.name !== 'GLOBAL') as ProxyGroup[] + const proxies = Object.values(data.proxies).filter( + (proxy) => proxy.all?.length && proxy.name !== 'GLOBAL', + ) as ProxyGroup[] proxyMap.value = data.proxies - proxyGroups.value = proxies.sort((prev, next) => - sortIndex.indexOf(prev.name) - sortIndex.indexOf(next.name)) + proxyGroups.value = proxies.sort( + (prev, next) => sortIndex.indexOf(prev.name) - sortIndex.indexOf(next.name), + ) latencyMap.value = Object.fromEntries( - Object.entries(data.proxies).map(([name, proxy]) => [name, getLatencyFromHistory(proxy)])) + Object.entries(data.proxies).map(([name, proxy]) => [name, getLatencyFromHistory(proxy)]), + ) } export const selectProxy = async (proxyGroup: string, name: string) => { @@ -35,13 +45,21 @@ export const selectProxy = async (proxyGroup: string, name: string) => { } export const proxyLatencyTest = async (proxyName: string) => { - const { data: latencyResult } = await fetchProxyLatencyAPI(proxyName, speedtestUrl.value, speedtestTimeout.value) + const { data: latencyResult } = await fetchProxyLatencyAPI( + proxyName, + speedtestUrl.value, + speedtestTimeout.value, + ) latencyMap.value[getNowProxyNodeName(proxyName)] = latencyResult.delay } export const proxyGroupLatencyTest = async (proxyGroupName: string) => { - const { data: latencyResult } = await fetchProxyGroupLatencyAPI(proxyGroupName, speedtestUrl.value, speedtestTimeout.value) + const { data: latencyResult } = await fetchProxyGroupLatencyAPI( + proxyGroupName, + speedtestUrl.value, + speedtestTimeout.value, + ) Object.entries(latencyResult).forEach(([name, latency]) => { latencyMap.value[getNowProxyNodeName(name)] = latency diff --git a/src/store/rules.ts b/src/store/rules.ts index 95204c5b..f94c139b 100644 --- a/src/store/rules.ts +++ b/src/store/rules.ts @@ -1,6 +1,6 @@ -import { getRulesAPI } from "@/api"; -import type { Rule } from "@/types"; -import { ref } from "vue"; +import { getRulesAPI } from '@/api' +import type { Rule } from '@/types' +import { ref } from 'vue' export const rules = ref() export const fetchRules = async () => { diff --git a/src/store/setup.ts b/src/store/setup.ts index 10e5f34b..4fba7510 100644 --- a/src/store/setup.ts +++ b/src/store/setup.ts @@ -1,19 +1,21 @@ import { useStorage } from '@vueuse/core' -import { computed } from 'vue' -import { v4 as uuid } from 'uuid' import { isEqual, omit } from 'lodash' +import { v4 as uuid } from 'uuid' +import { computed } from 'vue' type Backend = { - host: string, - port: number, - password: string, - protocol: string, + host: string + port: number + password: string + protocol: string uuid: string } export const backendList = useStorage('setup/api-list', []) export const activeUuid = useStorage('setup/active-uuid', '') -export const activeBackend = computed(() => backendList.value.find((backend) => backend.uuid === activeUuid.value)) +export const activeBackend = computed(() => + backendList.value.find((backend) => backend.uuid === activeUuid.value), +) export const addBackend = (backend: Omit) => { const currentEnd = backendList.value.find((end) => { @@ -29,7 +31,7 @@ export const addBackend = (backend: Omit) => { backendList.value.push({ ...backend, - uuid: id + uuid: id, }) activeUuid.value = id -} \ No newline at end of file +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 2702c3d1..9f9ebdcd 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -1,8 +1,6 @@ -declare module '*.vue' { -} +declare module '*.vue' {} -declare module '*.tsx' { -} +declare module '*.tsx' {} export type Proxy = { name: string @@ -53,7 +51,7 @@ export type Connection = ConnectionRawMessage & { } export type Log = { - type: LOG_LEVEL + type: 'info' | 'warning' | 'error' | 'debug' payload: string } diff --git a/src/views/ConnectionsPage.vue b/src/views/ConnectionsPage.vue index a819af5b..1f3b1d61 100644 --- a/src/views/ConnectionsPage.vue +++ b/src/views/ConnectionsPage.vue @@ -1,18 +1,29 @@ \ No newline at end of file + diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue index ae1f1563..a8097cb8 100644 --- a/src/views/HomePage.vue +++ b/src/views/HomePage.vue @@ -1,57 +1,93 @@ \ No newline at end of file +watch( + activeUuid, + () => { + fetchConfigs() + fetchProxies() + fetchRules() + initConnections() + initLogs() + }, + { + immediate: true, + }, +) + diff --git a/src/views/LogsPage.vue b/src/views/LogsPage.vue index bb136284..a4098d7d 100644 --- a/src/views/LogsPage.vue +++ b/src/views/LogsPage.vue @@ -1,25 +1,32 @@