Skip to content

Commit

Permalink
feat(desktop): 添加规则测速功能
Browse files Browse the repository at this point in the history
  • Loading branch information
aooiuu committed May 28, 2024
1 parent d93cd00 commit d6d885d
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 14 deletions.
9 changes: 9 additions & 0 deletions packages/shared/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { favoritesManager } from './favoritesManager'
import { historyManager } from './historyManager'
import type { BookChapter } from './localBookManager'
import localBookManager from './localBookManager'
import * as ruleExtraManager from './ruleExtraManager'

// 初始化
export async function init() {
Expand Down Expand Up @@ -196,3 +197,11 @@ export async function updateConfig(filePath: string, data: any) {
await ensureFile(filePath)
writeJson(filePath, data, { spaces: 2 })
}

export function getRuleExtras() {
return ruleExtraManager.getRuleExtras()
}

export function ping(data: { id: string; host: string }) {
return ruleExtraManager.ping(data.id, data.host)
}
2 changes: 2 additions & 0 deletions packages/shared/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const HISTORY_PATH = path.join(ROOT_PATH, 'history.json')
export const FAVORITES_PATH = path.join(ROOT_PATH, 'favorites.json')
// 本地文件目录
export const LOCAL_BOOK_DIR = path.join(ROOT_PATH, 'local-book')
// 规则扩展数据
export const RULE_EXTRA_PATH = path.join(ROOT_PATH, 'source.extra.json')
36 changes: 36 additions & 0 deletions packages/shared/src/ping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import http from 'node:http'
import https from 'node:https'

/**
* ping
* @param {string} url 地址
* @param {number} port 端口
* @returns {Promise<number>} -1=失败
*/
export default function ping(url: string, port?: number): Promise<number> {
const promise = new Promise<number>((resolve) => {
if (!url || !url.startsWith('http'))
return -1
const isHttps = url.startsWith('https')
const send = isHttps ? https.request : http.request
const outPort = port || (isHttps ? 443 : 80)
const baseUrl = url.replace('http://', '').replace('https://', '')

const options = { host: baseUrl, port: outPort, path: '/' }
const start = Date.now()

const req = send(options, () => {
resolve(Date.now() - start)
req.destroy()
})

req.on('error', () => {
req.destroy()
resolve(-1)
})

req.write('')
req.end()
})
return promise
}
49 changes: 49 additions & 0 deletions packages/shared/src/ruleExtraManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// @ts-expect-error
import { readJson } from 'fs-extra/esm'
import debounce from 'lodash-es/debounce'
import { JSONFilePreset } from 'lowdb/node'
import type { Low } from 'lowdb/lib'
import { RULE_EXTRA_PATH } from './constants'
import _ping from './ping'

export interface SourceExtraRow {
ping?: number
}

export interface SourceExtra {
[_: string]: SourceExtraRow
}

let mDb: Low<SourceExtra>

async function getDb() {
if (mDb)
return mDb
const data = await readJson(RULE_EXTRA_PATH).catch(() => ({}))
mDb = await JSONFilePreset<SourceExtra>(RULE_EXTRA_PATH, data)
return mDb
}

const writeDB = debounce(async () => {
const db = await getDb()
db.write()
}, 500)

export async function ping(id: string, host: string) {
const db = await getDb()
if (!db.data[id])
db.data[id] = {}
db.data[id].ping = await _ping(host)
writeDB()
return db.data[id]
}

export async function getExtraInfoById(id: string): Promise<SourceExtraRow> {
const db = await getDb()
return db.data[id]
}

export async function getRuleExtras(): Promise<SourceExtra> {
const db = await getDb()
return db.data
}
2 changes: 2 additions & 0 deletions packages/web/electron/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export function createAPI(win: BrowserWindow) {
pm.answer('post@getChapter', async (data: any) => success(await api.getChapter(data)));
pm.answer('get@readConfig', async () => success(getConfig()));
pm.answer('post@updateConfig', async (data: any) => success(await api.updateConfig(CONFIG_PATH, data)));
pm.answer('get@getRuleExtras', async () => success(await api.getRuleExtras()));
pm.answer('post@ping', async (data: any) => success(await api.ping(data)));

pm.answer('get@minimize', () => {
win.minimize();
Expand Down
16 changes: 16 additions & 0 deletions packages/web/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,19 @@ export function updateConfig(data: any) {
data
});
}

// 获取扩展数据
export function getRuleExtras() {
return request({
method: 'get',
url: 'getRuleExtras'
});
}

export function ping(data: any) {
return request({
method: 'post',
url: 'ping',
data
});
}
2 changes: 1 addition & 1 deletion packages/web/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ declare global {
}
}

export {};
export {};
25 changes: 25 additions & 0 deletions packages/web/src/pages/pc/rules/hooks/useRuleExtra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { getRuleExtras } from '@/api';

interface SourceExtraRow {
ping?: number;
}

interface SourceExtra {
[_: string]: SourceExtraRow;
}

export function useRuleExtra() {
const data = ref<SourceExtra>({});

async function sync() {
const res = await getRuleExtras().catch(() => {});
if (res?.code === 0) {
data.value = res?.data || [];
}
}

return {
data,
sync
};
}
104 changes: 91 additions & 13 deletions packages/web/src/pages/pc/rules/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
</a-checkbox>
</a-checkbox-group>
</div>
<a-button type="primary" @click="pingAll">测速</a-button>
<a-button type="primary" @click="addRule">
<template #icon>
<icon-plus />
</template>
添加规则
</a-button>
</div>
<div class="flex-1 overflow-hidden">
Expand All @@ -31,13 +31,20 @@
</template>

<script setup lang="jsx">
import _ from 'lodash';
import { CONTENT_TYPES } from '@/constants';
import { useRulesStore } from '@/stores/rules';
import { updateRule } from '@/api';
import { editBookSource } from '@/api/vsc';
import { updateRule, ping } from '@/api';
import { timeoutWith } from '@/utils/promise';
import { useRuleExtra } from './hooks/useRuleExtra';
const router = useRouter();
const rulesStore = useRulesStore();
const ruleExtra = useRuleExtra();
const pingIds = ref([]);
rulesStore.sync();
ruleExtra.sync();
const searchText = ref('');
const contentTypes = ref(CONTENT_TYPES.map((e) => e.value).flat());
Expand All @@ -51,22 +58,72 @@ function editRule(row) {
});
}
function sortableValue(obj, path) {
return _.get(obj, path) || Number.MAX_SAFE_INTEGER;
}
const tableColumns = ref([
{
title: '名称',
dataIndex: 'name'
dataIndex: 'name',
align: 'center',
width: 100,
sortable: {
sortDirections: ['ascend', 'descend']
}
},
{
title: '域名',
dataIndex: 'host'
dataIndex: 'host',
ellipsis: true,
tooltip: true,
sortable: {
sortDirections: ['ascend', 'descend']
}
},
{
title: 'Ping',
dataIndex: 'extra.ping',
width: 100,
align: 'center',
render: ({ record }) => {
const extra = record.extra;
return (
<a-button
class={extra?.ping === -1 ? '!text-red' : ''}
type="text"
onClick={async () => {
await ping(_.pick(record, ['id', 'host']));
ruleExtra.sync();
}}>
{pingIds.value.includes(record.id) ? '-' : extra?.ping === -1 ? '超时' : extra?.ping || '测速'}
</a-button>
);
},
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: (a, b, { direction }) => {
if (direction === 'ascend') {
return sortableValue(a, 'extra.ping') - sortableValue(b, 'extra.ping');
}
return sortableValue(b, 'extra.ping') - sortableValue(a, 'extra.ping');
}
}
},
{
title: '作者',
dataIndex: 'author'
dataIndex: 'author',
align: 'center',
width: 100,
ellipsis: true,
tooltip: true,
sortable: {
sortDirections: ['ascend', 'descend']
}
},
{
title: '启用搜索',
width: 100,
width: 120,
align: 'center',
render: ({ record }) => (
<a-switch
Expand All @@ -80,11 +137,14 @@ const tableColumns = ref([
})
}
/>
)
),
sortable: {
sortDirections: ['ascend', 'descend']
}
},
{
title: '启用发现',
width: 100,
width: 120,
align: 'center',
render: ({ record }) => (
<a-switch
Expand All @@ -98,11 +158,14 @@ const tableColumns = ref([
})
}
/>
)
),
sortable: {
sortDirections: ['ascend', 'descend']
}
},
{
title: '操作',
width: 100,
width: 120,
align: 'center',
fixed: 'right',
render: ({ record }) => (
Expand All @@ -113,15 +176,30 @@ const tableColumns = ref([
}
]);
const tableData = computed(() => {
const tableDataFilter = computed(() => {
if (!searchText.value) return rulesStore.list.filter((e) => contentTypes.value.includes(e.contentType));
return rulesStore.list.filter((e) => e.name?.includes(searchText.value) && contentTypes.value.includes(e.contentType));
});
const tableData = computed(() => {
return tableDataFilter.value.map((e) => ({
...e,
extra: ruleExtra.data.value[e.id] || {}
}));
});
// 添加规则
function addRule() {
router.push('/pc/rule-info');
}
rulesStore.sync();
async function pingAll() {
const rows = _.chunk(tableDataFilter.value, 5);
for (const row of rows) {
pingIds.value = row.map((e) => e.id);
await timeoutWith(Promise.all(row.map((e) => ping(_.pick(e, ['id', 'host'])))), 3000).catch(() => {});
pingIds.value = [];
await ruleExtra.sync();
}
}
</script>
8 changes: 8 additions & 0 deletions packages/web/src/utils/promise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function timeoutWith(tasks: any, t = 6000) {
const delay = () =>
new Promise((_, reject) => {
setTimeout(reject, t);
});

return Promise.race([tasks, delay()]);
}
1 change: 1 addition & 0 deletions packages/web/src/utils/sleep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default (t: number) => new Promise((c) => setTimeout(c, t));

0 comments on commit d6d885d

Please sign in to comment.