Skip to content

Commit f90558f

Browse files
committed
feat: 系统代理密钥优化随机生成
1 parent 7b06d83 commit f90558f

File tree

1 file changed

+147
-4
lines changed

1 file changed

+147
-4
lines changed

web/src/views/Settings.vue

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import { settingsApi, type SettingCategory } from "@/api/settings";
3-
import { HelpCircle, Save } from "@vicons/ionicons5";
3+
import { copy } from "@/utils/clipboard";
4+
import { Copy, HelpCircle, Key, Save } from "@vicons/ionicons5";
45
import {
56
NButton,
67
NCard,
@@ -11,6 +12,7 @@ import {
1112
NIcon,
1213
NInput,
1314
NInputNumber,
15+
NModal,
1416
NSpace,
1517
NTooltip,
1618
useMessage,
@@ -23,6 +25,11 @@ const form = ref<Record<string, string | number>>({});
2325
const isSaving = ref(false);
2426
const message = useMessage();
2527
28+
// 密钥生成弹窗相关
29+
const showKeyGeneratorModal = ref(false);
30+
const keyCount = ref(1);
31+
const isGenerating = ref(false);
32+
2633
fetchSettings();
2734
2835
async function fetchSettings() {
@@ -58,6 +65,88 @@ async function handleSubmit() {
5865
isSaving.value = false;
5966
}
6067
}
68+
69+
// 生成随机字符串
70+
function generateRandomString(length: number): string {
71+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
72+
let result = "";
73+
for (let i = 0; i < length; i++) {
74+
result += chars.charAt(Math.floor(Math.random() * chars.length));
75+
}
76+
return result;
77+
}
78+
79+
// 生成密钥
80+
function generateKeys(): string[] {
81+
const keys: string[] = [];
82+
for (let i = 0; i < keyCount.value; i++) {
83+
keys.push(`sk-${generateRandomString(48)}`);
84+
}
85+
return keys;
86+
}
87+
88+
// 打开密钥生成器弹窗
89+
function openKeyGenerator() {
90+
showKeyGeneratorModal.value = true;
91+
keyCount.value = 1;
92+
}
93+
94+
// 确认生成密钥
95+
function confirmGenerateKeys() {
96+
if (isGenerating.value) {
97+
return;
98+
}
99+
100+
try {
101+
isGenerating.value = true;
102+
const newKeys = generateKeys();
103+
const currentValue = (form.value["proxy_keys"] as string) || "";
104+
105+
let updatedValue = currentValue.trim();
106+
107+
// 处理逗号兼容情况
108+
if (updatedValue && !updatedValue.endsWith(",")) {
109+
updatedValue += ",";
110+
}
111+
112+
// 添加新生成的密钥
113+
if (updatedValue) {
114+
updatedValue += newKeys.join(",");
115+
} else {
116+
updatedValue = newKeys.join(",");
117+
}
118+
119+
form.value["proxy_keys"] = updatedValue;
120+
showKeyGeneratorModal.value = false;
121+
122+
message.success(`成功生成 ${keyCount.value} 个密钥`);
123+
} finally {
124+
isGenerating.value = false;
125+
}
126+
}
127+
128+
// 复制代理密钥
129+
async function copyProxyKeys() {
130+
const proxyKeys = (form.value["proxy_keys"] as string) || "";
131+
if (!proxyKeys.trim()) {
132+
message.warning("暂无密钥可复制");
133+
return;
134+
}
135+
136+
// 将逗号分隔的密钥转换为换行分隔
137+
const formattedKeys = proxyKeys
138+
.split(",")
139+
.map(key => key.trim())
140+
.filter(key => key.length > 0)
141+
.join("\n");
142+
143+
const success = await copy(formattedKeys);
144+
if (success) {
145+
message.success("密钥已复制到剪贴板");
146+
} else {
147+
message.error("复制失败,请手动复制");
148+
}
149+
}
61150
</script>
62151

63152
<template>
@@ -72,11 +161,11 @@ async function handleSubmit() {
72161
hoverable
73162
bordered
74163
>
75-
<n-grid :x-gap="24" :y-gap="0" responsive="screen" cols="1 s:2 m:2 l:3 xl:4">
164+
<n-grid :x-gap="36" :y-gap="0" responsive="screen" cols="1 s:2 m:2 l:3 xl:3">
76165
<n-grid-item
77166
v-for="item in category.settings"
78167
:key="item.key"
79-
:span="item.key === 'proxy_keys' ? 4 : 1"
168+
:span="item.key === 'proxy_keys' ? 3 : 1"
80169
>
81170
<n-form-item
82171
:path="item.key"
@@ -115,7 +204,30 @@ async function handleSubmit() {
115204
placeholder="请输入内容"
116205
clearable
117206
size="small"
118-
/>
207+
>
208+
<template v-if="item.key === 'proxy_keys'" #suffix>
209+
<n-space :size="4" :wrap-item="false">
210+
<n-button text type="primary" size="small" @click="openKeyGenerator">
211+
<template #icon>
212+
<n-icon :component="Key" />
213+
</template>
214+
生成
215+
</n-button>
216+
<n-button
217+
text
218+
type="tertiary"
219+
size="small"
220+
@click="copyProxyKeys"
221+
style="opacity: 0.7"
222+
>
223+
<template #icon>
224+
<n-icon :component="Copy" />
225+
</template>
226+
复制
227+
</n-button>
228+
</n-space>
229+
</template>
230+
</n-input>
119231
</n-form-item>
120232
</n-grid-item>
121233
</n-grid>
@@ -141,5 +253,36 @@ async function handleSubmit() {
141253
{{ isSaving ? "保存中..." : "保存设置" }}
142254
</n-button>
143255
</div>
256+
257+
<!-- 密钥生成器弹窗 -->
258+
<n-modal
259+
v-model:show="showKeyGeneratorModal"
260+
preset="dialog"
261+
title="生成代理密钥"
262+
positive-text="确认生成"
263+
negative-text="取消"
264+
:positive-button-props="{ loading: isGenerating }"
265+
@positive-click="confirmGenerateKeys"
266+
>
267+
<n-space vertical :size="16">
268+
<div>
269+
<p style="margin: 0 0 8px 0; color: #666; font-size: 14px">
270+
请输入要生成的密钥数量(最大100个):
271+
</p>
272+
<n-input-number
273+
v-model:value="keyCount"
274+
:min="1"
275+
:max="100"
276+
placeholder="请输入数量"
277+
style="width: 100%"
278+
:disabled="isGenerating"
279+
/>
280+
</div>
281+
<div style="color: #999; font-size: 12px; line-height: 1.4">
282+
<p style="margin: 0">密钥格式:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>
283+
<p style="margin: 4px 0 0 0">生成的密钥将会插入到当前输入框内容的后面,以逗号分隔</p>
284+
</div>
285+
</n-space>
286+
</n-modal>
144287
</n-space>
145288
</template>

0 commit comments

Comments
 (0)