Skip to content

Commit d071306

Browse files
authored
refactor(agents): improve backend setup, agent creation workflow, and data cleanup
- Add loading state management to BackendSwitcher with auto-unlock after 3s on error - Initialize agent UUID automatically and emit 'added' event on successful online detection - Fix Checkbox component event binding from @update:checked to @update:modelValue - Implement time-based storage management with minute-to-timestamp conversion in NodeSettingTabStorage - Replace immediate cleanup with scheduled cron-based cleanup for expired data - Fix KV field name from database_limit_agent_task to database_limit_task for consistency - Update cleanup descriptions to reflect proper retention period-based cleanup - Add conditional rendering for AddAgentDialog to improve component lifecycle
1 parent 71b8126 commit d071306

6 files changed

Lines changed: 102 additions & 32 deletions

File tree

‎src/components/BackendSwitcher.vue‎

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Button } from "@/components/ui/button";
1111
import { Input } from "@/components/ui/input";
1212
import { Label } from "@/components/ui/label";
1313
import { useBackendStore, type Backend } from "@/composables/useBackendStore";
14-
import { Trash2 } from "lucide-vue-next";
14+
import { Trash2, Loader2 } from "lucide-vue-next";
1515
import { RainbowButton } from "@/components/ui/rainbow-button";
1616
import { useI18n } from "vue-i18n";
1717
import { useLifecycle } from "@/composables/useLifecycle";
@@ -56,6 +56,7 @@ const { backends, currentBackend, addBackend, removeBackend, selectBackend } =
5656
const newName = ref(props.initForm.newName);
5757
const newUrl = ref(props.initForm.newUrl);
5858
const newToken = ref(props.initForm.newToken);
59+
const isLoading = ref(false);
5960
6061
const resetForm = () => {
6162
newName.value = props.initForm.newName;
@@ -65,18 +66,31 @@ const resetForm = () => {
6566
6667
const handleAdd = async () => {
6768
if (!newName.value || !newUrl.value || !newToken.value) return;
68-
const backend = {
69-
name: newName.value,
70-
url: newUrl.value,
71-
token: newToken.value,
72-
};
73-
addBackend(backend);
74-
await afterServerCreate(backend);
75-
resetForm();
76-
if (props.showList === false) isOpen.value = false;
77-
78-
// 防止出现有未预料到的未更新的内存变量
79-
location.reload();
69+
if (isLoading.value) return;
70+
71+
isLoading.value = true;
72+
try {
73+
const backend = {
74+
name: newName.value,
75+
url: newUrl.value,
76+
token: newToken.value,
77+
};
78+
addBackend(backend);
79+
await afterServerCreate(backend);
80+
resetForm();
81+
if (props.showList === false) isOpen.value = false;
82+
83+
isLoading.value = false;
84+
// 防止出现有未预料到的未更新的内存变量
85+
location.reload();
86+
} catch (e) {
87+
console.error("Failed to add backend:", e);
88+
isLoading.value = false;
89+
// 自动解锁:3秒后恢复正常操作
90+
setTimeout(() => {
91+
isLoading.value = false;
92+
}, 3000);
93+
}
8094
};
8195
8296
const handleRemove = (b: Backend) => removeBackend(b);
@@ -101,7 +115,10 @@ watch(
101115
</DialogDescription>
102116
</DialogHeader>
103117

104-
<div class="grid gap-4 py-4">
118+
<div
119+
class="grid gap-4 py-4"
120+
:class="{ 'opacity-50 pointer-events-none': isLoading }"
121+
>
105122
<!-- 主控列表:仅在 showList !== false 时显示 -->
106123
<template v-if="showList !== false">
107124
<div class="flex flex-col gap-2 max-h-[300px] overflow-y-auto">
@@ -198,9 +215,14 @@ watch(
198215
</div>
199216
<RainbowButton
200217
@click="handleAdd"
201-
:disabled="!newName || !newUrl || !newToken"
218+
:disabled="!newName || !newUrl || !newToken || isLoading"
202219
>
203-
{{ t("dashboard.servers.addServer") }}
220+
<Loader2 v-if="isLoading" class="h-4 w-4 mr-2 animate-spin" />
221+
{{
222+
isLoading
223+
? t("dashboard.common.loading")
224+
: t("dashboard.servers.addServer")
225+
}}
204226
</RainbowButton>
205227
</div>
206228
</div>

‎src/components/agents/AddAgentDialog.vue‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const step = ref(1);
5050
5151
// Step 1: 基础信息
5252
const nodeName = ref("");
53-
const nodeUuid = ref("");
53+
const nodeUuid = ref(crypto.randomUUID());
5454
5555
const generateUuid = () => {
5656
nodeUuid.value = crypto.randomUUID();
@@ -62,7 +62,7 @@ const generatedToken = ref("");
6262
const resetForm = () => {
6363
step.value = 1;
6464
nodeName.value = "";
65-
nodeUuid.value = "";
65+
nodeUuid.value = crypto.randomUUID();
6666
generatedToken.value = "";
6767
selectedCronIds.value = new Set();
6868
staticRetention.value = 60 * 24 * 7; // minute
@@ -123,6 +123,7 @@ const checkOnline = async () => {
123123
},
124124
});
125125
stopPolling();
126+
emit("added");
126127
}
127128
} catch {
128129
// ignore
@@ -373,8 +374,8 @@ const steps = [
373374
>
374375
<Checkbox
375376
:checked="selectedCronIds.has(cron.id)"
376-
@update:checked="
377-
(v: boolean) => {
377+
@update:modelValue="
378+
(v: unknown) => {
378379
if (v) selectedCronIds.add(cron.id);
379380
else selectedCronIds.delete(cron.id);
380381
}

‎src/components/node-manage/NodeManageTabAgents.vue‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ defineExpose({ fetchAgents });
291291
</TableBody>
292292
</Table>
293293
</div>
294-
<AddAgentDialog v-model:open="addAgentOpen" @added="fetchAgents()" />
294+
<AddAgentDialog
295+
v-if="addAgentOpen"
296+
v-model:open="addAgentOpen"
297+
@added="fetchAgents()"
298+
/>
295299
</div>
296300
</template>

‎src/components/node/setting/NodeSettingTabStorage.vue‎

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ import {
1414
} from "@/components/ui/card";
1515
import { useKv } from "@/composables/useKv";
1616
import { useI18n } from "vue-i18n";
17+
import { useCron, taskToCronType } from "@/composables/useCron";
1718
1819
const props = defineProps<{ uuid: string }>();
1920
2021
const { t } = useI18n();
2122
const kv = useKv();
23+
const cron = useCron();
2224
2325
const loading = ref(false);
2426
const saveLoading = ref(false);
@@ -28,6 +30,25 @@ const storageStatic = ref<number | undefined>(undefined);
2830
const storageDynamic = ref<number | undefined>(undefined);
2931
const storageAgentTask = ref<number | undefined>(undefined);
3032
33+
let cleanCronName = "periodic-cleanup";
34+
35+
const oneMinute = 60 * 1000;
36+
function tsToMinute(value: unknown) {
37+
if (typeof value === "number") {
38+
return value / oneMinute;
39+
}
40+
if (typeof value === "string" && /^\d+$/.test(value)) {
41+
return parseInt(value) / oneMinute;
42+
}
43+
}
44+
const minute2Ts = (t: typeof storageAgentTask.value) => {
45+
const oneMinute = 60 * 1000; // ms
46+
if (typeof t == "undefined") {
47+
return undefined;
48+
}
49+
return t * oneMinute;
50+
};
51+
3152
onMounted(async () => {
3253
loading.value = true;
3354
try {
@@ -38,11 +59,13 @@ onMounted(async () => {
3859
const get = (key: string) => results.find((r) => r.key === key)?.value;
3960
4061
if (get("database_limit_static_monitoring") !== undefined)
41-
storageStatic.value = Number(get("database_limit_static_monitoring"));
62+
storageStatic.value = tsToMinute(get("database_limit_static_monitoring"));
4263
if (get("database_limit_dynamic_monitoring") !== undefined)
43-
storageDynamic.value = Number(get("database_limit_dynamic_monitoring"));
44-
if (get("database_limit_agent_task") !== undefined)
45-
storageAgentTask.value = Number(get("database_limit_agent_task"));
64+
storageDynamic.value = tsToMinute(
65+
get("database_limit_dynamic_monitoring"),
66+
);
67+
if (get("database_limit_task") !== undefined)
68+
storageAgentTask.value = tsToMinute(get("database_limit_task"));
4669
} catch {
4770
// ignore
4871
} finally {
@@ -59,17 +82,17 @@ async function handleSave() {
5982
if (storageStatic.value !== undefined)
6083
items.push({
6184
key: "database_limit_static_monitoring",
62-
value: storageStatic.value,
85+
value: minute2Ts(storageStatic.value),
6386
});
6487
if (storageDynamic.value !== undefined)
6588
items.push({
6689
key: "database_limit_dynamic_monitoring",
67-
value: storageDynamic.value,
90+
value: minute2Ts(storageDynamic.value),
6891
});
6992
if (storageAgentTask.value !== undefined)
7093
items.push({
71-
key: "database_limit_agent_task",
72-
value: storageAgentTask.value,
94+
key: "database_limit_task",
95+
value: minute2Ts(storageAgentTask.value),
7396
});
7497
7598
if (items.length === 0) {
@@ -90,6 +113,26 @@ async function handleSave() {
90113
}
91114
}
92115
116+
async function cleanExpiredData() {
117+
try {
118+
cleanLoading.value = true;
119+
const tempName = crypto.randomUUID();
120+
await cron.create({
121+
name: tempName, // temp name
122+
cron_expression: "* * * * * *",
123+
cron_type: {
124+
server: "clean_up_database",
125+
},
126+
});
127+
await new Promise((r) => setTimeout(r, 2000));
128+
await cron.remove(tempName);
129+
toast.success(t("dashboard.node.storage.cleanSuccess"));
130+
} catch (error) {
131+
if (error instanceof Error) toast.error(error.toString());
132+
} finally {
133+
cleanLoading.value = false;
134+
}
135+
}
93136
async function handleCleanData() {
94137
cleanLoading.value = true;
95138
try {
@@ -229,7 +272,7 @@ async function handleCleanData() {
229272
:confirm-text="$t('dashboard.node.storage.cleanConfirm')"
230273
:cancel-text="$t('dashboard.node.storage.cleanCancel')"
231274
:loading="cleanLoading"
232-
@confirm="handleCleanData"
275+
@confirm="cleanExpiredData"
233276
>
234277
<Button variant="destructive" :disabled="cleanLoading">
235278
<Loader2 v-if="cleanLoading" class="h-4 w-4 animate-spin mr-2" />

‎src/locales/en.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ export default {
503503
title: "Storage Retention",
504504
cleanTitle: "Clean Data Now",
505505
cleanDesc:
506-
"This will temporarily clear all stored data. This action cannot be undone.",
506+
"Immediately purge expired data based on the configured retention period. This action is irreversible.",
507507
cleanButton: "Clean Data",
508508
cleaning: "Cleaning...",
509509
cleanSuccess: "Data cleaned successfully",

‎src/locales/zh_cn.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ export default {
498498
storage: {
499499
title: "储存周期设置",
500500
cleanTitle: "立即清理数据",
501-
cleanDesc: "将临时清除所有储存数据,此操作不可恢复。",
501+
cleanDesc: "立即按照储存周期设置清理过期数据,此操作不可恢复。",
502502
cleanButton: "清理数据",
503503
cleaning: "清理中...",
504504
cleanSuccess: "数据清理完成",

0 commit comments

Comments
 (0)