From 817969b11680e20b4fdf55141d0876f259d22d04 Mon Sep 17 00:00:00 2001 From: bread <104435263+bread-ovO@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:35:53 +0800 Subject: [PATCH 1/4] feat(dashboard): add log and cache cleanup in settings --- astrbot/core/utils/storage_cleaner.py | 275 ++++++++++++++++++ astrbot/dashboard/routes/stat.py | 28 ++ .../mdi-subset/materialdesignicons-subset.css | 18 +- .../materialdesignicons-webfont-subset.woff | Bin 16252 -> 16648 bytes .../materialdesignicons-webfont-subset.woff2 | Bin 13068 -> 13348 bytes .../components/shared/StorageCleanupPanel.vue | 241 +++++++++++++++ .../i18n/locales/en-US/features/settings.json | 34 +++ .../i18n/locales/ru-RU/features/settings.json | 36 ++- .../i18n/locales/zh-CN/features/settings.json | 34 +++ dashboard/src/views/Settings.vue | 7 +- tests/test_storage_cleaner.py | 85 ++++++ 11 files changed, 755 insertions(+), 3 deletions(-) create mode 100644 astrbot/core/utils/storage_cleaner.py create mode 100644 dashboard/src/components/shared/StorageCleanupPanel.vue create mode 100644 tests/test_storage_cleaner.py diff --git a/astrbot/core/utils/storage_cleaner.py b/astrbot/core/utils/storage_cleaner.py new file mode 100644 index 0000000000..144c602d6f --- /dev/null +++ b/astrbot/core/utils/storage_cleaner.py @@ -0,0 +1,275 @@ +from __future__ import annotations + +from collections.abc import Iterable +from pathlib import Path +from typing import Protocol + +from astrbot import logger +from astrbot.core.utils.astrbot_path import get_astrbot_data_path, get_astrbot_temp_path + + +class ConfigLike(Protocol): + def get(self, key: str, default=None): ... + + +class StorageCleaner: + TARGET_LOGS = "logs" + TARGET_CACHE = "cache" + VALID_TARGETS = {TARGET_LOGS, TARGET_CACHE, "all"} + + def __init__( + self, + config: ConfigLike, + *, + data_dir: Path | None = None, + temp_dir: Path | None = None, + ) -> None: + self._config = config + self._data_dir = data_dir or Path(get_astrbot_data_path()) + self._temp_dir = temp_dir or Path(get_astrbot_temp_path()) + + def get_status(self) -> dict: + logs = self._build_status(self.TARGET_LOGS) + cache = self._build_status(self.TARGET_CACHE) + return { + self.TARGET_LOGS: logs, + self.TARGET_CACHE: cache, + "total_bytes": logs["size_bytes"] + cache["size_bytes"], + } + + def cleanup(self, target: str = "all") -> dict: + normalized_target = (target or "all").strip().lower() + if normalized_target not in self.VALID_TARGETS: + raise ValueError(f"Unsupported cleanup target: {target}") + + targets = ( + [self.TARGET_LOGS, self.TARGET_CACHE] + if normalized_target == "all" + else [normalized_target] + ) + results = { + target_name: self._cleanup_target(target_name) for target_name in targets + } + status = self.get_status() + + return { + "target": normalized_target, + "results": results, + "removed_bytes": sum(item["removed_bytes"] for item in results.values()), + "processed_files": sum( + item["processed_files"] for item in results.values() + ), + "deleted_files": sum(item["deleted_files"] for item in results.values()), + "truncated_files": sum( + item["truncated_files"] for item in results.values() + ), + "failed_files": sum(item["failed_files"] for item in results.values()), + "status": status, + } + + def _build_status(self, target: str) -> dict: + files = self._collect_target_files(target) + size_bytes, file_count = self._summarize_files(files) + primary_path = self._primary_path_for_target(target) + + return { + "size_bytes": size_bytes, + "file_count": file_count, + "path": str(primary_path), + "exists": primary_path.exists(), + } + + def _cleanup_target(self, target: str) -> dict: + files = self._collect_target_files(target) + removed_bytes = 0 + deleted_files = 0 + truncated_files = 0 + failed_files = 0 + active_log_files = ( + self._active_log_files() if target == self.TARGET_LOGS else set() + ) + + for file_path in sorted(files): + if not file_path.exists(): + continue + + try: + size = file_path.stat().st_size + except OSError as exc: + logger.warning("Failed to stat %s before cleanup: %s", file_path, exc) + failed_files += 1 + continue + + try: + if file_path in active_log_files: + file_path.write_bytes(b"") + truncated_files += 1 + else: + file_path.unlink() + deleted_files += 1 + removed_bytes += size + except OSError as exc: + logger.warning("Failed to clean %s: %s", file_path, exc) + failed_files += 1 + + if target == self.TARGET_CACHE: + self._cleanup_empty_dirs(self._temp_dir) + self._temp_dir.mkdir(parents=True, exist_ok=True) + + logger.info( + "Storage cleanup finished: target=%s removed_bytes=%s deleted_files=%s truncated_files=%s failed_files=%s", + target, + removed_bytes, + deleted_files, + truncated_files, + failed_files, + ) + + return { + "removed_bytes": removed_bytes, + "processed_files": deleted_files + truncated_files, + "deleted_files": deleted_files, + "truncated_files": truncated_files, + "failed_files": failed_files, + } + + def _collect_target_files(self, target: str) -> set[Path]: + if target == self.TARGET_LOGS: + return self._collect_log_files() + if target == self.TARGET_CACHE: + return self._collect_cache_files() + raise ValueError(f"Unsupported cleanup target: {target}") + + def _collect_log_files(self) -> set[Path]: + files = set(self._iter_files(self._data_dir / "logs")) + for log_path in self._configured_log_paths(): + files.update(self._iter_log_family_files(log_path)) + return files + + def _collect_cache_files(self) -> set[Path]: + files = set(self._iter_files(self._temp_dir)) + files.update(self._data_dir.glob("plugins_custom_*.json")) + + for extra_file in ( + self._data_dir / "plugins.json", + self._data_dir / "sandbox_skills_cache.json", + ): + if extra_file.is_file(): + files.add(extra_file) + + return files + + def _configured_log_paths(self) -> set[Path]: + return { + self._resolve_log_path( + self._config.get("log_file_path"), + default_relative_path="logs/astrbot.log", + ), + self._resolve_log_path( + self._config.get("trace_log_path"), + default_relative_path="logs/astrbot.trace.log", + ), + } + + def _active_log_files(self) -> set[Path]: + active_files: set[Path] = set() + if self._config.get("log_file_enable", False): + active_files.add( + self._resolve_log_path( + self._config.get("log_file_path"), + default_relative_path="logs/astrbot.log", + ) + ) + if self._config.get("trace_log_enable", False): + active_files.add( + self._resolve_log_path( + self._config.get("trace_log_path"), + default_relative_path="logs/astrbot.trace.log", + ) + ) + return active_files + + def _resolve_log_path( + self, + configured_path: str | None, + *, + default_relative_path: str, + ) -> Path: + path_value = configured_path or default_relative_path + path = Path(path_value) + if path.is_absolute(): + return path.resolve() + return (self._data_dir / path).resolve() + + def _iter_log_family_files(self, log_path: Path) -> set[Path]: + files: set[Path] = set() + if log_path.is_file(): + files.add(log_path) + + parent_dir = log_path.parent + if not parent_dir.exists(): + return files + + base_name = log_path.name + suffix = log_path.suffix + stem = base_name[: -len(suffix)] if suffix else base_name + + for candidate in parent_dir.iterdir(): + if not candidate.is_file() or candidate == log_path: + continue + candidate_name = candidate.name + if suffix: + if candidate_name.startswith(f"{stem}.") and candidate_name.endswith( + suffix + ): + files.add(candidate) + elif candidate_name.startswith(f"{stem}."): + files.add(candidate) + + return files + + def _primary_path_for_target(self, target: str) -> Path: + if target == self.TARGET_LOGS: + return self._data_dir / "logs" + if target == self.TARGET_CACHE: + return self._temp_dir + raise ValueError(f"Unsupported cleanup target: {target}") + + @staticmethod + def _iter_files(path: Path) -> Iterable[Path]: + if path.is_file(): + yield path + return + if not path.exists(): + return + for child in path.rglob("*"): + if child.is_file(): + yield child + + @staticmethod + def _summarize_files(files: Iterable[Path]) -> tuple[int, int]: + total_size = 0 + file_count = 0 + for file_path in files: + if not file_path.exists() or not file_path.is_file(): + continue + try: + total_size += file_path.stat().st_size + file_count += 1 + except OSError as exc: + logger.debug("Skip %s during storage scan: %s", file_path, exc) + return total_size, file_count + + @staticmethod + def _cleanup_empty_dirs(root_dir: Path) -> None: + if not root_dir.exists(): + return + for path in sorted( + root_dir.rglob("*"), key=lambda item: len(item.parts), reverse=True + ): + if not path.is_dir(): + continue + try: + path.rmdir() + except OSError: + continue diff --git a/astrbot/dashboard/routes/stat.py b/astrbot/dashboard/routes/stat.py index 532238ac7a..7b336f78c6 100644 --- a/astrbot/dashboard/routes/stat.py +++ b/astrbot/dashboard/routes/stat.py @@ -1,3 +1,4 @@ +import asyncio import os import re import threading @@ -17,6 +18,7 @@ from astrbot.core.db.migration.helper import check_migration_needed_v4 from astrbot.core.utils.astrbot_path import get_astrbot_path from astrbot.core.utils.io import get_dashboard_version +from astrbot.core.utils.storage_cleaner import StorageCleaner from astrbot.core.utils.version_comparator import VersionComparator from .route import Response, Route, RouteContext @@ -39,10 +41,13 @@ def __init__( "/stat/changelog": ("GET", self.get_changelog), "/stat/changelog/list": ("GET", self.list_changelog_versions), "/stat/first-notice": ("GET", self.get_first_notice), + "/stat/storage": ("GET", self.get_storage_status), + "/stat/storage/cleanup": ("POST", self.cleanup_storage), } self.db_helper = db_helper self.register_routes() self.core_lifecycle = core_lifecycle + self.storage_cleaner = StorageCleaner(self.config) async def restart_core(self): if DEMO_MODE: @@ -89,6 +94,29 @@ async def get_version(self): async def get_start_time(self): return Response().ok({"start_time": self.core_lifecycle.start_time}).__dict__ + async def get_storage_status(self): + try: + status = await asyncio.to_thread(self.storage_cleaner.get_status) + return Response().ok(status).__dict__ + except Exception as e: + logger.error(f"获取存储占用失败: {e}", exc_info=True) + return Response().error(f"获取存储占用失败: {e}").__dict__ + + async def cleanup_storage(self): + try: + data = await request.get_json(silent=True) + target = "all" + if isinstance(data, dict): + target = str(data.get("target", "all")) + + result = await asyncio.to_thread(self.storage_cleaner.cleanup, target) + return Response().ok(result).__dict__ + except ValueError as e: + return Response().error(str(e)).__dict__ + except Exception as e: + logger.error(f"清理存储失败: {e}", exc_info=True) + return Response().error(f"清理存储失败: {e}").__dict__ + async def get_stat(self): offset_sec = request.args.get("offset_sec", 86400) offset_sec = int(offset_sec) diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css b/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css index 52a052cfa1..6117190cfc 100644 --- a/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css +++ b/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css @@ -1,4 +1,4 @@ -/* Auto-generated MDI subset – 231 icons */ +/* Auto-generated MDI subset – 235 icons */ /* Do not edit manually. Run: pnpm run subset-icons */ @font-face { @@ -112,6 +112,10 @@ content: "\F09D1"; } +.mdi-broom::before { + content: "\F00E2"; +} + .mdi-bug::before { content: "\F00E4"; } @@ -300,6 +304,10 @@ content: "\F1640"; } +.mdi-database-refresh-outline::before { + content: "\F1634"; +} + .mdi-delete::before { content: "\F01B4"; } @@ -308,6 +316,10 @@ content: "\F09E7"; } +.mdi-delete-sweep-outline::before { + content: "\F0C62"; +} + .mdi-dots-hexagon::before { content: "\F15FF"; } @@ -728,6 +740,10 @@ content: "\F0A66"; } +.mdi-qrcode::before { + content: "\F0432"; +} + .mdi-refresh::before { content: "\F0450"; } diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff index 85c2ed9fdfa09200ceb1892ce611e8570eb76b5e..32e3eea05b3a960cb13f7b1c20aefe3fd0dea83c 100644 GIT binary patch delta 16396 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 Q|G|6)05G@$rjvs}g~(sQm;e9( diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 index 376e47ce72445b88b80adb0605c7e6319dcebd22..4b578d1d5c0155a5fa50e2d6881a881d09b4433a 100644 GIT binary patch literal 13348 zcmV+0LI)rl*I*TFoXUoc1At$~6Gl-A zSvfLLN9N>q_W!2@PKH>}huXQ_P(Y+Pb9w~HLIin&JQBghOr5D~ZhWv{!GaxZ3O9Q7 z_O|TLYv26ryZ(6!iZ$5v)tyfxhw84={b<+BMA8|Qzuc~~aQvysE16sZYOJhh>5eDB z=zzchQTddQj*4UE|3w+xg7{pweOB07V$Go zf};o`j(?|iq+=6AtEFyo>2P%czX`5i#j%JTreh05I$T}GdZ(X2<6X^4-A(@XT}mr3 zF93$1_@%1fTe8~)y32u-E+)wr+H5bdw%N$SpHb6-EztfHa(l_5YXf><`jsiN9RMKk ze_wz93+hESlia^y5?q2?`4eWrd|N&4gvV?|NH6fZnUIY`d2 z%29KbL#lpUxpf;W^GJVE+Gl&Xf}P;G1XljUS_n5@d`>&JUGlIZA`&D>u-$(yW9q^a zdIRD3C=zAkx=+w4dxbzJ7tZtuFR?_AY#B-U-d9K@ zZ%)p4O49YfQl-cr1!bugG$WHn-MTfe zmj^)EE>N$9i_5ZoPXc%hs(Jrdw3;<*`R{-w0-U}pv)liN1QUX{5)p)NJYmFH3xG`gTOqj5agF~-NF6r}yTi5#6 zCxBvuI&}stvkXq04jp)_ttMD&Em4mi(vLzmXpmyW2-T=jnh$(HciL%&^QM?wTL z;rQA&m-|XEg)d%$fQm{aMT*!8D4Th-z0zsHOa(kttKF zLkDK9wK_d|^gi%`VIPq&%7+z`G->8NPO;c&r`2JHZN`n;=OW<{gNjqCRM$39+^nn~ zuY29QQ4&7!Y{D;IO<7r|yswh=%KNF=U0jvTsO^s_@_Bn2I(R z$DMP|371`Vl8ei!&2s0oir+c!f-_Dy;oKgPoL7`47ZiU^E-L<=TvCaQ;<9$_uK3bd zuI@LI>neFOxuH@G$xW56FK+EalG`d1SlofZ+}&#=_f+OVa$jY`k_Rg5Ngk@4xOhZO z?J)x3iE`zhs#4{d!$v*dF_M?6AiMKQ6_5Qtj@PO}eR=!koiFtXx8TQLVU23kZ|h=s zCF_!}XmniO71F#Vx<)a)MLC>6j!=Xnvm%T}T;I0`@5+tRj*9XkmflI<7HcEuv__|#jjxC%PuH%zrKpn6odNZT0Z$ePuP zPHPy{k5wzp0$4i02c{%Vi}K9b4xYTw*Z}9>5Nd8xh~9e|fsYvQ2qOvvt>)@16qHI` zV$wkCmoPxdBp~ydy`@^mDfi8xI+%jA)LhmwK-vLF)mXEDZnipd^{F~HAaO%W6?W~N z=vyp6Lsiay*FXyzf!2}iBXa4CZGpyjYxC%t2M>4yRJgH5#m6d0M%?5`8#q5GR02A* zWE1USb*Z^{#T$`<{R2Kgs6na#`f_Bp)BeS~4FAqz7@`P>xeTQ@m7*uM;P{*pv2U4G z%trL$Cvjs9DX`VhI?;tZZHxZte>F;jk2;ox2(GeQ()#XU z$3D3p=NSt~eO5t+cXu$~NR_s>DNHd6+lBpMf-&*XxuI{Qv3j8a%DT%Eq00na6VdaH?0k4Y)_6j(TB*2 zKgMqQIfY}An1}>SqJH~Uw;c^s{8M6_VDh(|K}cz*K^p5RpE46JbbRv&c9@Er~W201ML)avQre1`X)YIK-v7fkQs(wp(Y zQlnB098yHilcps$nDW*}z$&rs>VsPn$CJ(T$BiN2Z#H0*K zldM|h5EiD~T)0l>$+MwBj8$4+6rb6<#kH1YwH^k( zgh_(3GT_Dn>B0ft+0v{glubGRe{f^U=tKx=#IaJe?rF0qVFVX^Fg}ovtxR4vlxjCI(@0zpyd~-TCZw8Kc zIejt3H3oM+g8Tt7&2&!thKUo6_`SB&x@JXLe(NeRO4wr&iafVM9dk%1CzUO&?{c!q zosdiG4FWD)@dR}UepedZ4bs}MHqDHQJx(aNy?2oEM&u-8@a$zNdm1hQiiA}=Osu1`R;p_S zA`uFGLy3a&#s_qb2&)+p!Yh#u%B1#`o?QvLh#>?2Z~;0LVFJf+QE^$OoZp8Gd$dBXqY2ZJ0uHKwp}GfSwU$7MDf;G*;90fWp_+Y1Q|9uDNO6g-Vx z$^xiCRK<)<9R3aAaTqB`h=K_s=`~Z-L3>O495aFW#i|4N$m?BDEi8(a;Gwc zqaMJ6d0ZvG*mH}ca>(?NvB&^qwSUwH$iDJAg`n7$Rg%hh{}njie2wc%ZImRT?Y#F= zwNF#$5f3npx*mdmDd6$5`w?VnZYr82MJP_jqY9*p3&s$2`dkS4#u0;lXN^U}2++E~ zqJ9hBDn{}rMyw1~y;K}cPh)V$JqTU@(PeAeXY?K$*Kz>*RLgyaC z@y07*Pp}XlMXl>EuVmchq%j0U2|63EZ^rsm_?G9$!wVO6XqTQp-8_FzRabM$$R0Z? z?OS4>D-Bw(T2G#hFLczGvZQZqy1E$ImB1FQg{>w+Gy^I|$#f!pmJbli2Ebk$6Y~{i z4H9=?LYS%bOHTLa$*6pg146tg9LFeY8Sbo(mjOW&ed( zOfuehg-|z{FyahG;>?)}xXx?I{zfRDfn$KUcp%4yCltb(nBIgc z+lfSuEzgH_Y<2M!qK}OA_#SjP;yF;Yy01}8iPx}FXONoR(#SA1eG1Y*HU2u8OK@6Z zXEFHd1NG$G8vM&EIeU1~6d0=iH z{?%1hlUU~HIvvP${eQ@a0fpzhn7Zc{s##@M6#Hzs5r#4k0`gP0w3|;p%s1MFUjmO% z&zEYRB~?97V!p5Z+9~d`e9S?aw|nKEeg4h_a*L@gX05N)6jAA#El&RWp^khUlyQ7) zK`go?*=%+zyW}hZCKPC@d7_J?t)s=L8Z^1qThBI4(J7>TiX8j^8VEw7EJw;|Cs4FY z4w@QI1PO{7OeuK`hDP)ht^pBIvUm`mh#Rq=WXm8?wU~BVb@31ta56+kcbqmwt+ zeE&R6$qEo49Yo@ZH@@KlY^#q3kj;fCSPbSVxx$^eA@rVte=?bUr3B7hU4(>ISHFhe zf3@H3^FwTA+wqwl$ESB}qhV*6cw{3=usCgmO4#wqsaI&*ou7SdzZAtECpZaL}uDD{U^;J73 zHsh#TznUf$P~oYeHK*omm*(J?!v0&dXG>e|q7oJU zc5$j}Krub>ky!p(UT7x1;V4Am z_-ROJmn;%>=CRGNhYmU!$e?*=yC+lG)tkK=?o0t zjPYt2p1q={Q~6Q>TvF%tFy9?|wm+p_qQ$d(H?)I=%MtdkwAg>|+1izh`PKDu)@zfc z^Y@?U!#uR-!`@)I-|ot2zc_RM^0`a*O5L?w+3!f5$;EN0O9~v7;n~scLB=Dj#}8h- z`ugep?DDBhDO{CTUcI=MT`o#g@|W7(l~pB_L_o&6>c_E7f%OiRV?f!y^6HbHe*W~x zufM2j2xc)nEB0K(n5;=`K@^;Dbh;}K~{NmQG$$s(KlhZ1t4(4@t-=Nb)z-~fL zqAoRm6*0tU3C-bwL`lF(s3U`?SP@q48xTb~R_6)u78JW=BV}9KEIX0X&VB z;nbb|1QZg{4pf%cW-EG zzSS-L%&y#;?7j{}6 z+J57f`b(yEYFjn?>)_Zf{Ptb_=Y0L__D1ff;nZ%EvCI7p`Jg&8<&mu(F7oz3KI)P4 zaG>b2Qkmu)2}9{vsO;EW{8e9?u5WV4C#tl#IQK5M*DjIFlM}uq7d&~t87M<~Pd=a$ zMI~>mxRjO3hTHd?1Kx#&Cacbe|LkR&|%OL7m3wt%DNvQCk+b(5Q!bl_4U%hYubS?il_CF`ZOzsK5@+)BRBReg26 zI7+n;*ZbMd!)>QkpYQR{eSE(vrA#&l53Ut_seI=gY@Os_YNZquyf&chOYTfS#$~Sn zd21z9{2n2V`R?9sJJTi}xqHm_mpr*!@Q7tCOC9{~X|>mpmIuY&dVW<2*-J#@cEE=F zS?3CVWt>!z=`>-Zkr}5#aIKE67Hvd@c!>3a6K5V;4+>zw z_uhSbegH!(MoH^|y~-&lGN6uv9h}4@n~_utbO}#H320OUVRxA77up4{Kt}mhWq3oH zFLktJ-~^Hp%Y9Y64F^+0f>rYnnK#7kyqPc1X~4&gLP`{>j5V(wt@UNoc>30z;h8)W z@t!s~eJaDutQc4Vt~+LoBWD9TZ8$I)43JdRn#sz$?fCGUQ`0c&$?E5-@*FNxqli{Q zlQtwLr9~W^n$dU9qzjUT&3k+oabL=Lux-L%BtLfnhRro~Jb>)GN`v`0kzZAZ{NqCo zL<_i=ksxmLv*$_CcXDCcR7((?y;a^iLa~8?hpNnYl*1B9B43kOgVD2_QKpV2k50&2 zDN`mhC7)W#O1XSRP-i|(Lzg3(M)vKe-gB9o+IiQ7GDI-NrtCcP!VGpc6 zqoRqS+2Kj5zi2{$G`hW!r9}b;@ptJd0EAG;eqV+!f`88bp-f!l3e^sZ#H;Xkj_+3V}wFaE3Jg&U}?QW{hy%%vujNs^jq^ zI8Ms$e+5T*n8HknVDTBo>?`UC-Zjw~29ob%VY*vxCLyqlc&ROoYudb~YWgC#*=$D> zsZ}-87ec#d3mh@Rr=;k2@4*k98G#1KW}4O-TD7LutChxr^=V|f7VPr^FBY6>&;-KF zpr2KX;NsKFz(@6U+$G{wdU^y#cTxEX|0fsfms`-MvR1>y@r)C|_R&Fh-Do@l(Ta8| zN0u5JYRrai+;F${SY(!OKqSw+HcKb7^?3ru_AE)8lA0^uRYkxvv745`K3t1%94Ah+ z5y9#c%fTEfrEV#J^smSvy^TsD;<*LBMcRe-=n`A$m3t@Gf|Coe^9bI5@2q#ybdWAV zP=0f?)*HcrwnFsRQW6e9s7;Yn8%e7k?_idJzo4R37Qa9d=pVhDlGvpu~ z#aOL|hc9z2#g$cy?Df!lg$%poaz?>nhbbLS0;v4>-E!OqFZ$DQ7o1bUS5e?WV@~@& zj3rXX?N-N;wiEp?7-O?$g6t7)=Ww}{fc~kZn0&OWR1`}SI@eFGdz#Jtfmv`~Iz5m@ zC|dGI_FjkB)j^>jTkq&xZ3-igeXay2jT^7bcMYK5KA2nZ3LMZv&a8pynHuzQ6r+(g zaz-?emAW3TDBm3t*bbS!CEL8_CWHf0xtHiD_M58cQl^9slM15SfU~T?` z|0nW)x}+@4~8>@t7)7$>TF611`FQIazIOS->eYLnlPgV{*+5 zx_bu5Lf0BXVSMD6!1NMuy=vbS7Ch&mA+2N(L?HJNEMDXuMcG|U5YqnCqQwAK0gRsA zxtOIU>W1|61~u``&q*i~_4>rR05Yjt6ev?Rx-)g5@=6J0*583!;ttVHd|}!GxuPp7!yM=*xJy|;IzA~ zExPTxCRpNQ9x2}@(Bthuze)4QKKFP>mUzr1S0yD?wFHpWmIZ)1riHU$PFq%1TMnG; zpf;{;&H8JNo(0<0y&lW|qQEpJ>E&3h_i8<5tiT5Dzok`MHS_cWrnA$1+%L%8@>GL< zF-#>a&g#5$Ek$efzVV8F#rmD0Zqm$ghk~|~Rd7!9KsBYy;=&%HY! zGNa=ZN#+$5(imM>m=E^kXa>&oWkkpEnLLD9YAJ9Ts?Y+^+B7w-RVwZ6t4YAvQUHU# zj5LvD(kTMvyf*L%4CpDY$Xd?hpE5+)qZW2uj`ZdOM`m+TGp?lQ90$=@I8MT0z>>EH zgI&)7^OH3&1~mcRK?Ic!ip3N)I0$kmAqgjR5)5n~5Ap~^000R+7XUMfA>%MRl8X*< z<~W0L9A{KOHPa|50s!h83kUdfeS!CAp5(DmP^Vl;`?Z1j^=_0zhZ~>&b=dr3j26|Y zM~jQNfq{=m->k659lmM0rRqg@B5>B%*Bda{bv&?u$b(z|V1qGsh5=H8FL?8p?AvE-t3%ZbBRY@vqY8<2F8t@^5>Fjk?#*yGTQ0;iVEodO_ zF`J>Gw=Yfvrv-_Tl+6fA3pQ$lWx*O&lj5{gBt^0JH$^-fBO*j5tym-m&rfK=cGyx1 zVr`c2rLKfi#k9pc9F?iUE-8tF0W5_h-YZR;j+2fkM;2+2SC137qdFOy2xF?M=0dT# zH>z=cw-<0j$5P|t?y>Xk)cw?W<7cA*7jTN}kJ#e1ad#1R&JX#W!;02@0toNaBpH9B6RX|U?B?v(bF`_kaNh7I_X#}XBXRSo-Eu4tw97Ljt>Zw5k&0v z&~zdMkHjUiNLdhbH7ZW{9#JQOXH#EcJB+6D4O%OtL(}I3sKLT*VFnwGZ#Gy|vzibs z@AQc))*XhR;~Y&vTA!~E;p9jmxZT}(54WADyPX-^kQIpa`1=z=U1I{HbHN8d7`sNX5iu>KM)WFb(+EO8b&wdtTv>r#}meaa%`KACdubh?D1 zDS`&aMEd*gv&4SGqTTo+MSo2{6D*6IOnc1`1f00Q(jJZBf%^Tp3o8l?Z*18HI4Ff` za|8leH!-^c16hg{GG=5&CbM%Tg$kH}C4s5Z7FtIcA4^s>BA7%))3dUgMgKdL>dyObM&>0OhKnMW}st3ShLHvqCE&34s=(76wA2JsAmYaeP8fmx3gTd<$o*x3c*AxkkF(ofE#mW~A z#{2^ZsN$V|&7a^?T1G7Pgg-Y~?211qkns~0t@DVGXj%j%D1__@iIQtWO52hE4#fjNFl$mY zdM;u(RKZK*4>{m$XdRiKW-DkBW?RqOcnI=3Rz7o?Gt_uvSGa>|=f&IiDaXeKm{Hom zFsHZ@jhMTF=W_{tG*)ER7R8J&IrPYA0`b6;GG75KUC@(ih!*$rNFGhUSpDK?Lu$_g zSSs+9os7OdF=z^SS<~t?CvwLx3`<$5!?BzwbXG30y!I4=wM-H&0;E|pn-WgL8w|9< zL71W-P!6;ZG%9_X+-? zL4+0PEDoY)xh6@mHz=NJ%u_vrvntDyJQ}9|85OHh$z$N?b%L1L0}cRv4|o8cT3o0Dp8C||a$p}h2x!vZI) zo#hz&wEe~nJ8MT;2c!WiEQM}xlArDkM5e$fQ-V=Y8%Be7G(|(uMYQw)T%mI3R<%!$ z;8yL_YZ5A(L2egtBW}voKvg(Uxx$s%9=CXhOq9%i(DdxSabi7_4PeotB$*}r6^)|ReiEi?O#92%ZaCCtMxt$ExVx|3;pazso|1Ra@lCP z5t4crX-8$9$o6&$Azg(K%^cXmO}StA)fv#D`npdK5|GH*Pr0dhA4cD>sj}b`(fWZ~ zwiA%>IJyTPadPte(CP~fA#F>SdX_Cs2%$ah>d>mrCY=}!7MBE&(T~Cs@?EIe*r*3S z{WaILgW|fCS~LV9976MJCNF28-c8*mx5=o}S$3dS4`MnPw8I$NB5MJW2_*deCDAH8a(Nf$tR7bXQIqeCQIy9L9L(o3gp5JS9JR%tgU21Q%cp? zIurK8x5>`>)nxHHc|`N(aM=fbtrZ4bl6wf0@@lV<%%EXugi>O_Dy21k?;8IE_GIIIPe@AYfy|%de5ilh;zn}>eXCy@= z2f3vT;~-0PYT$P5ve{BT)p~B$+h&du86(H2JsdR{@st|G}M#32g zcA9O99>dF`8;ZMbS^m~rl~rzY_xba{jEo(Fjt@%23WFk1uN3j8_yk2nVmPIVhh!xO zc!xqP89W~J$*a6Ao>$%RL-Sm#If!3hv0tbVmgB>Ma*&6qd4*ugo{wbLezX}@goC%F zB%&?6t+co;|5OqB*H4(5OI5dYbMKD{A!3;EEGXBH z+0n;9YFlk@uVbcnm7<`ooD?meyp7^Js(F=1^$3)&!VY=07`+=Z#R1l$J!NTrS3%XcM+Mv}WPbB%w+3;qU=YR?bH zye&=~7X)Q>HIxJjIfd{4VupcA3j4TC$TR~*h{7$2w+v$jSwI>m=U*LQ*^k(5Dqzkv zt4h_jYMzG#t=b1B;e`)qK^h^62_Gtcco;~CIJAO}BKN!(Q z{3ZHQMc)fg#6Q%lPpNCGMyF}-bniT0U;504RZ^YT^*H^{A;CZEZRUs=`Ss0zDZcI;eAb{KgL_#bA*E6Tm{hk76+K)f|w6Sb{zSIcr zp7O6)@`$-~kEip)-adW0?T^rCh8N0y|Dk`_;P8fzuRJ?HxucMG;(CaA70~h91Ygd}5aSPvKMk>}~%5pNTu(Px#+l@NKI<@XiIHpbZ~@^WS4Z zh0nq1wKY2C+XaZ9Ke&G5;}`wtr;`%`dzhO0+XgJ@VLsvmR)0Ze+gcw#MeWVhnylC4 z_YV||OnqCn^dtYb*yW4l0sJX^94|`q;hzVV8`HyrFntCWrnwQV#jc^NjT1u&FU)~? zkPA&y&;_SSG=zXy#?3jU)oh*x7!ZeSr<}=Yt#)sOjzNEKxLfZSJo(r_MN`+@ev-iB z8ZxaN?Cb00%0X&t+{4V8%~qP>NK!MG8>C}V*~fKK^MQTCvBg-pZ+X|P%Y7~DwOrj( z=6JR-S87>J1WV24w+}M;d@K=rJ~{a#nW&)VD)_v3lXM$w?Zg!K@lZk)NDfa%N(F+FPeAi>^oxaygakzu5)gn#iKth~1jkBb5?nGG z3c)g17sfW6iyw7#8S@q3WYPb$Si%*DG&tf_y~q}nlVXAKRf&KoFFC% z6>?C%328ED6%EP8L7mr)$qT3=-ld|;3mni`{)L>$lVIuHSG_ z0V}J>3gpFdb$Rj2K9n^ZqV;3DK~I)IGu*yr ztxgh`eB2ObyUg0$tx&+rRsB3@ZnT_ALETaiB8NC7L&Z#{3pf?S6u9mwCD|YgH+gG$ z>M?nMtnh-t;9b9UYf=)q!Ma?vSwxH4UlIdYS4BwS?r43C9GK0T+?NQrmAqb=K67M`~+l zeyOV2LEFil21G@_BI-w-&Rw8eEm3d;Xe2>i_U5OA2rZUx_D83LoKi;R{dxncNzz6sAN|N(7NXEEOwW|2uRFYWFAQFIZU7dAOjQWY=mrgOmNC= zVSard_~qU7T>dqtiF@;V8y$kbLvkj4{XzMu*f3^Ds?npD~LerU;% zjYYj0V~|w%e8agNYC6D!Lc`Wf8Ia<>!PBa43a9@4=hZ+_+S` z%4I$*t!W4i0ZkA=E6jR!Vd#L^AK{i;Ex%mGQ`^-L4QcdJ;Q*?5bM%*(1sP(@1ZXHA zZug#dQ`73zq`~06L(ZjaT)`fg%{tX{bJV<;!st+(e0y-HC_{rOt%zz37b{)$$kS)J zN*dQf#yGEs;;Tg<7xN&r(dciaC>pQwC_B1tz%QWxUh8oveP zY~JRAg=GZ`H>WT53lXgO2f=l&T_*?jgn1=A7z+H^cX3l$pe?OHl&Hr{Zx0p+{~R#% zu;SrRfZR>s(1U|9lt(qMx+4y59*BGTEr}}Gg~dXHy~Syw+(Y4E&$`RDgql~m>zr=} ztzE^c{uel%u9P1+@R`@E263;LnBWq&$+BVc_EG=BI3jqnhXaiJ#ZsELf9u+USsvY& z5(RJ;FE3)zxKHsyQAsiiXgTKP4age2;ecE(nHO4+UcRmWSzfFjPhTrqi}~nddCvxv z^L!EG&29VSS-%B(Ol`l~zXZu6(j(F$WZ;0e0^hnAXG;?f_f9u>;d=ac4Se2OpA>t&i)cgLlPr=FDa=~()YAt?!b)NNe3z3+zpFU!mG zxFWn5H_%mzxUD;6aG&g;4;B6D_V}R*YaYfcCd}v`j_l2Qc4shHSrN}KkRP1<)Q0TL z`5UgG_$kZ|Jy@|Z`|XTuG=P+_(l^OI*#Ft*sk_>-^>+NuQtnUO1Ld@>t}Nw$2Em5# zv-qr(qqHWjL8Xk2D~=+V0SY1cEbLW#T_R&af!cHTe(sCc{RH5*_v1Mbb17~OJZf$=AJOHN5(@ziW+C)n`yH?b!z`JDm;5;;QtHlvc{4x^_d4 z+;4}rX^O1sf7b~%`qsmKoq^`HIt=;h78Rj>%c|C0n~5mcPbC$t;djx``wQ!=d`~NW zu>a@wu5+l_8C6?h<$tL5mVl*d*|$)JMyc0@a?407&Vcmi^Y%NVm#;t#YZ;uIKI9mu zwI$fiQZgS)Gi7uwv`UT4qb@%z@o{hRKlx{J_kFj&mtc;v z>~C4Y#~Pe8M8CfUqm|PpJ*wpovPUa<&$~X|{hWseVi1Jcb8mdT8WP$?t3GEpPKG8c z2Ey(EvU2(12dy#ho|gsSg3E4lVDLH8PIgj zr}O0>&0Z;EfTWP(gP@TEcPJ0RRAsVu^YH literal 13068 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

e}-wIw&OMNDf;Y`8XQ&b$2Ir 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 diff --git a/dashboard/src/components/shared/StorageCleanupPanel.vue b/dashboard/src/components/shared/StorageCleanupPanel.vue new file mode 100644 index 0000000000..84aeaeb057 --- /dev/null +++ b/dashboard/src/components/shared/StorageCleanupPanel.vue @@ -0,0 +1,241 @@ + + + + + diff --git a/dashboard/src/i18n/locales/en-US/features/settings.json b/dashboard/src/i18n/locales/en-US/features/settings.json index 19232125f9..b497659ee4 100644 --- a/dashboard/src/i18n/locales/en-US/features/settings.json +++ b/dashboard/src/i18n/locales/en-US/features/settings.json @@ -42,6 +42,40 @@ "title": "Backup & Restore", "subtitle": "Export or import all AstrBot data for easy migration to a new server", "button": "Backup Manager" + }, + "cleanup": { + "title": "Log & Cache Cleanup", + "subtitle": "Review disk usage for logs and caches, then clean them from the UI without shell commands.", + "refresh": "Refresh Usage", + "cleanAll": "Clean All", + "panel": { + "title": "Cleanup Details", + "subtitle": "Current usage: {size}" + }, + "fileCount": "{count} files", + "confirm": "Clean {target} now?", + "targetNames": { + "cache": "cache", + "logs": "logs", + "all": "logs and cache" + }, + "targets": { + "cache": { + "title": "Cache", + "subtitle": "Remove temporary files, plugin market cache, and skill cache.", + "button": "Clean Cache" + }, + "logs": { + "title": "Logs", + "subtitle": "Remove rotated logs and truncate the current active log files.", + "button": "Clean Logs" + } + }, + "messages": { + "statusFailed": "Failed to load storage usage", + "cleanupSuccess": "Cleared {count} files and freed {size}", + "cleanupFailed": "Cleanup failed" + } } }, "sidebar": { diff --git a/dashboard/src/i18n/locales/ru-RU/features/settings.json b/dashboard/src/i18n/locales/ru-RU/features/settings.json index 29b826fffe..d1100435f4 100644 --- a/dashboard/src/i18n/locales/ru-RU/features/settings.json +++ b/dashboard/src/i18n/locales/ru-RU/features/settings.json @@ -42,6 +42,40 @@ "title": "Резервное копирование", "subtitle": "Важнейший инструмент для безопасного переноса данных между серверами.", "button": "Управление бэкапами" + }, + "cleanup": { + "title": "Очистка логов и кэша", + "subtitle": "Показывает текущий размер логов и кэша и позволяет очистить их прямо из WebUI.", + "refresh": "Обновить", + "cleanAll": "Очистить все", + "panel": { + "title": "Детали очистки", + "subtitle": "Текущий размер: {size}" + }, + "fileCount": "{count} файлов", + "confirm": "Очистить {target}?", + "targetNames": { + "cache": "кэш", + "logs": "логи", + "all": "логи и кэш" + }, + "targets": { + "cache": { + "title": "Кэш", + "subtitle": "Удаляет временные файлы, кэш каталога плагинов и кэш навыков.", + "button": "Очистить кэш" + }, + "logs": { + "title": "Логи", + "subtitle": "Удаляет старые файлы логов и очищает текущие активные логи.", + "button": "Очистить логи" + } + }, + "messages": { + "statusFailed": "Не удалось получить размер хранилища", + "cleanupSuccess": "Очищено {count} файлов, освобождено {size}", + "cleanupFailed": "Ошибка очистки" + } } }, "sidebar": { @@ -177,4 +211,4 @@ "copyFailed": "Ошибка копирования" } } -} \ No newline at end of file +} diff --git a/dashboard/src/i18n/locales/zh-CN/features/settings.json b/dashboard/src/i18n/locales/zh-CN/features/settings.json index 19c1c7c41e..092b4a5d9b 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/settings.json +++ b/dashboard/src/i18n/locales/zh-CN/features/settings.json @@ -42,6 +42,40 @@ "title": "数据备份与恢复", "subtitle": "导出或导入 AstrBot 的所有数据,方便迁移到新服务器", "button": "备份管理" + }, + "cleanup": { + "title": "日志与缓存清理", + "subtitle": "查看当前日志和缓存占用,并一键清理。", + "refresh": "刷新占用", + "cleanAll": "全部清理", + "panel": { + "title": "清理详情", + "subtitle": "当前占用 {size}" + }, + "fileCount": "{count} 个文件", + "confirm": "确定要清理 {target} 吗?", + "targetNames": { + "cache": "缓存", + "logs": "日志", + "all": "日志和缓存" + }, + "targets": { + "cache": { + "title": "缓存", + "subtitle": "清理临时文件、插件市场缓存和技能缓存。", + "button": "清理缓存" + }, + "logs": { + "title": "日志", + "subtitle": "清理历史日志,并清空当前正在写入的日志文件内容。", + "button": "清理日志" + } + }, + "messages": { + "statusFailed": "加载存储占用失败", + "cleanupSuccess": "已清理 {count} 个文件,释放 {size}", + "cleanupFailed": "清理失败" + } } }, "sidebar": { diff --git a/dashboard/src/views/Settings.vue b/dashboard/src/views/Settings.vue index 8ec447dac2..9ddb4e1fec 100644 --- a/dashboard/src/views/Settings.vue +++ b/dashboard/src/views/Settings.vue @@ -1,6 +1,6 @@