Skip to content

Commit

Permalink
improvement: 改进 surl 和 pwd 识别
Browse files Browse the repository at this point in the history
improvement: 改进控制台图标
improvement: 改进公告显示
improvement: 一个错别字
improvement: 接口拦截器
feat: 控制台 IP 统计
feat: 后台密钥管理
style: 去除无用代码
fix: 修复安装时,Redis 与 MySQL 参数一致
  • Loading branch information
Yingyya committed Jul 29, 2024
1 parent a1717a0 commit 706a621
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 20 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"axios": "^1.7.2",
"copy-to-clipboard": "^3.3.3",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"pinia": "^2.1.7",
Expand Down
7 changes: 6 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {computed} from 'vue';
import {storeToRefs} from 'pinia';
import GithubCorners from '@/components/GithubCorners.vue';
import {router} from '@/router';
import {stringIsEmpty} from '@/utils/string-is-empty';
const message = useMessage(); // 挂载对象
const systemConfigStore = useSystemConfigStore();
Expand All @@ -21,10 +22,14 @@ const visible = computed({
});
// 初始化系统配置
systemConfigStore.init().catch(e => {
systemConfigStore.init().catch(() => {
if (router.currentRoute.value.name !== 'Install') {
message.error('系统错误!');
}
}).then(() => {
if (!stringIsEmpty(systemConfigStore.notice.content) || !stringIsEmpty(systemConfigStore.notice.title)) {
visible.value = true;
}
});
window.addEventListener('beforeunload', (e) => {
Expand Down
3 changes: 2 additions & 1 deletion src/pages/Install.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ const buildInstallQuery = () => {
}
for (let key in redisConfig) {
if (isValidKey(key, redisConfig)) {
query[`redis_${key}`] = mysqlConfig[key] || '';
// 对不起 我的问题
query[`redis_${key}`] = redisConfig[key] || '';
}
}
return query;
Expand Down
117 changes: 117 additions & 0 deletions src/pages/admin/ApiKey.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<script setup lang="ts">
import {onMounted, ref} from 'vue';
import Button from 'primevue/button';
import Card from 'primevue/card';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import copy from 'copy-to-clipboard';
import type {ApiKey} from '@/types';
import {useMessage} from '@/hooks/useMessage';
import {useConfirm} from 'primevue/useconfirm';
import {deleteApiKey, generateApiKey, getApiKeyList} from '@/services/key';
const refreshing = ref(false);
const items = ref<ApiKey[]>([]);
const message = useMessage();
const confirm = useConfirm();
const loading = ref(false);
const onDeleteApiKey = (event: Event, api: ApiKey) => {
confirm.require({
target: event.target as HTMLElement,
message: '您要删除此密钥吗?',
icon: 'pi pi-info-circle',
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
acceptClass: 'p-button-danger p-button-sm',
rejectLabel: '取消',
acceptLabel: '确定',
accept: async () => {
try {
message.default('正在删除……');
(await deleteApiKey(api.id));
message.success('删除成功');
const index = items.value.findIndex(v => v.id === api.id);
items.value.splice(index, 1);
} catch {
message.warn('删除失败!');
}
}
});
};
const refresh = async () => {
loading.value = true;
try {
refreshing.value = true;
const response = await getApiKeyList();
if (response.data.data.length > 0) {
// 清空列表
items.value.length = 0;
items.value.push(...response.data.data);
}
} catch {
message.warn('加载密钥失败!请稍后刷新页面!');
} finally {
refreshing.value = false;
loading.value = false;
}
};
const onCopyKey = (key: ApiKey) => {
copy(key.key || '');
message.success('复制成功!');
};
const newApiKey = async () => {
loading.value = true;
try {
const {data} = (await generateApiKey()).data;
message.success('生成成功!已自动复制。');
copy(data.key);
await refresh();
} catch {
message.warn('生成失败!');
} finally {
loading.value = false;
}
};
onMounted(refresh);
</script>

<template>
<div class="col-12">
<Card>
<template #content>
<DataTable :value="items" dataKey="id"
tableStyle="min-width: 60rem" :loading="refreshing">
<template #loading>
正在加载,请稍后……
</template>
<template #empty>
暂时没有数据,添加一个吧!
</template>
<template #header>
<div class="md:flex md:flex-row md:flex-nowrap">
<Button :loading="loading" icon="pi pi-plus" label="添加" class="mr-2 mb-2 flex-grow-0" @click="newApiKey"/>
</div>
</template>
<Column field="id" header="ID" :sortable="true"/>
<Column field="key" header="KEY"/>
<Column field="use_count" header="使用次数"/>
<Column header="操作">
<template #body="{data}">
<Button icon="pi pi-copy" rounded class="mb-2 mr-2" @click="onCopyKey(data)"/>
<Button icon="pi pi-trash" severity="danger" rounded class="mb-2 mr-2"
@click="onDeleteApiKey($event, data)"/>
</template>
</Column>
</DataTable>
</template>
</Card>
</div>
</template>

<style scoped>
</style>
49 changes: 43 additions & 6 deletions src/pages/admin/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import Panel from 'primevue/panel';
import {getSystemStatus} from '@/services/system';
import {onMounted, ref} from 'vue';
import {useMessage} from '@/hooks/useMessage';
import {getStatisticIPCount} from '@/services/statistic';
const message = useMessage();
const parsedAllCount = ref('……');
const spentVip = ref(0);
const parsedTick = ref(0);
const ipNum = ref(0);
const requestNum = ref(0);
onMounted(() => {
// 延迟一会等待 toast 自动配置 z-index 后
Expand All @@ -18,6 +21,9 @@ onMounted(() => {
parsedAllCount.value = data.total_parsing_traffic_format;
spentVip.value = data.spent_svip_count;
parsedTick.value = data.total_parsing_count;
const {data: ip} = (await getStatisticIPCount()).data;
ipNum.value = ip.ip_type;
requestNum.value = ip.all_count;
message.clear();
} catch {}
}, 888);
Expand All @@ -34,8 +40,9 @@ onMounted(() => {
<span class="block text-500 font-medium mb-3">解析量</span>
<div class="text-900 font-medium text-xl">{{ parsedAllCount }}</div>
</div>
<div class="flex align-items-center justify-content-center bg-blue-100 border-round" style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-shopping-cart text-blue-500 text-xl"></i>
<div class="flex align-items-center justify-content-center bg-blue-100 border-round"
style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-cloud-download text-blue-500 text-xl"></i>
</div>
</div>
</Panel>
Expand All @@ -47,8 +54,9 @@ onMounted(() => {
<span class="block text-500 font-medium mb-3">解析次数</span>
<div class="text-900 font-medium text-xl">{{ parsedTick }}</div>
</div>
<div class="flex align-items-center justify-content-center bg-purple-100 border-round" style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-eye text-purple-500 text-xl"></i>
<div class="flex align-items-center justify-content-center bg-purple-100 border-round"
style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-clock text-purple-500 text-xl"></i>
</div>
</div>
</Panel>
Expand All @@ -60,8 +68,37 @@ onMounted(() => {
<span class="block text-500 font-medium mb-3">已使用的系统账号</span>
<div class="text-900 font-medium text-xl">{{ spentVip }}</div>
</div>
<div class="flex align-items-center justify-content-center bg-green-100 border-round" style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-eye text-green-500 text-xl"></i>
<div class="flex align-items-center justify-content-center bg-green-100 border-round"
style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-user text-green-500 text-xl"></i>
</div>
</div>
</Panel>
</div>
<div class="col-12 lg:col-6 xl:col-3">
<Panel>
<div class="flex justify-content-between mb-3">
<div>
<span class="block text-500 font-medium mb-3">IP数</span>
<div class="text-900 font-medium text-xl">{{ ipNum }}</div>
</div>
<div class="flex align-items-center justify-content-center bg-red-100 border-round"
style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-android text-red-500 text-xl"></i>
</div>
</div>
</Panel>
</div>
<div class="col-12 lg:col-6 xl:col-3">
<Panel>
<div class="flex justify-content-between mb-3">
<div>
<span class="block text-500 font-medium mb-3">总请求数</span>
<div class="text-900 font-medium text-xl">{{ requestNum }}</div>
</div>
<div class="flex align-items-center justify-content-center bg-orange-100 border-round"
style="width: 2.5rem; height: 2.5rem">
<i class="pi pi-book text-orange-500 text-xl"></i>
</div>
</div>
</Panel>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/admin/DiskAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ onMounted(refresh);
<template #body="{ data }">
<p>
<Tag severity="success" v-if="data.show_msg === '可用'">可用</Tag>
<Tag severity="danger" v-else-if="data.show_msg === '过期'">可用</Tag>
<Tag severity="danger" v-else-if="data.show_msg === '过期'">过期</Tag>
<Tag v-else>限速</Tag>
</p>
</template>
Expand All @@ -153,7 +153,7 @@ onMounted(refresh);
</Column>
<Column header="操作" headerStyle="width:4rem">
<template #body="item">
<Button icon="pi pi-pen" severity="info" rounded class="mb-2 mr-2" :loading="loading"
<Button icon="pi pi-pencil" severity="info" rounded class="mb-2 mr-2" :loading="loading"
@click="onChangeAccount(item.data)"/>
<Button icon="pi pi-trash" severity="danger" rounded class="mb-2 mr-2" :loading="loading"
@click="onDeleteVipAccount($event, item.data)"/>
Expand Down
12 changes: 9 additions & 3 deletions src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createRouter, createWebHashHistory} from 'vue-router';
import routes from './routes';
import {useCacheStore, useUserStore} from '@/store';
import {useCacheStore, useSystemConfigStore, useUserStore} from '@/store';
import {stringIsEmpty} from '@/utils/string-is-empty';
import {useMessage} from '@/hooks/useMessage';

Expand All @@ -13,7 +13,6 @@ router.beforeEach((to, from, next) => {
if (to.meta.login) {
const userStore = useUserStore();
const message = useMessage();
console.log(userStore.token);
if (!stringIsEmpty(userStore.token)) {
next();
return;
Expand All @@ -30,6 +29,13 @@ router.beforeEach((to, from, next) => {
router.afterEach((to, from, next) => {
if (to.meta.notice) {
const cacheStore = useCacheStore();
cacheStore.noticeDialog = true;
const systemStore = useSystemConfigStore();
if (systemStore.isInit && (!stringIsEmpty(systemStore.notice.content) || !stringIsEmpty(systemStore.notice.title)))
cacheStore.noticeDialog = true;
}
if (to.meta.title) {
window.document.title = `F4Pan - ${to.meta.title}`;
} else {
window.document.title = `F4Pan`;
}
});
9 changes: 9 additions & 0 deletions src/router/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ export default [{
title: '系统管理',
icon: 'pi pi-cog'
}
},
{
path: 'api-key',
component: () => import('@/pages/admin/ApiKey.vue'),
name: 'ApiKey',
meta: {
title: '密钥管理',
icon: 'pi pi-key'
}
}
]
}, {
Expand Down
27 changes: 26 additions & 1 deletion src/services/key.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import request from '@/utils/request';
import type {GenerateApiKeyResponse} from '@/types';
import type {ApiKeyListResponse, DeleteApiKeyResponse, GenerateApiKeyResponse} from '@/types';

/**
* 生成 API KEY
Expand All @@ -10,3 +10,28 @@ export function generateApiKey() {
withLogin: true
});
}

/**
* 获取 API KEY 列表
*/
export function getApiKeyList() {
return request<ApiKeyListResponse>({
url: '/admin/api_keys',
withLogin: true
});
}

/**
* 删除 API KEY
* @param id
*/
export function deleteApiKey(id: number) {
return request<DeleteApiKeyResponse>({
url: '/admin/api_keys/delete',
method: 'GET',
params: {
id
},
withLogin: true
});
}
5 changes: 4 additions & 1 deletion src/services/statistic.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import request from '@/utils/request';
import type {IPCountResponse} from '@/types';

export function getStatisticIP() {
return request({
url: '/admin/statistics/get_ip',
withLogin: true
});
}

export function getStatisticIPCount() {
return request({
return request<IPCountResponse>({
url: '/admin/statistics/get_ip_count',
withLogin: true
});
}
9 changes: 7 additions & 2 deletions src/store/modules/system-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ interface SystemConfig {
notice_id: number;
parse_ua: string;
notice: Notice;
isInit: boolean;
}

export const useSystemConfigStore = defineStore('system-config-store', {
state(): SystemConfig {
return {
isInit: false,
requires_key: 'dynamic',
notice_id: 0,
parse_ua: '',
Expand All @@ -32,8 +34,11 @@ export const useSystemConfigStore = defineStore('system-config-store', {
this.parse_ua = response.parse_ua;
this.notice_id = Number(response.notice_id);
// 公告
const {data} = await getNotice(this.notice_id);
this.notice = data.data;
if (this.notice_id > 0) {
const {data} = await getNotice(this.notice_id);
this.notice = data.data;
}
this.isInit = true;
}
}
});
Loading

0 comments on commit 706a621

Please sign in to comment.