Skip to main content

yggdrasil/pages/admin/logs/
cleanup.rs

1//! 日志清理策略:保留天数与最大行数,在标题行「清理策略」展开的面板里编辑。
2use crate::api::logs::{get_log_settings, update_log_settings};
3use crate::components::forms::NumberInput;
4use crate::components::ui::{
5    button_class, use_toast, ButtonSize, ButtonVariant, IconButton, IconName,
6};
7use crate::models::log::LogSettings;
8use crate::utils::error::failure_message;
9use leptos::prelude::*;
10use leptos::task::spawn_local;
11
12/// 清理策略面板。
13///
14/// Props:
15/// - `on_close`:点击「收起」时的回调
16#[component]
17pub(super) fn CleanupPolicy(#[prop(into)] on_close: Callback<()>) -> impl IntoView {
18    let toast = use_toast();
19    let saved = RwSignal::new(None::<LogSettings>);
20    let days = RwSignal::new(7_i64);
21    let rows = RwSignal::new(100_000_i64);
22    let saving = RwSignal::new(false);
23    // 挂载后拉取一次当前设置;失败时保留默认值(与原版一致,不打扰用户)。
24    Effect::new(move |_| {
25        spawn_local(async move {
26            if let Ok(s) = get_log_settings().await {
27                days.try_set(i64::from(s.retention_days));
28                rows.try_set(i64::from(s.max_rows));
29                saved.try_set(Some(s));
30            }
31        });
32    });
33    let dirty = move || {
34        saved.with(|s| {
35            s.as_ref().is_some_and(|s| {
36                i64::from(s.retention_days) != days.get() || i64::from(s.max_rows) != rows.get()
37            })
38        })
39    };
40    let save = move |_| {
41        saving.set(true);
42        let (retention_days, max_rows) = (days.get_untracked() as i32, rows.get_untracked() as i32);
43        spawn_local(async move {
44            match update_log_settings(retention_days, max_rows).await {
45                Ok(s) => {
46                    days.try_set(i64::from(s.retention_days));
47                    rows.try_set(i64::from(s.max_rows));
48                    saved.try_set(Some(s));
49                    toast.success("清理策略已保存");
50                }
51                Err(e) => toast.error(failure_message("保存失败", &e)),
52            }
53            saving.try_set(false);
54        });
55    };
56    view! {
57        <section class="admin-panel logs-cleanup" aria-labelledby="logs-cleanup-h">
58            <div class="logs-cleanup-head">
59                <div>
60                    <h2 id="logs-cleanup-h">"清理策略"</h2>
61                    <p>"后台任务定期清理,保留期与行数上限先到者生效。"</p>
62                </div>
63                <IconButton
64                    icon=IconName::X
65                    label="收起"
66                    size=ButtonSize::Sm
67                    onclick=move |_| on_close.run(())
68                />
69            </div>
70            <div class="logs-cleanup-fields">
71                <label for="logs-days">"保留天数"</label>
72                <NumberInput
73                    id="logs-days"
74                    value=days
75                    min=1
76                    max=90
77                    unit="天"
78                    onchange=move |v: i64| days.set(v)
79                />
80                <label for="logs-rows">"最大行数"</label>
81                <NumberInput
82                    id="logs-rows"
83                    value=rows
84                    min=1000
85                    max=1_000_000
86                    step=10_000
87                    unit="行"
88                    onchange=move |v: i64| rows.set(v)
89                />
90                <button
91                    class=button_class(ButtonVariant::Primary, ButtonSize::Sm)
92                    type="button"
93                    disabled=move || !dirty() || saving.get()
94                    on:click=save
95                >
96                    {move || if saving.get() { "保存中…" } else { "保存" }}
97                </button>
98            </div>
99        </section>
100    }
101}