diff --git a/components/game-server-nodes/GameServerNodeRow.vue b/components/game-server-nodes/GameServerNodeRow.vue index 3494623ce..a4710eccc 100644 --- a/components/game-server-nodes/GameServerNodeRow.vue +++ b/components/game-server-nodes/GameServerNodeRow.vue @@ -28,6 +28,16 @@ import { DialogHeader, DialogTitle, } from "~/components/ui/dialog"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "~/components/ui/alert-dialog"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { @@ -983,7 +993,7 @@ const isSectionExpanded = (section: string) => { @@ -1218,7 +1228,7 @@ const isSectionExpanded = (section: string) => { @@ -1880,6 +1890,32 @@ const isSectionExpanded = (section: string) => { + + + + + + {{ + $t("game_server.remove_node_confirm_title", { + node: gameServerNode.label || gameServerNode.id, + }) + }} + + + {{ $t("game_server.remove_node_confirm_description") }} + + + + + {{ $t("common.cancel") }} + + + {{ $t("game_server.remove_node") }} + + + + + ; server_regions: ServerRegion[]; removingNode: boolean; + showRemoveNodeDialog: boolean; } export default defineComponent({ @@ -2058,6 +2095,7 @@ export default defineComponent({ editCs2OptionsSheet: false, server_regions: [], removingNode: false, + showRemoveNodeDialog: false, pinBuildIdForm: useForm({ validationSchema: toTypedSchema( z.object({ diff --git a/i18n/locales/ar_SA.json b/i18n/locales/ar_SA.json index 98c09c180..42da67115 100644 --- a/i18n/locales/ar_SA.json +++ b/i18n/locales/ar_SA.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "المساحة المطلوبة - تثبيت موجود (GB)", "reserved_disk_space_existing_gb_description": "الحد الأدنى لمساحة القرص بالـ GB المطلوبة عندما يكون CS2 مثبتًا بالفعل على العقدة. اضبطها على 0 للتعطيل.", "update_cpu_pinning": "تم تحديث تثبيت CPU", + "danger_zone_title": "منطقة الخطر", + "danger_zone_description": "تحذف هذه الإجراءات كائنات من عنقود Kubernetes لديك، ولا يمكن التراجع عنها.", + "cleanup_nodes_title": "تنظيف العقد المحذوفة", + "cleanup_nodes_description": "يحذف ما لا يزال Kubernetes يحتفظ به لعقد سيرفرات اللعب التي حُذفت من اللوحة: مهام التحديث والبث والتحقق من gamedata الخاصة بها، وطلبات وحدات التخزين، وإدخالات وحدات التخزين، وإدخالات العقد. تبقى الملفات الموجودة على أقراص العقد كما هي. يتم تخطي العقد التي أبلغ Kubernetes عن حالتها بأنها «Ready» خلال آخر 10 دقائق، وكذلك سيرفر اللوحة نفسه. يعمل التنظيف نفسه تلقائيًا أيضًا عند حذف عقدة؛ استخدم هذا الإجراء لكل ما لم يتمكن التنظيف التلقائي من إكماله، مثل عمليات حذف تمت قبل هذا التحديث.", + "cleanup_nodes_button": "نظّف", + "cleanup_nodes_running": "جارٍ التنظيف...", + "cleanup_nodes_dialog_title": "هل تريد تنظيف العقد المحذوفة؟", + "cleanup_nodes_dialog_description": "يحذف هذا مهام التحديث والبث والتحقق من gamedata وطلبات وحدات التخزين وإدخالات وحدات التخزين وإدخالات العقد التي تركتها عقد سيرفرات اللعب المحذوفة. تبقى الملفات الموجودة على أقراص العقد كما هي. يتم تخطي العقد التي أبلغ Kubernetes عن حالتها بأنها «Ready» خلال آخر 10 دقائق، وكذلك سيرفر اللوحة نفسه.", + "cleanup_nodes_nothing": "لا يوجد ما يمكن تنظيفه", + "cleanup_nodes_done": "اكتمل التنظيف", + "cleanup_nodes_done_with_errors": "اكتمل التنظيف مع وجود أخطاء", + "cleanup_nodes_counts": "إدخالات العقد: {nodes}، إدخالات وحدات التخزين: {volumes}، طلبات وحدات التخزين: {volume_claims}، المهام: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "تم الاحتفاظ بإدخالات العقد مع طلبات وحدات التخزين وإدخالات وحدات التخزين الخاصة بها لأن API الخاص باللوحة لا يملك صلاحية حذف عقد Kubernetes. اسحب التحديثات وشغّل سكربت تحديث اللوحة (git pull && ./update.sh)، ثم شغّل هذا الإجراء مرة أخرى.", + "cleanup_nodes_failed_count": "كائنات تعذّر حذفها: {failed}. راجع سجلات API.", + "cleanup_nodes_recently_ready": "العقد المحذوفة التي تم تخطيها لأن Kubernetes أبلغ عن حالتها بأنها «Ready» خلال آخر 10 دقائق: {recently_ready}. شغّل هذا الإجراء مرة أخرى بعد مرور 10 دقائق على توقف Kubernetes عن الإبلاغ عنها بأنها «Ready».", + "cleanup_nodes_failed": "فشل التنظيف", "player_sessions_section": "جلسات لاعبي سيرفرات المجتمع", "player_sessions_description": "تسجّل سيرفرات المجتمع التي تشغّل إضافة Player Management من لعب ومتى ومن أي عنوان IP.", "player_session_retention_days": "الاحتفاظ بسجل الجلسات (أيام)", @@ -8508,6 +8524,8 @@ "select_region": "اختر المنطقة", "no_region": "غير مخصصة", "remove_node": "احذف العقدة", + "remove_node_confirm_title": "هل تريد حذف {node}؟", + "remove_node_confirm_description": "يؤدي هذا إلى حذف العقدة من اللوحة. بعد مرور 10 دقائق على توقف Kubernetes عن الإبلاغ عنها بأنها «Ready»، تُحذف أيضًا مهام التحديث والبث والتحقق من gamedata الخاصة بها وطلبات وحدات التخزين وإدخالات وحدات التخزين وإدخال العقدة الخاص بها من Kubernetes. تبقى الملفات الموجودة على قرصها كما هي. العقدة التي لا تزال تعمل تضيف نفسها مرة أخرى خلال دقيقة، لكنها تعود معطّلة وبإعدادات مُعادة إلى الوضع الافتراضي؛ احذفها مرة أخرى بعد توقفها. إذا لم تُضف نفسها مرة أخرى وظل Kubernetes يُبلغ عنها بأنها «Ready» بعد 10 دقائق من الحذف، فشغّل «تنظيف العقد المحذوفة» في إعدادات السيرفر بعد مرور 10 دقائق على توقف Kubernetes عن الإبلاغ عنها بأنها «Ready».", "remove_ports": "احذف المنافذ", "mode": { "use_as_match": "استخدمها كسيرفر مباريات", diff --git a/i18n/locales/da_DK.json b/i18n/locales/da_DK.json index fa8c981b6..0c4792517 100644 --- a/i18n/locales/da_DK.json +++ b/i18n/locales/da_DK.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Krævet diskplads - eksisterende installation (GB)", "reserved_disk_space_existing_gb_description": "Minimum diskplads i GB krævet, når CS2 allerede er installeret på noden. Sæt til 0 for at slå fra.", "update_cpu_pinning": "CPU-pinning opdateret", + "danger_zone_title": "Farezone", + "danger_zone_description": "Disse handlinger sletter objekter fra dit Kubernetes-cluster og kan ikke fortrydes.", + "cleanup_nodes_title": "Ryd op i fjernede noder", + "cleanup_nodes_description": "Sletter det, Kubernetes stadig gemmer for spilserver-noder, der er fjernet fra panelet: deres opdaterings-, streaming- og gamedata-valideringsjobs, volume claims, volume-poster og node-poster. Filer på nodernes diske bevares. Noder, som Kubernetes har rapporteret som «Ready» inden for de sidste 10 minutter, springes over, og det samme gør panelets egen server. Den samme oprydning kører også automatisk, når en node fjernes. Brug dette til alt, som den automatiske oprydning ikke kunne gøre færdig, for eksempel fjernelser fra før denne opdatering.", + "cleanup_nodes_button": "Ryd op", + "cleanup_nodes_running": "Rydder op...", + "cleanup_nodes_dialog_title": "Ryd op i fjernede noder?", + "cleanup_nodes_dialog_description": "Dette sletter de opdaterings-, streaming- og gamedata-valideringsjobs, volume claims, volume-poster og node-poster, som fjernede spilserver-noder har efterladt. Filer på nodernes diske bevares. Noder, som Kubernetes har rapporteret som «Ready» inden for de sidste 10 minutter, springes over, og det samme gør panelets egen server.", + "cleanup_nodes_nothing": "Intet at rydde op i", + "cleanup_nodes_done": "Oprydningen er færdig", + "cleanup_nodes_done_with_errors": "Oprydningen er færdig, men med fejl", + "cleanup_nodes_counts": "Node-poster: {nodes}, volume-poster: {volumes}, volume claims: {volume_claims}, jobs: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Node-poster blev bevaret sammen med deres volume claims og volume-poster, fordi API'et ikke har tilladelse til at slette Kubernetes-noder. Hent og kør panelets opdateringsscript (git pull && ./update.sh), og kør derefter dette igen.", + "cleanup_nodes_failed_count": "Objekter, der ikke kunne fjernes: {failed}. Tjek API'ets logfiler.", + "cleanup_nodes_recently_ready": "Fjernede noder, der blev sprunget over, fordi Kubernetes har rapporteret dem som «Ready» inden for de sidste 10 minutter: {recently_ready}. Kør dette igen, når Kubernetes ikke har rapporteret dem som «Ready» i 10 minutter.", + "cleanup_nodes_failed": "Oprydningen mislykkedes", "player_sessions_section": "Spillersessioner på Community-Servere", "player_sessions_description": "Community-servere med Player Management-pluginnet registrerer, hvem der spillede, hvornår og fra hvilken IP.", "player_session_retention_days": "Gem Sessionshistorik (dage)", @@ -8508,6 +8524,8 @@ "select_region": "Vælg region", "no_region": "Ikke tildelt", "remove_node": "Fjern node", + "remove_node_confirm_title": "Fjern {node}?", + "remove_node_confirm_description": "Dette fjerner noden fra panelet. Når Kubernetes ikke har rapporteret den som «Ready» i 10 minutter, slettes dens opdaterings-, streaming- og gamedata-valideringsjobs, volume claims, volume-poster og node-post også fra Kubernetes. Filer på dens disk bevares. En node, der stadig kører, tilføjer sig selv igen inden for et minut, men deaktiveret og med nulstillede indstillinger; fjern den igen, når den er nede. Hvis den ikke kommer tilbage, men Kubernetes stadig rapporterer den som «Ready» 10 minutter efter fjernelsen, så kør «Ryd op i fjernede noder» i Serverindstillinger, når Kubernetes ikke har rapporteret den som «Ready» i 10 minutter.", "remove_ports": "Fjern porte", "mode": { "use_as_match": "Brug som kampserver", diff --git a/i18n/locales/de_DE.json b/i18n/locales/de_DE.json index 5342466a6..40918167c 100644 --- a/i18n/locales/de_DE.json +++ b/i18n/locales/de_DE.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Benötigter Speicherplatz – Bestehende Installation (GB)", "reserved_disk_space_existing_gb_description": "Mindestens benötigter Speicherplatz in GB, wenn CS2 auf dem Node bereits installiert ist. 0 deaktiviert die Prüfung.", "update_cpu_pinning": "CPU-Pinning aktualisiert", + "danger_zone_title": "Gefahrenzone", + "danger_zone_description": "Diese Aktionen löschen Objekte aus deinem Kubernetes-Cluster. Das lässt sich nicht rückgängig machen.", + "cleanup_nodes_title": "Entfernte Nodes bereinigen", + "cleanup_nodes_description": "Löscht, was Kubernetes für aus dem Panel entfernte Game-Server-Nodes noch vorhält: ihre Update-, Streamer- und Gamedata-Prüfungs-Jobs, Volume-Claims, Volume-Einträge und Node-Einträge. Dateien auf den Festplatten der Nodes bleiben erhalten. Nodes, die Kubernetes in den letzten 10 Minuten als „Ready“ gemeldet hat, werden übersprungen, ebenso der Server, auf dem das Panel selbst läuft. Dieselbe Bereinigung läuft auch automatisch, wenn ein Node entfernt wird. Nutze sie hier für alles, was die automatische Bereinigung nicht abschließen konnte, zum Beispiel für Nodes, die vor diesem Update entfernt wurden.", + "cleanup_nodes_button": "Bereinigen", + "cleanup_nodes_running": "Wird bereinigt …", + "cleanup_nodes_dialog_title": "Entfernte Nodes bereinigen?", + "cleanup_nodes_dialog_description": "Das löscht die Update-, Streamer- und Gamedata-Prüfungs-Jobs, Volume-Claims, Volume-Einträge und Node-Einträge, die entfernte Game-Server-Nodes hinterlassen haben. Dateien auf den Festplatten der Nodes bleiben erhalten. Nodes, die Kubernetes in den letzten 10 Minuten als „Ready“ gemeldet hat, werden übersprungen, ebenso der Server, auf dem das Panel selbst läuft.", + "cleanup_nodes_nothing": "Nichts zu bereinigen", + "cleanup_nodes_done": "Bereinigung abgeschlossen", + "cleanup_nodes_done_with_errors": "Bereinigung mit Fehlern abgeschlossen", + "cleanup_nodes_counts": "Node-Einträge: {nodes}, Volume-Einträge: {volumes}, Volume-Claims: {volume_claims}, Jobs: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Node-Einträge wurden samt ihren Volume-Claims und Volume-Einträgen behalten, weil die API keine Berechtigung hat, Kubernetes-Nodes zu löschen. Führe das Update-Skript des Panels aus (git pull && ./update.sh) und starte danach diese Bereinigung erneut.", + "cleanup_nodes_failed_count": "Objekte, die nicht entfernt werden konnten: {failed}. Sieh in den API-Logs nach.", + "cleanup_nodes_recently_ready": "Entfernte Nodes, die übersprungen wurden, weil Kubernetes sie in den letzten 10 Minuten als „Ready“ gemeldet hat: {recently_ready}. Führe das erneut aus, sobald Kubernetes sie seit 10 Minuten nicht mehr als „Ready“ meldet.", + "cleanup_nodes_failed": "Bereinigung fehlgeschlagen", "player_sessions_section": "Community-Spielersitzungen", "player_sessions_description": "Community-Server mit dem Player-Management-Plugin erfassen, wer wann und von welcher IP gespielt hat.", "player_session_retention_days": "Sitzungsverlauf Aufbewahren (Tage)", @@ -8508,6 +8524,8 @@ "select_region": "Region wählen", "no_region": "Nicht zugewiesen", "remove_node": "Node entfernen", + "remove_node_confirm_title": "{node} entfernen?", + "remove_node_confirm_description": "Das entfernt den Node aus dem Panel. Sobald Kubernetes ihn seit 10 Minuten nicht mehr als „Ready“ meldet, werden auch seine Update-, Streamer- und Gamedata-Prüfungs-Jobs, Volume-Claims, Volume-Einträge und sein Node-Eintrag aus Kubernetes gelöscht. Dateien auf seiner Festplatte bleiben erhalten. Ein Node, der noch läuft, fügt sich innerhalb einer Minute wieder hinzu, allerdings deaktiviert und mit zurückgesetzten Einstellungen; entferne ihn erneut, sobald er nicht mehr läuft. Wenn er nicht zurückkommt, Kubernetes ihn aber 10 Minuten nach dem Entfernen noch als „Ready“ meldet, führe „Entfernte Nodes bereinigen“ in den Server-Einstellungen aus, sobald Kubernetes ihn seit 10 Minuten nicht mehr als „Ready“ meldet.", "remove_ports": "Ports entfernen", "mode": { "use_as_match": "Als Match-Server nutzen", diff --git a/i18n/locales/en.json b/i18n/locales/en.json index ec791f43e..dfaa49f4c 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Required Disk Space - Existing Install (GB)", "reserved_disk_space_existing_gb_description": "Minimum disk space in GB required when CS2 is already installed on the node. Set to 0 to disable.", "update_cpu_pinning": "CPU Pinning updated", + "danger_zone_title": "Danger Zone", + "danger_zone_description": "These actions delete objects from your Kubernetes cluster and cannot be undone.", + "cleanup_nodes_title": "Clean Up Removed Nodes", + "cleanup_nodes_description": "Removes what Kubernetes still holds for game server nodes that were removed from the panel: their update, streamer and gamedata validation jobs, volume claims, volume records and node entries. Files on the nodes' disks are kept. Nodes that Kubernetes reported as Ready in the last 10 minutes are skipped, and so is the panel's own server. The same cleanup also runs automatically when a node is removed; use this for anything it could not finish, for example removals from before this update.", + "cleanup_nodes_button": "Clean Up", + "cleanup_nodes_running": "Cleaning up...", + "cleanup_nodes_dialog_title": "Clean up removed nodes?", + "cleanup_nodes_dialog_description": "This deletes the update, streamer and gamedata validation jobs, volume claims, volume records and node entries that removed game server nodes left behind. Files on the nodes' disks are kept. Nodes that Kubernetes reported as Ready in the last 10 minutes are skipped, and so is the panel's own server.", + "cleanup_nodes_nothing": "Nothing to clean up", + "cleanup_nodes_done": "Cleanup finished", + "cleanup_nodes_done_with_errors": "Cleanup finished with errors", + "cleanup_nodes_counts": "Node entries: {nodes}, volume records: {volumes}, volume claims: {volume_claims}, jobs: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Node entries, with their volume claims and volume records, were kept because the API has no permission to delete Kubernetes nodes. Pull and run the panel's update script (git pull && ./update.sh), then run this again.", + "cleanup_nodes_failed_count": "Objects that could not be removed: {failed}. Check the API logs.", + "cleanup_nodes_recently_ready": "Removed nodes skipped because Kubernetes reported them as Ready in the last 10 minutes: {recently_ready}. Run this again once Kubernetes has reported them as not ready for 10 minutes.", + "cleanup_nodes_failed": "Cleanup failed", "player_sessions_section": "Community Player Sessions", "player_sessions_description": "Community servers running the Player Management plugin record who played, when, and from which IP.", "player_session_retention_days": "Keep Session History (days)", @@ -8508,6 +8524,8 @@ "select_region": "Select Region", "no_region": "Unassigned", "remove_node": "Remove Node", + "remove_node_confirm_title": "Remove {node}?", + "remove_node_confirm_description": "This removes the node from the panel. Once Kubernetes has reported it as not ready for 10 minutes, its update, streamer and gamedata validation jobs, volume claims, volume records and node entry are deleted from Kubernetes too. Files on its disk are kept. A node that is still running adds itself back within a minute, but disabled and with its settings reset; remove it again once it is down. If it does not come back but Kubernetes still reports it as Ready 10 minutes after the removal, run Clean Up Removed Nodes in Server Settings once Kubernetes has reported it as not ready for 10 minutes.", "remove_ports": "Remove Ports", "mode": { "use_as_match": "Use as Match Server", diff --git a/i18n/locales/es_ES.json b/i18n/locales/es_ES.json index 20ece8047..70d25acd8 100644 --- a/i18n/locales/es_ES.json +++ b/i18n/locales/es_ES.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Espacio de disco necesario - Instalación existente (GB)", "reserved_disk_space_existing_gb_description": "Espacio de disco mínimo en GB necesario cuando CS2 ya está instalado en el nodo. Ponlo a 0 para desactivarlo.", "update_cpu_pinning": "Asignación de CPU actualizada", + "danger_zone_title": "Zona de peligro", + "danger_zone_description": "Estas acciones eliminan objetos de tu clúster de Kubernetes y no se pueden deshacer.", + "cleanup_nodes_title": "Limpiar nodos eliminados", + "cleanup_nodes_description": "Borra lo que Kubernetes aún guarda de los nodos de servidor de juego eliminados del panel: sus trabajos de actualización, de streaming y de validación del gamedata, solicitudes de volumen, registros de volumen y registros de nodo. Los archivos de los discos de los nodos se conservan. Se omiten los nodos que Kubernetes ha marcado como «Ready» en los últimos 10 minutos, así como el propio servidor del panel. La misma limpieza también se ejecuta automáticamente al eliminar un nodo; usa esta opción para lo que la limpieza automática no haya podido terminar, por ejemplo eliminaciones anteriores a esta actualización.", + "cleanup_nodes_button": "Limpiar", + "cleanup_nodes_running": "Limpiando...", + "cleanup_nodes_dialog_title": "¿Limpiar los nodos eliminados?", + "cleanup_nodes_dialog_description": "Esto elimina los trabajos de actualización, de streaming y de validación del gamedata, solicitudes de volumen, registros de volumen y registros de nodo que dejaron los nodos de servidor de juego eliminados. Los archivos de los discos de los nodos se conservan. Se omiten los nodos que Kubernetes ha marcado como «Ready» en los últimos 10 minutos, así como el propio servidor del panel.", + "cleanup_nodes_nothing": "No hay nada que limpiar", + "cleanup_nodes_done": "Limpieza terminada", + "cleanup_nodes_done_with_errors": "Limpieza terminada con errores", + "cleanup_nodes_counts": "Registros de nodo: {nodes}, registros de volumen: {volumes}, solicitudes de volumen: {volume_claims}, trabajos: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Se han conservado los registros de nodo, junto con sus solicitudes de volumen y registros de volumen, porque la API no tiene permiso para eliminar nodos de Kubernetes. Haz pull y ejecuta el script de actualización del panel (git pull && ./update.sh), y luego vuelve a ejecutar esto.", + "cleanup_nodes_failed_count": "Objetos que no se han podido eliminar: {failed}. Revisa los logs de la API.", + "cleanup_nodes_recently_ready": "Nodos eliminados que se han omitido porque Kubernetes los ha marcado como «Ready» en los últimos 10 minutos: {recently_ready}. Vuelve a ejecutar esto cuando Kubernetes lleve 10 minutos sin marcarlos como «Ready».", + "cleanup_nodes_failed": "La limpieza ha fallado", "player_sessions_section": "Sesiones de Jugadores de la Comunidad", "player_sessions_description": "Los servidores de la comunidad con el plugin Player Management registran quién jugó, cuándo y desde qué IP.", "player_session_retention_days": "Conservar Historial de Sesiones (días)", @@ -8508,6 +8524,8 @@ "select_region": "Selecciona una región", "no_region": "Sin asignar", "remove_node": "Eliminar nodo", + "remove_node_confirm_title": "¿Eliminar {node}?", + "remove_node_confirm_description": "Esto elimina el nodo del panel. Cuando Kubernetes lleve 10 minutos sin marcarlo como «Ready», también se borrarán de Kubernetes sus trabajos de actualización, de streaming y de validación del gamedata, solicitudes de volumen, registros de volumen y su registro de nodo. Los archivos de su disco se conservan. Un nodo que sigue en funcionamiento se vuelve a añadir en menos de un minuto, pero desactivado y con su configuración restablecida; vuelve a eliminarlo cuando esté apagado. Si no se vuelve a añadir, pero Kubernetes aún lo marca como «Ready» 10 minutos después de eliminarlo, usa «Limpiar nodos eliminados» en Ajustes del servidor cuando Kubernetes lleve 10 minutos sin marcarlo como «Ready».", "remove_ports": "Eliminar puertos", "mode": { "use_as_match": "Usar como servidor de partidas", diff --git a/i18n/locales/fr_FR.json b/i18n/locales/fr_FR.json index 9793c15d7..1476d1883 100644 --- a/i18n/locales/fr_FR.json +++ b/i18n/locales/fr_FR.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Espace disque requis - installation existante (Go)", "reserved_disk_space_existing_gb_description": "Espace disque minimum en Go requis quand CS2 est déjà installé sur le nœud. Mettre 0 pour désactiver.", "update_cpu_pinning": "Épinglage CPU mis à jour", + "danger_zone_title": "Zone de danger", + "danger_zone_description": "Ces actions suppriment des objets de ton cluster Kubernetes et sont irréversibles.", + "cleanup_nodes_title": "Nettoyer les nœuds supprimés", + "cleanup_nodes_description": "Efface ce que Kubernetes garde encore pour les nœuds de serveurs de jeu supprimés du panel : leurs jobs de mise à jour, de streaming et de validation des gamedata, leurs demandes de volume, leurs entrées de volume et leurs entrées de nœud. Les fichiers sur les disques des nœuds sont conservés. Les nœuds que Kubernetes a signalés comme « Ready » au cours des 10 dernières minutes sont ignorés, tout comme le serveur du panel lui-même. Le même nettoyage se lance aussi automatiquement à la suppression d'un nœud ; utilise ce bouton pour tout ce que le nettoyage automatique n'a pas pu terminer, par exemple des suppressions antérieures à cette mise à jour.", + "cleanup_nodes_button": "Nettoyer", + "cleanup_nodes_running": "Nettoyage en cours…", + "cleanup_nodes_dialog_title": "Nettoyer les nœuds supprimés ?", + "cleanup_nodes_dialog_description": "Cela supprime les jobs de mise à jour, de streaming et de validation des gamedata, demandes de volume, entrées de volume et entrées de nœud laissés par les nœuds de serveurs de jeu supprimés. Les fichiers sur les disques des nœuds sont conservés. Les nœuds que Kubernetes a signalés comme « Ready » au cours des 10 dernières minutes sont ignorés, tout comme le serveur du panel lui-même.", + "cleanup_nodes_nothing": "Rien à nettoyer", + "cleanup_nodes_done": "Nettoyage terminé", + "cleanup_nodes_done_with_errors": "Nettoyage terminé avec des erreurs", + "cleanup_nodes_counts": "Entrées de nœud : {nodes}, entrées de volume : {volumes}, demandes de volume : {volume_claims}, jobs : {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Les entrées de nœud ont été conservées, avec leurs demandes de volume et leurs entrées de volume, car l'API n'a pas le droit de supprimer les nœuds Kubernetes. Récupère et lance le script de mise à jour du panel (git pull && ./update.sh), puis relance cette action.", + "cleanup_nodes_failed_count": "Objets qui n'ont pas pu être supprimés : {failed}. Consulte les logs de l'API.", + "cleanup_nodes_recently_ready": "Nœuds supprimés ignorés, car Kubernetes les a signalés comme « Ready » au cours des 10 dernières minutes : {recently_ready}. Relance cette action quand Kubernetes ne les signale plus comme « Ready » depuis 10 minutes.", + "cleanup_nodes_failed": "Échec du nettoyage", "player_sessions_section": "Sessions des Joueurs Communautaires", "player_sessions_description": "Les serveurs communautaires équipés du plugin Player Management enregistrent qui a joué, quand et depuis quelle IP.", "player_session_retention_days": "Conserver l’Historique des Sessions (jours)", @@ -8508,6 +8524,8 @@ "select_region": "Sélectionner une région", "no_region": "Non attribué", "remove_node": "Supprimer le nœud", + "remove_node_confirm_title": "Supprimer {node} ?", + "remove_node_confirm_description": "Cela supprime le nœud du panel. Une fois que Kubernetes ne le signale plus comme « Ready » depuis 10 minutes, ses jobs de mise à jour, de streaming et de validation des gamedata, ses demandes de volume, ses entrées de volume et son entrée de nœud sont aussi supprimés de Kubernetes. Les fichiers sur son disque sont conservés. Un nœud qui tourne encore se rajoute de lui-même en moins d'une minute, mais désactivé et avec ses paramètres réinitialisés ; supprime-le à nouveau une fois qu'il est arrêté. S'il ne revient pas mais que Kubernetes le signale encore comme « Ready » 10 minutes après la suppression, lance « Nettoyer les nœuds supprimés » dans les Paramètres des serveurs une fois que Kubernetes ne le signale plus comme « Ready » depuis 10 minutes.", "remove_ports": "Supprimer les ports", "mode": { "use_as_match": "Utiliser comme serveur de match", diff --git a/i18n/locales/it_IT.json b/i18n/locales/it_IT.json index 0c89c03b2..f2b57f0da 100644 --- a/i18n/locales/it_IT.json +++ b/i18n/locales/it_IT.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Spazio disco richiesto - installazione esistente (GB)", "reserved_disk_space_existing_gb_description": "Spazio disco minimo in GB richiesto quando CS2 è già installato sul nodo. Imposta 0 per disattivare.", "update_cpu_pinning": "CPU pinning aggiornato", + "danger_zone_title": "Zona pericolosa", + "danger_zone_description": "Queste azioni eliminano oggetti dal tuo cluster Kubernetes e sono irreversibili.", + "cleanup_nodes_title": "Pulizia dei nodi rimossi", + "cleanup_nodes_description": "Elimina ciò che Kubernetes conserva ancora per i nodi server di gioco rimossi dal pannello: i loro job di aggiornamento, di streaming e di validazione gamedata, le richieste di volume, le voci dei volumi e le voci dei nodi. I file sui dischi dei nodi vengono mantenuti. I nodi che Kubernetes ha segnalato come «Ready» negli ultimi 10 minuti vengono saltati, così come il server su cui gira il pannello. La stessa pulizia viene eseguita automaticamente anche quando rimuovi un nodo; usa questa funzione per ciò che la pulizia automatica non è riuscita a completare, per esempio rimozioni precedenti a questo aggiornamento.", + "cleanup_nodes_button": "Pulisci", + "cleanup_nodes_running": "Pulizia in corso…", + "cleanup_nodes_dialog_title": "Pulire i nodi rimossi?", + "cleanup_nodes_dialog_description": "Questa operazione elimina i job di aggiornamento, di streaming e di validazione gamedata, le richieste di volume, le voci dei volumi e le voci dei nodi lasciati dai nodi server di gioco rimossi. I file sui dischi dei nodi vengono mantenuti. I nodi che Kubernetes ha segnalato come «Ready» negli ultimi 10 minuti vengono saltati, così come il server su cui gira il pannello.", + "cleanup_nodes_nothing": "Niente da pulire", + "cleanup_nodes_done": "Pulizia completata", + "cleanup_nodes_done_with_errors": "Pulizia completata con errori", + "cleanup_nodes_counts": "Voci dei nodi: {nodes}, voci dei volumi: {volumes}, richieste di volume: {volume_claims}, job: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Le voci dei nodi sono state mantenute, insieme alle loro richieste di volume e voci dei volumi, perché l'API non ha il permesso di eliminare i nodi Kubernetes. Esegui il pull e lo script di aggiornamento del pannello (git pull && ./update.sh), poi esegui di nuovo questa operazione.", + "cleanup_nodes_failed_count": "Oggetti che non è stato possibile rimuovere: {failed}. Controlla i log dell'API.", + "cleanup_nodes_recently_ready": "Nodi rimossi saltati perché Kubernetes li ha segnalati come «Ready» negli ultimi 10 minuti: {recently_ready}. Esegui di nuovo questa operazione quando Kubernetes non li segnala più come «Ready» da 10 minuti.", + "cleanup_nodes_failed": "Pulizia non riuscita", "player_sessions_section": "Sessioni dei Giocatori della Community", "player_sessions_description": "I server della community con il plugin Player Management registrano chi ha giocato, quando e da quale IP.", "player_session_retention_days": "Conserva lo Storico delle Sessioni (giorni)", @@ -8508,6 +8524,8 @@ "select_region": "Seleziona regione", "no_region": "Non assegnato", "remove_node": "Rimuovi nodo", + "remove_node_confirm_title": "Rimuovere {node}?", + "remove_node_confirm_description": "Questa operazione rimuove il nodo dal pannello. Quando Kubernetes non lo segnala più come «Ready» da 10 minuti, anche i suoi job di aggiornamento, di streaming e di validazione gamedata, le richieste di volume, le voci dei volumi e la sua voce del nodo vengono eliminati da Kubernetes. I file sul suo disco vengono mantenuti. Un nodo ancora in esecuzione si aggiunge di nuovo entro un minuto, ma disattivato e con le impostazioni predefinite; rimuovilo di nuovo quando è spento. Se non si aggiunge di nuovo ma Kubernetes lo segnala ancora come «Ready» 10 minuti dopo la rimozione, esegui «Pulizia dei nodi rimossi» in Impostazioni server quando Kubernetes non lo segnala più come «Ready» da 10 minuti.", "remove_ports": "Rimuovi porte", "mode": { "use_as_match": "Usa come server di match", diff --git a/i18n/locales/ja_JP.json b/i18n/locales/ja_JP.json index 4dab1f643..aae2c1989 100644 --- a/i18n/locales/ja_JP.json +++ b/i18n/locales/ja_JP.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "必要ディスク容量 - 既存インストール(GB)", "reserved_disk_space_existing_gb_description": "ノードに既にCS2がインストールされている場合に必要な最小ディスク容量(GB)です。0にすると無効になります。", "update_cpu_pinning": "CPUピニングを更新しました", + "danger_zone_title": "危険な操作", + "danger_zone_description": "これらの操作はKubernetesクラスターからオブジェクトを削除します。元に戻すことはできません。", + "cleanup_nodes_title": "削除済みノードのクリーンアップ", + "cleanup_nodes_description": "パネルから削除されたゲームサーバーノードについて、Kubernetesにまだ残っている更新ジョブ、ストリーミングジョブ、ゲームデータ検証ジョブ、ボリューム要求、ボリュームの登録情報、ノードの登録情報を削除します。ノードのディスク上のファイルは残ります。過去10分以内にKubernetesが「Ready」と報告したノードと、パネル自体が動作しているサーバーはスキップされます。ノードを削除したときにも同じクリーンアップが自動的に実行されます。自動クリーンアップで完了できなかったもの、たとえばこのアップデートより前の削除で残ったものには、これを使用してください。", + "cleanup_nodes_button": "クリーンアップ", + "cleanup_nodes_running": "クリーンアップ中...", + "cleanup_nodes_dialog_title": "削除済みノードをクリーンアップしますか?", + "cleanup_nodes_dialog_description": "削除されたゲームサーバーノードが残した更新ジョブ、ストリーミングジョブ、ゲームデータ検証ジョブ、ボリューム要求、ボリュームの登録情報、ノードの登録情報を削除します。ノードのディスク上のファイルは残ります。過去10分以内にKubernetesが「Ready」と報告したノードと、パネル自体が動作しているサーバーはスキップされます。", + "cleanup_nodes_nothing": "クリーンアップするものはありません", + "cleanup_nodes_done": "クリーンアップが完了しました", + "cleanup_nodes_done_with_errors": "クリーンアップは完了しましたが、エラーがありました", + "cleanup_nodes_counts": "ノードの登録情報: {nodes}、ボリュームの登録情報: {volumes}、ボリューム要求: {volume_claims}、ジョブ: {jobs}。", + "cleanup_nodes_node_delete_forbidden": "APIにKubernetesノードを削除する権限がないため、ノードの登録情報と、そのボリューム要求およびボリュームの登録情報は残されました。パネルのアップデートスクリプトを取得して実行してから(git pull && ./update.sh)、このクリーンアップをもう一度実行してください。", + "cleanup_nodes_failed_count": "削除できなかったオブジェクト: {failed}。APIのログを確認してください。", + "cleanup_nodes_recently_ready": "過去10分以内にKubernetesが「Ready」と報告したためスキップされた削除済みノード: {recently_ready}。Kubernetesが「Ready」と報告しなくなってから10分経過した後に、もう一度実行してください。", + "cleanup_nodes_failed": "クリーンアップに失敗しました", "player_sessions_section": "コミュニティのプレイヤーセッション", "player_sessions_description": "Player Managementプラグインを導入したコミュニティサーバーは、誰がいつどのIPからプレイしたかを記録します。", "player_session_retention_days": "セッション履歴の保存期間(日)", @@ -8508,6 +8524,8 @@ "select_region": "リージョンを選択", "no_region": "未割り当て", "remove_node": "ノードを削除", + "remove_node_confirm_title": "{node} を削除しますか?", + "remove_node_confirm_description": "パネルからこのノードを削除します。Kubernetesが「Ready」と報告しなくなってから10分経過すると、その更新ジョブ、ストリーミングジョブ、ゲームデータ検証ジョブ、ボリューム要求、ボリュームの登録情報、ノードの登録情報もKubernetesから削除されます。ディスク上のファイルは残ります。まだ動作しているノードは、1分以内に自動的に再追加されますが、無効な状態で、設定もリセットされます。その場合は、ノードが停止してからもう一度削除してください。再追加されないまま、削除から10分経ってもKubernetesが「Ready」と報告している場合は、Kubernetesが「Ready」と報告しなくなってから10分経過した後に、サーバー設定の「削除済みノードのクリーンアップ」を実行してください。", "remove_ports": "ポートを削除", "mode": { "use_as_match": "試合サーバーとして使用", diff --git a/i18n/locales/ko_KR.json b/i18n/locales/ko_KR.json index 32c8264bf..6fd1194c2 100644 --- a/i18n/locales/ko_KR.json +++ b/i18n/locales/ko_KR.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "필요 디스크 공간 - 기존 설치(GB)", "reserved_disk_space_existing_gb_description": "노드에 CS2가 이미 설치된 경우 필요한 최소 디스크 공간(GB)입니다. 비활성화하려면 0으로 설정하세요.", "update_cpu_pinning": "CPU 고정 설정이 업데이트되었습니다", + "danger_zone_title": "위험 구역", + "danger_zone_description": "이 작업은 Kubernetes 클러스터에서 객체를 삭제하며 되돌릴 수 없습니다.", + "cleanup_nodes_title": "제거된 노드 정리", + "cleanup_nodes_description": "패널에서 제거된 게임 서버 노드에 대해 Kubernetes에 아직 남아 있는 업데이트, 스트리밍 및 게임 데이터 검증 작업, 볼륨 요청, 볼륨 항목, 노드 항목을 삭제합니다. 노드 디스크의 파일은 유지됩니다. 최근 10분 이내에 Kubernetes가 「Ready」로 보고한 노드와 패널 자체가 실행되는 서버는 건너뜁니다. 노드를 제거할 때도 같은 정리가 자동으로 실행됩니다. 자동 정리가 끝내지 못한 항목에 이 기능을 사용하세요. 예를 들어 이번 업데이트 이전에 제거한 노드의 남은 항목이 여기에 해당합니다.", + "cleanup_nodes_button": "정리", + "cleanup_nodes_running": "정리 중...", + "cleanup_nodes_dialog_title": "제거된 노드를 정리하시겠습니까?", + "cleanup_nodes_dialog_description": "제거된 게임 서버 노드가 남긴 업데이트, 스트리밍 및 게임 데이터 검증 작업, 볼륨 요청, 볼륨 항목, 노드 항목을 삭제합니다. 노드 디스크의 파일은 유지됩니다. 최근 10분 이내에 Kubernetes가 「Ready」로 보고한 노드와 패널 자체가 실행되는 서버는 건너뜁니다.", + "cleanup_nodes_nothing": "정리할 항목이 없습니다", + "cleanup_nodes_done": "정리가 완료되었습니다", + "cleanup_nodes_done_with_errors": "정리가 완료되었지만 오류가 있습니다", + "cleanup_nodes_counts": "노드 항목: {nodes}, 볼륨 항목: {volumes}, 볼륨 요청: {volume_claims}, 작업: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "API에 Kubernetes 노드를 삭제할 권한이 없어 노드 항목과 해당 볼륨 요청 및 볼륨 항목을 유지했습니다. 패널의 업데이트 스크립트를 받아서 실행한 후(git pull && ./update.sh) 이 정리를 다시 실행하세요.", + "cleanup_nodes_failed_count": "제거하지 못한 객체: {failed}. API 로그를 확인하세요.", + "cleanup_nodes_recently_ready": "최근 10분 이내에 Kubernetes가 「Ready」로 보고하여 건너뛴 제거된 노드: {recently_ready}. Kubernetes가 「Ready」로 보고하지 않게 된 지 10분이 지나면 다시 실행하세요.", + "cleanup_nodes_failed": "정리에 실패했습니다", "player_sessions_section": "커뮤니티 플레이어 세션", "player_sessions_description": "Player Management 플러그인을 실행하는 커뮤니티 서버는 누가, 언제, 어떤 IP에서 플레이했는지 기록합니다.", "player_session_retention_days": "세션 기록 보관 기간 (일)", @@ -8508,6 +8524,8 @@ "select_region": "지역 선택", "no_region": "미할당", "remove_node": "노드 제거", + "remove_node_confirm_title": "{node}을(를) 제거할까요?", + "remove_node_confirm_description": "패널에서 이 노드를 제거합니다. Kubernetes가 「Ready」로 보고하지 않게 된 지 10분이 지나면 해당 업데이트, 스트리밍 및 게임 데이터 검증 작업, 볼륨 요청, 볼륨 항목, 노드 항목도 Kubernetes에서 삭제됩니다. 디스크의 파일은 유지됩니다. 아직 실행 중인 노드는 1분 이내에 스스로 다시 추가되지만, 비활성화된 상태이며 설정도 초기화됩니다. 이 경우 노드가 중지된 뒤 다시 제거하세요. 다시 추가되지 않았는데 제거 후 10분이 지나도 Kubernetes가 여전히 「Ready」로 보고하면, Kubernetes가 「Ready」로 보고하지 않게 된 지 10분이 지난 뒤 서버 설정에서 「제거된 노드 정리」를 실행하세요.", "remove_ports": "포트 제거", "mode": { "use_as_match": "매치 서버로 사용", diff --git a/i18n/locales/pl_PL.json b/i18n/locales/pl_PL.json index 0a37d3068..845f2b6b7 100644 --- a/i18n/locales/pl_PL.json +++ b/i18n/locales/pl_PL.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Wymagane miejsce na dysku — istniejąca instalacja (GB)", "reserved_disk_space_existing_gb_description": "Minimalna ilość miejsca na dysku w GB wymagana, gdy CS2 jest już zainstalowane na węźle. Ustaw 0, aby wyłączyć.", "update_cpu_pinning": "Przypisywanie CPU zaktualizowane", + "danger_zone_title": "Strefa zagrożenia", + "danger_zone_description": "Te akcje usuwają obiekty z twojego klastra Kubernetes i nie można ich cofnąć.", + "cleanup_nodes_title": "Wyczyść usunięte węzły", + "cleanup_nodes_description": "Usuwa to, co Kubernetes wciąż przechowuje dla węzłów serwerów gry usuniętych z panelu: ich zadania aktualizacji, streamingu i walidacji gamedata, żądania woluminów, wpisy woluminów i wpisy węzłów. Pliki na dyskach węzłów zostają zachowane. Węzły, które Kubernetes zgłosił jako „Ready” w ciągu ostatnich 10 minut, są pomijane, podobnie jak serwer, na którym działa sam panel. To samo czyszczenie uruchamia się też automatycznie przy usuwaniu węzła. Użyj tej opcji, aby dokończyć to, czego nie zdołało automatyczne czyszczenie, na przykład w przypadku usunięć sprzed tej aktualizacji.", + "cleanup_nodes_button": "Wyczyść", + "cleanup_nodes_running": "Czyszczenie...", + "cleanup_nodes_dialog_title": "Wyczyścić usunięte węzły?", + "cleanup_nodes_dialog_description": "To usuwa zadania aktualizacji, streamingu i walidacji gamedata, żądania woluminów, wpisy woluminów i wpisy węzłów pozostawione przez usunięte węzły serwerów gry. Pliki na dyskach węzłów zostają zachowane. Węzły, które Kubernetes zgłosił jako „Ready” w ciągu ostatnich 10 minut, są pomijane, podobnie jak serwer, na którym działa sam panel.", + "cleanup_nodes_nothing": "Nie ma czego czyścić", + "cleanup_nodes_done": "Czyszczenie zakończone", + "cleanup_nodes_done_with_errors": "Czyszczenie zakończone z błędami", + "cleanup_nodes_counts": "Wpisy węzłów: {nodes}, wpisy woluminów: {volumes}, żądania woluminów: {volume_claims}, zadania: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Wpisy węzłów zostały zachowane razem z ich żądaniami woluminów i wpisami woluminów, bo API nie ma uprawnień do usuwania węzłów Kubernetes. Pobierz i uruchom skrypt aktualizacji panelu (git pull && ./update.sh), a potem uruchom to ponownie.", + "cleanup_nodes_failed_count": "Obiekty, których nie udało się usunąć: {failed}. Sprawdź logi API.", + "cleanup_nodes_recently_ready": "Usunięte węzły pominięte, bo Kubernetes zgłosił je jako „Ready” w ciągu ostatnich 10 minut: {recently_ready}. Uruchom to ponownie, gdy Kubernetes od 10 minut nie będzie ich już zgłaszał jako „Ready”.", + "cleanup_nodes_failed": "Czyszczenie nie powiodło się", "player_sessions_section": "Sesje Graczy na Serwerach Społeczności", "player_sessions_description": "Serwery społeczności z wtyczką Player Management zapisują, kto grał, kiedy i z jakiego IP.", "player_session_retention_days": "Przechowuj Historię Sesji (dni)", @@ -8508,6 +8524,8 @@ "select_region": "Wybierz region", "no_region": "Nieprzypisany", "remove_node": "Usuń węzeł", + "remove_node_confirm_title": "Usunąć {node}?", + "remove_node_confirm_description": "To usuwa węzeł z panelu. Gdy Kubernetes od 10 minut nie będzie go już zgłaszał jako „Ready”, z Kubernetes zostaną też usunięte jego zadania aktualizacji, streamingu i walidacji gamedata, żądania woluminów, wpisy woluminów i wpis węzła. Pliki na jego dysku zostają zachowane. Węzeł, który nadal działa, sam doda się ponownie w ciągu minuty, ale będzie wyłączony i z przywróconymi ustawieniami domyślnymi; usuń go ponownie, gdy przestanie działać. Jeśli węzeł nie doda się ponownie, ale Kubernetes 10 minut po usunięciu nadal będzie zgłaszał go jako „Ready”, uruchom „Wyczyść usunięte węzły” w Ustawieniach serwera, gdy Kubernetes od 10 minut nie będzie go już zgłaszał jako „Ready”.", "remove_ports": "Usuń porty", "mode": { "use_as_match": "Użyj jako serwer meczowy", diff --git a/i18n/locales/pt_BR.json b/i18n/locales/pt_BR.json index b23ea3420..13bb74bc5 100644 --- a/i18n/locales/pt_BR.json +++ b/i18n/locales/pt_BR.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Espaço em Disco Necessário - Instalação Existente (GB)", "reserved_disk_space_existing_gb_description": "Espaço mínimo em disco (GB) exigido quando o CS2 já está instalado no nó. Defina como 0 para desativar.", "update_cpu_pinning": "CPU Pinning atualizado", + "danger_zone_title": "Zona de risco", + "danger_zone_description": "Essas ações excluem objetos do seu cluster Kubernetes e não podem ser desfeitas.", + "cleanup_nodes_title": "Limpar nós removidos", + "cleanup_nodes_description": "Apaga o que o Kubernetes ainda guarda dos nós de servidor de jogo que foram removidos do painel: as tarefas de atualização, de streaming e de validação do gamedata, as solicitações de volume, os registros de volume e os registros de nó. Os arquivos nos discos dos nós são mantidos. Os nós que o Kubernetes reportou como \"Ready\" nos últimos 10 minutos são ignorados, assim como o próprio servidor do painel. A mesma limpeza também roda automaticamente quando um nó é removido; use esta opção para o que a limpeza automática não conseguiu concluir, por exemplo remoções anteriores a esta atualização.", + "cleanup_nodes_button": "Limpar", + "cleanup_nodes_running": "Limpando...", + "cleanup_nodes_dialog_title": "Limpar os nós removidos?", + "cleanup_nodes_dialog_description": "Isso exclui as tarefas de atualização, de streaming e de validação do gamedata, solicitações de volume, registros de volume e registros de nó deixados pelos nós de servidor de jogo removidos. Os arquivos nos discos dos nós são mantidos. Os nós que o Kubernetes reportou como \"Ready\" nos últimos 10 minutos são ignorados, assim como o próprio servidor do painel.", + "cleanup_nodes_nothing": "Nada para limpar", + "cleanup_nodes_done": "Limpeza concluída", + "cleanup_nodes_done_with_errors": "Limpeza concluída com erros", + "cleanup_nodes_counts": "Registros de nó: {nodes}, registros de volume: {volumes}, solicitações de volume: {volume_claims}, tarefas: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Os registros de nó foram mantidos, junto com suas solicitações de volume e registros de volume, porque a API não tem permissão para excluir nós do Kubernetes. Execute o script de atualização do painel (git pull && ./update.sh) e depois execute isto novamente.", + "cleanup_nodes_failed_count": "Objetos que não puderam ser removidos: {failed}. Verifique os logs da API.", + "cleanup_nodes_recently_ready": "Nós removidos que foram ignorados porque o Kubernetes os reportou como \"Ready\" nos últimos 10 minutos: {recently_ready}. Execute isto novamente quando fizer 10 minutos que o Kubernetes deixou de reportá-los como \"Ready\".", + "cleanup_nodes_failed": "Falha na limpeza", "player_sessions_section": "Sessões de Jogadores da Comunidade", "player_sessions_description": "Servidores da comunidade com o plugin Player Management registram quem jogou, quando e de qual IP.", "player_session_retention_days": "Manter Histórico de Sessões (dias)", @@ -8508,6 +8524,8 @@ "select_region": "Selecionar Região/Local", "no_region": "Sem região", "remove_node": "Remover Nó", + "remove_node_confirm_title": "Remover {node}?", + "remove_node_confirm_description": "Isso remove o nó do painel. Quando fizer 10 minutos que o Kubernetes deixou de reportá-lo como \"Ready\", as tarefas de atualização, de streaming e de validação do gamedata, solicitações de volume, registros de volume e o registro de nó dele também serão excluídos do Kubernetes. Os arquivos no disco dele são mantidos. Um nó que ainda está em execução se adiciona novamente em até um minuto, mas desativado e com as configurações redefinidas; remova-o novamente quando ele estiver desligado. Se ele não se adicionar novamente, mas o Kubernetes ainda reportá-lo como \"Ready\" 10 minutos após a remoção, use \"Limpar nós removidos\" em Configurações do Servidor quando fizer 10 minutos que o Kubernetes deixou de reportá-lo como \"Ready\".", "remove_ports": "Remover Portas", "mode": { "use_as_match": "Usar como Servidor de Partidas", diff --git a/i18n/locales/ru_RU.json b/i18n/locales/ru_RU.json index 2378b076a..b26531e1e 100644 --- a/i18n/locales/ru_RU.json +++ b/i18n/locales/ru_RU.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Требуемое место на диске — существующая установка (ГБ)", "reserved_disk_space_existing_gb_description": "Минимальное место на диске в ГБ, требуемое, когда CS2 уже установлен на ноде. Установи 0, чтобы отключить.", "update_cpu_pinning": "Привязка CPU обновлена", + "danger_zone_title": "Опасная зона", + "danger_zone_description": "Эти действия удаляют объекты из твоего кластера Kubernetes, и их нельзя отменить.", + "cleanup_nodes_title": "Очистить удалённые ноды", + "cleanup_nodes_description": "Удаляет то, что Kubernetes ещё хранит для нод игровых серверов, удалённых из панели: их задачи обновления, стриминга и проверки gamedata, заявки на тома, записи томов и записи нод. Файлы на дисках нод сохраняются. Ноды, которые Kubernetes отмечал как «Ready» в последние 10 минут, пропускаются, как и сервер, на котором работает сама панель. Та же очистка запускается и автоматически при удалении ноды. Используй эту функцию для всего, что автоматическая очистка не смогла завершить, например для удалений, сделанных до этого обновления.", + "cleanup_nodes_button": "Очистить", + "cleanup_nodes_running": "Очистка…", + "cleanup_nodes_dialog_title": "Очистить удалённые ноды?", + "cleanup_nodes_dialog_description": "Это удалит задачи обновления, стриминга и проверки gamedata, заявки на тома, записи томов и записи нод, оставшиеся от удалённых нод игровых серверов. Файлы на дисках нод сохраняются. Ноды, которые Kubernetes отмечал как «Ready» в последние 10 минут, пропускаются, как и сервер, на котором работает сама панель.", + "cleanup_nodes_nothing": "Очищать нечего", + "cleanup_nodes_done": "Очистка завершена", + "cleanup_nodes_done_with_errors": "Очистка завершена с ошибками", + "cleanup_nodes_counts": "Записи нод: {nodes}, записи томов: {volumes}, заявки на тома: {volume_claims}, задачи: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Записи нод сохранены вместе с их заявками на тома и записями томов, потому что у API нет прав на удаление нод Kubernetes. Скачай и запусти скрипт обновления панели (git pull && ./update.sh), а затем запусти это снова.", + "cleanup_nodes_failed_count": "Объекты, которые не удалось удалить: {failed}. Проверь логи API.", + "cleanup_nodes_recently_ready": "Удалённые ноды, пропущенные, потому что Kubernetes отмечал их как «Ready» в последние 10 минут: {recently_ready}. Запусти это снова, когда пройдёт 10 минут с тех пор, как Kubernetes перестал отмечать их как «Ready».", + "cleanup_nodes_failed": "Не удалось выполнить очистку", "player_sessions_section": "Сессии Игроков на Серверах Сообщества", "player_sessions_description": "Серверы сообщества с плагином Player Management записывают, кто играл, когда и с какого IP.", "player_session_retention_days": "Хранить Историю Сессий (дней)", @@ -8508,6 +8524,8 @@ "select_region": "Выбрать регион", "no_region": "Не назначен", "remove_node": "Удалить ноду", + "remove_node_confirm_title": "Удалить {node}?", + "remove_node_confirm_description": "Это удалит ноду из панели. Когда пройдёт 10 минут с тех пор, как Kubernetes перестал отмечать её как «Ready», из Kubernetes также удалятся её задачи обновления, стриминга и проверки gamedata, заявки на тома, записи томов и запись ноды. Файлы на её диске сохраняются. Нода, которая всё ещё работает, добавит себя снова в течение минуты, но будет отключена, а её настройки сброшены; удали её снова, когда она перестанет работать. Если она не добавит себя снова, но через 10 минут после удаления Kubernetes всё ещё будет отмечать её как «Ready», запусти «Очистить удалённые ноды» в Настройках серверов, когда пройдёт 10 минут с тех пор, как Kubernetes перестал отмечать её как «Ready».", "remove_ports": "Удалить порты", "mode": { "use_as_match": "Использовать как сервер для матчей", diff --git a/i18n/locales/sv_SE.json b/i18n/locales/sv_SE.json index 1ba92e332..f9083e77c 100644 --- a/i18n/locales/sv_SE.json +++ b/i18n/locales/sv_SE.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Nödvändigt diskutrymme - befintlig installation (GB)", "reserved_disk_space_existing_gb_description": "Minsta diskutrymme i GB som krävs när CS2 redan är installerat på noden. Sätt till 0 för att inaktivera.", "update_cpu_pinning": "CPU-fixering uppdaterad", + "danger_zone_title": "Farlig zon", + "danger_zone_description": "Dessa åtgärder tar bort objekt från ditt Kubernetes-kluster och går inte att ångra.", + "cleanup_nodes_title": "Rensa borttagna noder", + "cleanup_nodes_description": "Raderar det som Kubernetes fortfarande har kvar för spelservernoder som har tagits bort från panelen: deras uppdaterings-, streaming- och gamedata-valideringsjobb, volymanspråk, volymposter och nodposter. Filer på nodernas diskar behålls. Noder som Kubernetes har rapporterat som «Ready» under de senaste 10 minuterna hoppas över, och det gör även servern som panelen själv körs på. Samma rensning körs också automatiskt när en nod tas bort. Använd den här funktionen för allt som den automatiska rensningen inte kunde slutföra, till exempel borttagningar som gjordes före den här uppdateringen.", + "cleanup_nodes_button": "Rensa", + "cleanup_nodes_running": "Rensar...", + "cleanup_nodes_dialog_title": "Rensa borttagna noder?", + "cleanup_nodes_dialog_description": "Detta tar bort uppdaterings-, streaming- och gamedata-valideringsjobb, volymanspråk, volymposter och nodposter som borttagna spelservernoder har lämnat kvar. Filer på nodernas diskar behålls. Noder som Kubernetes har rapporterat som «Ready» under de senaste 10 minuterna hoppas över, och det gör även servern som panelen själv körs på.", + "cleanup_nodes_nothing": "Inget att rensa", + "cleanup_nodes_done": "Rensningen är klar", + "cleanup_nodes_done_with_errors": "Rensningen är klar men med fel", + "cleanup_nodes_counts": "Nodposter: {nodes}, volymposter: {volumes}, volymanspråk: {volume_claims}, jobb: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Nodposter behölls tillsammans med sina volymanspråk och volymposter eftersom API:et saknar behörighet att ta bort Kubernetes-noder. Hämta och kör panelens uppdateringsskript (git pull && ./update.sh) och kör sedan detta igen.", + "cleanup_nodes_failed_count": "Objekt som inte kunde tas bort: {failed}. Kontrollera API-loggarna.", + "cleanup_nodes_recently_ready": "Borttagna noder som hoppades över eftersom Kubernetes har rapporterat dem som «Ready» under de senaste 10 minuterna: {recently_ready}. Kör detta igen när Kubernetes inte har rapporterat dem som «Ready» på 10 minuter.", + "cleanup_nodes_failed": "Rensningen misslyckades", "player_sessions_section": "Spelarsessioner på Communityservrar", "player_sessions_description": "Communityservrar med Player Management-pluginet registrerar vem som spelade, när och från vilken IP.", "player_session_retention_days": "Spara Sessionshistorik (dagar)", @@ -8508,6 +8524,8 @@ "select_region": "Välj region", "no_region": "Ej tilldelad", "remove_node": "Ta bort nod", + "remove_node_confirm_title": "Ta bort {node}?", + "remove_node_confirm_description": "Detta tar bort noden från panelen. När Kubernetes inte har rapporterat den som «Ready» på 10 minuter raderas även dess uppdaterings-, streaming- och gamedata-valideringsjobb, volymanspråk, volymposter och nodpost från Kubernetes. Filer på dess disk behålls. En nod som fortfarande körs lägger till sig själv igen inom en minut, men inaktiverad och med återställda inställningar; ta bort den igen när den är nere. Om den inte kommer tillbaka men Kubernetes fortfarande rapporterar den som «Ready» 10 minuter efter borttagningen, kör du «Rensa borttagna noder» i Serverinställningar när Kubernetes inte har rapporterat den som «Ready» på 10 minuter.", "remove_ports": "Ta bort portar", "mode": { "use_as_match": "Använd som matchserver", diff --git a/i18n/locales/tr_TR.json b/i18n/locales/tr_TR.json index af77fc5cc..7c41082ce 100644 --- a/i18n/locales/tr_TR.json +++ b/i18n/locales/tr_TR.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Gerekli disk alanı - mevcut kurulum (GB)", "reserved_disk_space_existing_gb_description": "Düğümde CS2 zaten yüklüyken gereken minimum disk alanı (GB). Devre dışı bırakmak için 0 gir.", "update_cpu_pinning": "CPU sabitleme güncellendi", + "danger_zone_title": "Tehlikeli Bölge", + "danger_zone_description": "Bu işlemler Kubernetes kümenden nesneleri siler ve geri alınamaz.", + "cleanup_nodes_title": "Kaldırılan düğümleri temizle", + "cleanup_nodes_description": "Panelden kaldırılan oyun sunucusu düğümleri için Kubernetes'te hâlâ kalanları siler: güncelleme, yayın ve gamedata doğrulama işleri, birim talepleri, birim girdileri ve düğüm girdileri. Düğümlerin disklerindeki dosyalar korunur. Kubernetes'in son 10 dakika içinde «Ready» olarak bildirdiği düğümler ve panelin kendi sunucusu atlanır. Aynı temizlik bir düğüm kaldırıldığında da otomatik olarak çalışır. Otomatik temizliğin tamamlayamadığı her şey için bunu kullan, örneğin bu güncellemeden önceki kaldırmalar için.", + "cleanup_nodes_button": "Temizle", + "cleanup_nodes_running": "Temizleniyor...", + "cleanup_nodes_dialog_title": "Kaldırılan düğümler temizlensin mi?", + "cleanup_nodes_dialog_description": "Bu işlem, kaldırılan oyun sunucusu düğümlerinden geriye kalan güncelleme, yayın ve gamedata doğrulama işlerini, birim taleplerini, birim girdilerini ve düğüm girdilerini siler. Düğümlerin disklerindeki dosyalar korunur. Kubernetes'in son 10 dakika içinde «Ready» olarak bildirdiği düğümler ve panelin kendi sunucusu atlanır.", + "cleanup_nodes_nothing": "Temizlenecek bir şey yok", + "cleanup_nodes_done": "Temizlik tamamlandı", + "cleanup_nodes_done_with_errors": "Temizlik hatalarla tamamlandı", + "cleanup_nodes_counts": "Düğüm girdileri: {nodes}, birim girdileri: {volumes}, birim talepleri: {volume_claims}, işler: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "API'nin Kubernetes düğümlerini silme izni olmadığı için düğüm girdileri, birim talepleri ve birim girdileriyle birlikte korundu. Panelin güncelleme betiğini çekip çalıştır (git pull && ./update.sh), ardından bunu yeniden çalıştır.", + "cleanup_nodes_failed_count": "Kaldırılamayan nesneler: {failed}. API günlüklerini kontrol et.", + "cleanup_nodes_recently_ready": "Kubernetes son 10 dakika içinde «Ready» olarak bildirdiği için atlanan kaldırılmış düğümler: {recently_ready}. Kubernetes onları 10 dakika boyunca «Ready» olarak bildirmediğinde bunu yeniden çalıştır.", + "cleanup_nodes_failed": "Temizlik başarısız oldu", "player_sessions_section": "Topluluk Oyuncu Oturumları", "player_sessions_description": "Player Management eklentisini çalıştıran topluluk sunucuları kimin, ne zaman ve hangi IP'den oynadığını kaydeder.", "player_session_retention_days": "Oturum Geçmişini Sakla (gün)", @@ -8508,6 +8524,8 @@ "select_region": "Bölge seç", "no_region": "Atanmamış", "remove_node": "Düğümü kaldır", + "remove_node_confirm_title": "{node} kaldırılsın mı?", + "remove_node_confirm_description": "Bu işlem düğümü panelden kaldırır. Kubernetes onu 10 dakika boyunca «Ready» olarak bildirmedikten sonra güncelleme, yayın ve gamedata doğrulama işleri, birim talepleri, birim girdileri ve düğüm girdisi de Kubernetes'ten silinir. Diskindeki dosyalar korunur. Hâlâ çalışan bir düğüm bir dakika içinde kendini yeniden ekler, ancak devre dışı olarak ve ayarları sıfırlanmış şekilde; kapandığında onu yeniden kaldır. Düğüm kendini yeniden eklemezse ama kaldırıldıktan 10 dakika sonra Kubernetes onu hâlâ «Ready» olarak bildiriyorsa, Kubernetes onu 10 dakika boyunca «Ready» olarak bildirmedikten sonra Sunucu ayarları içindeki «Kaldırılan düğümleri temizle» işlemini çalıştır.", "remove_ports": "Portları kaldır", "mode": { "use_as_match": "Maç sunucusu olarak kullan", diff --git a/i18n/locales/uk_UA.json b/i18n/locales/uk_UA.json index 1ce2656e3..291a42c13 100644 --- a/i18n/locales/uk_UA.json +++ b/i18n/locales/uk_UA.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "Необхідний обсяг диска — наявна установка (ГБ)", "reserved_disk_space_existing_gb_description": "Мінімальний обсяг диска в ГБ, необхідний, коли CS2 вже встановлено на ноді. Встанови 0, щоб вимкнути.", "update_cpu_pinning": "Прив'язку CPU оновлено", + "danger_zone_title": "Небезпечна зона", + "danger_zone_description": "Ці дії видаляють об'єкти з твого кластера Kubernetes, і їх неможливо скасувати.", + "cleanup_nodes_title": "Очищення видалених нод", + "cleanup_nodes_description": "Видаляє те, що Kubernetes досі зберігає для нод ігрових серверів, видалених із панелі: їхні завдання оновлення, стримінгу та перевірки gamedata, заявки на томи, записи томів і записи нод. Файли на дисках нод зберігаються. Ноди, які Kubernetes позначав як «Ready» протягом останніх 10 хвилин, пропускаються, як і сервер, на якому працює сама панель. Таке саме очищення запускається й автоматично під час видалення ноди. Використовуй цю функцію для всього, що автоматичне очищення не змогло завершити, наприклад для видалень, зроблених до цього оновлення.", + "cleanup_nodes_button": "Очистити", + "cleanup_nodes_running": "Очищення...", + "cleanup_nodes_dialog_title": "Очистити видалені ноди?", + "cleanup_nodes_dialog_description": "Це видалить завдання оновлення, стримінгу та перевірки gamedata, заявки на томи, записи томів і записи нод, що залишилися від видалених нод ігрових серверів. Файли на дисках нод зберігаються. Ноди, які Kubernetes позначав як «Ready» протягом останніх 10 хвилин, пропускаються, як і сервер, на якому працює сама панель.", + "cleanup_nodes_nothing": "Нема чого очищати", + "cleanup_nodes_done": "Очищення завершено", + "cleanup_nodes_done_with_errors": "Очищення завершено з помилками", + "cleanup_nodes_counts": "Записи нод: {nodes}, записи томів: {volumes}, заявки на томи: {volume_claims}, завдання: {jobs}.", + "cleanup_nodes_node_delete_forbidden": "Записи нод збережено разом з їхніми заявками на томи та записами томів, бо API не має дозволу видаляти ноди Kubernetes. Зроби pull і запусти скрипт оновлення панелі (git pull && ./update.sh), а потім запусти це знову.", + "cleanup_nodes_failed_count": "Об'єкти, які не вдалося видалити: {failed}. Перевір логи API.", + "cleanup_nodes_recently_ready": "Видалені ноди, пропущені, бо Kubernetes позначав їх як «Ready» протягом останніх 10 хвилин: {recently_ready}. Запусти це знову, коли мине 10 хвилин відтоді, як Kubernetes перестав позначати їх як «Ready».", + "cleanup_nodes_failed": "Не вдалося виконати очищення", "player_sessions_section": "Сесії Гравців на Серверах Спільноти", "player_sessions_description": "Сервери спільноти з плагіном Player Management записують, хто грав, коли і з якого IP.", "player_session_retention_days": "Зберігати Історію Сесій (днів)", @@ -8508,6 +8524,8 @@ "select_region": "Обери регіон", "no_region": "Не призначено", "remove_node": "Видалити ноду", + "remove_node_confirm_title": "Видалити {node}?", + "remove_node_confirm_description": "Це видалить ноду з панелі. Коли мине 10 хвилин відтоді, як Kubernetes перестав позначати її як «Ready», з Kubernetes також буде видалено її завдання оновлення, стримінгу та перевірки gamedata, заявки на томи, записи томів і запис ноди. Файли на її диску зберігаються. Нода, яка досі працює, додасть себе знову протягом хвилини, але буде вимкнена, а її налаштування скинуто; видали її знову, коли вона перестане працювати. Якщо вона не додасть себе знову, але через 10 хвилин після видалення Kubernetes досі позначатиме її як «Ready», запусти «Очищення видалених нод» у Налаштуваннях сервера, коли мине 10 хвилин відтоді, як Kubernetes перестав позначати її як «Ready».", "remove_ports": "Видалити порти", "mode": { "use_as_match": "Використовувати як матч-сервер", diff --git a/i18n/locales/zh_Hans.json b/i18n/locales/zh_Hans.json index 7642663a2..5c9e4ba44 100644 --- a/i18n/locales/zh_Hans.json +++ b/i18n/locales/zh_Hans.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "所需磁盘空间 - 已有安装 (GB)", "reserved_disk_space_existing_gb_description": "当节点上已安装 CS2 时所需的最小磁盘空间(GB)。设为 0 以禁用。", "update_cpu_pinning": "CPU 亲和性已更新", + "danger_zone_title": "危险区域", + "danger_zone_description": "这些操作会从您的 Kubernetes 集群中删除对象,且无法撤销。", + "cleanup_nodes_title": "清理已移除的节点", + "cleanup_nodes_description": "删除已从面板移除的游戏服务器节点在 Kubernetes 中残留的内容:更新任务、推流任务、游戏数据验证任务、存储卷声明、存储卷记录以及节点记录。节点磁盘上的文件会保留。过去 10 分钟内 Kubernetes 报告为“Ready”的节点会被跳过,面板自身所在的服务器也会被跳过。移除节点时也会自动执行同样的清理;自动清理未能完成的内容可在此处理,例如本次更新之前的移除所遗留的内容。", + "cleanup_nodes_button": "清理", + "cleanup_nodes_running": "清理中...", + "cleanup_nodes_dialog_title": "清理已移除的节点?", + "cleanup_nodes_dialog_description": "这会删除已移除的游戏服务器节点遗留的更新任务、推流任务、游戏数据验证任务、存储卷声明、存储卷记录和节点记录。节点磁盘上的文件会保留。过去 10 分钟内 Kubernetes 报告为“Ready”的节点会被跳过,面板自身所在的服务器也会被跳过。", + "cleanup_nodes_nothing": "没有需要清理的内容", + "cleanup_nodes_done": "清理完成", + "cleanup_nodes_done_with_errors": "清理完成,但有错误", + "cleanup_nodes_counts": "节点记录:{nodes},存储卷记录:{volumes},存储卷声明:{volume_claims},任务:{jobs}。", + "cleanup_nodes_node_delete_forbidden": "由于 API 没有删除 Kubernetes 节点的权限,已保留节点记录及其存储卷声明和存储卷记录。请拉取并运行面板的更新脚本(git pull && ./update.sh),然后再次运行此清理。", + "cleanup_nodes_failed_count": "无法移除的对象:{failed}。请检查 API 日志。", + "cleanup_nodes_recently_ready": "因过去 10 分钟内 Kubernetes 报告为“Ready”而跳过的已移除节点:{recently_ready}。待 Kubernetes 不再将它们报告为“Ready”满 10 分钟后请再次运行。", + "cleanup_nodes_failed": "清理失败", "player_sessions_section": "社区玩家会话", "player_sessions_description": "运行 Player Management 插件的社区服务器会记录谁在何时、从哪个 IP 游戏。", "player_session_retention_days": "会话历史保留天数", @@ -8508,6 +8524,8 @@ "select_region": "选择地区", "no_region": "未分配", "remove_node": "移除节点", + "remove_node_confirm_title": "移除 {node}?", + "remove_node_confirm_description": "这会将该节点从面板中移除。Kubernetes 不再将该节点报告为“Ready”满 10 分钟后,其更新任务、推流任务、游戏数据验证任务、存储卷声明、存储卷记录和节点记录也会从 Kubernetes 中删除。其磁盘上的文件会保留。仍在运行的节点会在一分钟内自动重新加入,但会处于停用状态,且设置会被重置;请在节点停止后再次移除它。如果节点没有重新加入,但移除 10 分钟后 Kubernetes 仍将其报告为“Ready”,请在 Kubernetes 不再将其报告为“Ready”满 10 分钟后,在服务器设置中运行“清理已移除的节点”。", "remove_ports": "移除端口", "mode": { "use_as_match": "用作比赛服务器", diff --git a/i18n/locales/zh_Hant.json b/i18n/locales/zh_Hant.json index 7e4857ba6..5b138513c 100644 --- a/i18n/locales/zh_Hant.json +++ b/i18n/locales/zh_Hant.json @@ -2275,6 +2275,22 @@ "reserved_disk_space_existing_gb": "所需磁碟空間 - 已有安裝 (GB)", "reserved_disk_space_existing_gb_description": "當節點上已安裝 CS2 時所需的最小磁碟空間(GB)。設為 0 以停用。", "update_cpu_pinning": "CPU 綁定已更新", + "danger_zone_title": "危險區域", + "danger_zone_description": "這些操作會從您的 Kubernetes 叢集中刪除物件,且無法復原。", + "cleanup_nodes_title": "清理已移除的節點", + "cleanup_nodes_description": "刪除已從面板移除的遊戲伺服器節點在 Kubernetes 中殘留的內容:更新工作、串流工作、遊戲資料驗證工作、磁碟區宣告、磁碟區記錄以及節點記錄。節點磁碟上的檔案會保留。過去 10 分鐘內 Kubernetes 回報為「Ready」的節點會被略過,面板本身所在的伺服器也會被略過。移除節點時也會自動執行相同的清理;自動清理未能完成的內容可在此處理,例如本次更新之前的移除所遺留的內容。", + "cleanup_nodes_button": "清理", + "cleanup_nodes_running": "清理中...", + "cleanup_nodes_dialog_title": "要清理已移除的節點嗎?", + "cleanup_nodes_dialog_description": "這會刪除已移除的遊戲伺服器節點遺留的更新工作、串流工作、遊戲資料驗證工作、磁碟區宣告、磁碟區記錄與節點記錄。節點磁碟上的檔案會保留。過去 10 分鐘內 Kubernetes 回報為「Ready」的節點會被略過,面板本身所在的伺服器也會被略過。", + "cleanup_nodes_nothing": "沒有需要清理的內容", + "cleanup_nodes_done": "清理完成", + "cleanup_nodes_done_with_errors": "清理完成,但有錯誤", + "cleanup_nodes_counts": "節點記錄:{nodes},磁碟區記錄:{volumes},磁碟區宣告:{volume_claims},工作:{jobs}。", + "cleanup_nodes_node_delete_forbidden": "由於 API 沒有刪除 Kubernetes 節點的權限,已保留節點記錄及其磁碟區宣告與磁碟區記錄。請拉取並執行面板的更新指令碼(git pull && ./update.sh),然後再次執行此清理。", + "cleanup_nodes_failed_count": "無法移除的物件:{failed}。請檢查 API 日誌。", + "cleanup_nodes_recently_ready": "因過去 10 分鐘內 Kubernetes 回報為「Ready」而略過的已移除節點:{recently_ready}。待 Kubernetes 不再將它們回報為「Ready」滿 10 分鐘後請再次執行。", + "cleanup_nodes_failed": "清理失敗", "player_sessions_section": "社群玩家工作階段", "player_sessions_description": "執行 Player Management 外掛的社群伺服器會記錄誰在何時、從哪個 IP 遊戲。", "player_session_retention_days": "工作階段歷史保留天數", @@ -8508,6 +8524,8 @@ "select_region": "選擇地區", "no_region": "未指派", "remove_node": "移除節點", + "remove_node_confirm_title": "移除 {node}?", + "remove_node_confirm_description": "這會將此節點從面板中移除。Kubernetes 不再將此節點回報為「Ready」滿 10 分鐘後,其更新工作、串流工作、遊戲資料驗證工作、磁碟區宣告、磁碟區記錄與節點記錄也會從 Kubernetes 中刪除。其磁碟上的檔案會保留。仍在執行的節點會在一分鐘內自動重新加入,但會處於停用狀態,且設定會被重設;請在節點停止後再次移除它。如果節點沒有重新加入,但移除 10 分鐘後 Kubernetes 仍將其回報為「Ready」,請在 Kubernetes 不再將其回報為「Ready」滿 10 分鐘後,於伺服器設定中執行「清理已移除的節點」。", "remove_ports": "移除連接埠", "mode": { "use_as_match": "用作比賽伺服器", diff --git a/pages/settings/application/servers.vue b/pages/settings/application/servers.vue index f1a3f2199..6162d3464 100644 --- a/pages/settings/application/servers.vue +++ b/pages/settings/application/servers.vue @@ -4,6 +4,19 @@ import PageTransition from "~/components/ui/transitions/PageTransition.vue"; import SettingsPage from "~/components/settings/SettingsPage.vue"; import SettingsSection from "~/components/settings/SettingsSection.vue"; import SettingsSaveBar from "~/components/settings/SettingsSaveBar.vue"; +import { Button } from "@/components/ui/button"; +import { Spinner } from "@/components/ui/spinner"; +import { AlertTriangle } from "lucide-vue-next"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; @@ -185,6 +298,7 @@ import { settings_constraint, settings_update_column } from "~/generated/zeus"; import { useForm } from "vee-validate"; import { toTypedSchema } from "~/utilities/vee-validate-zod"; import { z } from "zod"; +import gql from "graphql-tag"; // The api clamps to the same bounds: the weekly leaderboards read raw // sessions, so a week is the floor. @@ -194,6 +308,8 @@ export default { data() { return { submitting: false, + cleaningUpNodes: false, + showCleanupNodesDialog: false, PLAYER_SESSION_RETENTION, form: useForm({ validationSchema: toTypedSchema( @@ -335,6 +451,111 @@ export default { title: this.$t("pages.settings.application.servers.update_cpu_pinning"), }); }, + async cleanupRemovedNodes() { + if (this.cleaningUpNodes) { + return; + } + this.showCleanupNodesDialog = false; + this.cleaningUpNodes = true; + try { + const { data } = await this.$apollo.mutate({ + mutation: gql` + mutation CleanupRemovedNodes { + cleanupRemovedNodes { + nodes + jobs + volume_claims + volumes + failed + node_delete_forbidden + recently_ready + } + } + `, + }); + + const { + nodes, + jobs, + volume_claims, + volumes, + failed, + node_delete_forbidden, + recently_ready, + } = data.cleanupRemovedNodes; + const removed = nodes + jobs + volume_claims + volumes; + + const lines: string[] = []; + if (removed > 0) { + lines.push( + this.$t("pages.settings.application.servers.cleanup_nodes_counts", { + nodes, + volumes, + volume_claims, + jobs, + }), + ); + } + // Older panel installs do not let the API delete Kubernetes nodes. + if (node_delete_forbidden) { + lines.push( + this.$t( + "pages.settings.application.servers.cleanup_nodes_node_delete_forbidden", + ), + ); + } + // They may only be restarting, so the API keeps them for now. + if (recently_ready > 0) { + lines.push( + this.$t( + "pages.settings.application.servers.cleanup_nodes_recently_ready", + { recently_ready }, + ), + ); + } + if (failed > 0) { + lines.push( + this.$t( + "pages.settings.application.servers.cleanup_nodes_failed_count", + { failed }, + ), + ); + } + + let title = this.$t( + "pages.settings.application.servers.cleanup_nodes_done", + ); + if (failed > 0) { + title = this.$t( + "pages.settings.application.servers.cleanup_nodes_done_with_errors", + ); + } else if ( + removed === 0 && + !node_delete_forbidden && + recently_ready === 0 + ) { + title = this.$t( + "pages.settings.application.servers.cleanup_nodes_nothing", + ); + } + + toast({ + title, + description: lines.join("\n"), + variant: failed > 0 ? "destructive" : "default", + }); + } catch (error: any) { + toast({ + title: this.$t( + "pages.settings.application.servers.cleanup_nodes_failed", + ), + description: error?.message, + variant: "destructive", + }); + } finally { + this.cleaningUpNodes = false; + } + }, }, computed: { settings() { diff --git a/tests/components/game-server-nodes/removeNodeConfirm.spec.ts b/tests/components/game-server-nodes/removeNodeConfirm.spec.ts new file mode 100644 index 000000000..508b48fa3 --- /dev/null +++ b/tests/components/game-server-nodes/removeNodeConfirm.spec.ts @@ -0,0 +1,118 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { flushPromises } from "@vue/test-utils"; +import { mountSuspended } from "@nuxt/test-utils/runtime"; +import { print, type DocumentNode } from "graphql"; +import GameServerNodeRow from "~/components/game-server-nodes/GameServerNodeRow.vue"; + +const node = { + id: "node-a", + label: "EU West", + status: "Offline", + region: "eu-west", + enabled: true, + start_port_range: 30000, + end_port_range: 30100, + total_server_count: 0, + available_server_count: 0, +}; + +let mutate: ReturnType; +let unmount: (() => void) | undefined; + +afterEach(async () => { + unmount?.(); + unmount = undefined; + vi.restoreAllMocks(); + await flushPromises(); +}); + +async function mountRow() { + const client = (useNuxtApp() as any).$apollo.defaultClient; + vi.spyOn(client, "subscribe").mockImplementation(() => ({ + subscribe: () => ({ unsubscribe() {}, closed: false }), + })); + mutate = vi.fn(async () => ({ data: {} })); + vi.spyOn(client, "mutate").mockImplementation(mutate); + + const wrapper = await mountSuspended(GameServerNodeRow, { + props: { gameServerNode: node }, + attachTo: document.body, + global: { + mixins: (useNuxtApp().vueApp as any)._context.mixins, + stubs: { + NodeControlMenu: true, + NodeMetrics: true, + ServiceLogs: true, + UpdateGameServerLabel: true, + EditCs2Options: true, + }, + }, + }); + unmount = () => wrapper.unmount(); + await flushPromises(); + return wrapper; +} + +type Wrapper = Awaited>; + +const dialog = () => + document.body.querySelector('[role="alertdialog"]'); + +function button(root: ParentNode | null | undefined, label: string) { + return Array.from( + root?.querySelectorAll("button, [role='menuitem']") ?? [], + ).find((element) => element.textContent?.trim() === label); +} + +// The row renders the same menu twice: first for desktop, then for mobile. +async function chooseRemoveNode(wrapper: Wrapper, menu: number) { + const triggers = wrapper + .findAll("button[aria-haspopup='menu']") + .filter((candidate) => + candidate.find(".lucide-ellipsis-vertical").exists(), + ); + expect(triggers).toHaveLength(2); + await triggers[menu].trigger("keydown", { key: "Enter" }); + await flushPromises(); + + const item = button(document.body, "Remove Node"); + expect(item, "Remove Node menu item").toBeDefined(); + item!.click(); + await flushPromises(); +} + +describe.each([ + ["desktop", 0], + ["mobile", 1], +])("game server node removal from the %s menu", (_, menu) => { + it("asks first and removes nothing when cancelled", async () => { + const wrapper = await mountRow(); + + await chooseRemoveNode(wrapper, menu); + + expect(dialog()?.textContent).toContain("Remove EU West?"); + expect(dialog()?.textContent).toContain( + "Once Kubernetes has reported it as not ready for 10 minutes", + ); + expect(mutate).not.toHaveBeenCalled(); + + button(dialog(), "Cancel")!.click(); + await flushPromises(); + + expect(dialog()).toBeNull(); + expect(mutate).not.toHaveBeenCalled(); + }); + + it("deletes the node once confirmed", async () => { + const wrapper = await mountRow(); + + await chooseRemoveNode(wrapper, menu); + button(dialog(), "Remove Node")!.click(); + await flushPromises(); + + expect(dialog()).toBeNull(); + expect(mutate).toHaveBeenCalledTimes(1); + const sent = print(mutate.mock.calls[0][0].mutation as DocumentNode); + expect(sent).toContain('delete_game_server_nodes_by_pk(id: "node-a")'); + }); +}); diff --git a/tests/pages/settings/removedNodeCleanup.spec.ts b/tests/pages/settings/removedNodeCleanup.spec.ts new file mode 100644 index 000000000..0076f43b9 --- /dev/null +++ b/tests/pages/settings/removedNodeCleanup.spec.ts @@ -0,0 +1,286 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { flushPromises } from "@vue/test-utils"; +import { mountSuspended } from "@nuxt/test-utils/runtime"; +import type { DocumentNode, FieldNode, OperationDefinitionNode } from "graphql"; +import ServerSettings from "~/pages/settings/application/servers.vue"; + +const { toast } = vi.hoisted(() => ({ toast: vi.fn() })); + +vi.mock("@/components/ui/toast", async (importOriginal) => ({ + ...(await importOriginal()), + toast, +})); + +type CleanupResult = { + nodes: number; + jobs: number; + volume_claims: number; + volumes: number; + failed: number; + node_delete_forbidden: boolean; + recently_ready: number; +}; + +const NOTHING: CleanupResult = { + nodes: 0, + jobs: 0, + volume_claims: 0, + volumes: 0, + failed: 0, + node_delete_forbidden: false, + recently_ready: 0, +}; + +const FORBIDDEN_NOTE = + "Node entries, with their volume claims and volume records, were kept because the API has no permission to delete Kubernetes nodes. Pull and run the panel's update script (git pull && ./update.sh), then run this again."; + +const recentlyReadyNote = (count: number) => + `Removed nodes skipped because Kubernetes reported them as Ready in the last 10 minutes: ${count}. Run this again once Kubernetes has reported them as not ready for 10 minutes.`; + +let mutate: ReturnType; +let unmount: (() => void) | undefined; + +afterEach(async () => { + unmount?.(); + unmount = undefined; + toast.mockReset(); + vi.restoreAllMocks(); + await flushPromises(); +}); + +const answer = (result: Partial) => async () => ({ + data: { cleanupRemovedNodes: { ...NOTHING, ...result } }, +}); + +async function mountPage(respond: () => Promise = answer({})) { + const client = (useNuxtApp() as any).$apollo.defaultClient; + mutate = vi.fn(respond); + vi.spyOn(client, "mutate").mockImplementation(mutate); + + const wrapper = await mountSuspended(ServerSettings, { + attachTo: document.body, + global: { + mixins: (useNuxtApp().vueApp as any)._context.mixins, + }, + }); + unmount = () => wrapper.unmount(); + await flushPromises(); + return wrapper; +} + +type Wrapper = Awaited>; + +const cleanupButton = (wrapper: Wrapper) => + wrapper.find("#danger-zone button"); + +const dialog = () => + document.body.querySelector('[role="alertdialog"]'); + +function dialogButton(label: string) { + const buttons = dialog()?.querySelectorAll("button"); + return Array.from(buttons ?? []).find( + (button) => button.textContent?.trim() === label, + ); +} + +async function openDialog(wrapper: Wrapper) { + await cleanupButton(wrapper).trigger("click"); + await flushPromises(); +} + +async function confirmCleanup(wrapper: Wrapper) { + await openDialog(wrapper); + const confirm = dialogButton("Clean Up"); + expect(confirm, "confirm button").toBeDefined(); + confirm!.click(); + await flushPromises(); +} + +async function runCleanup(result: Partial) { + const wrapper = await mountPage(answer(result)); + await confirmCleanup(wrapper); + expect(toast).toHaveBeenCalledTimes(1); + return toast.mock.calls[0][0]; +} + +describe("server settings removed node cleanup", () => { + it("asks first and never submits the settings form", async () => { + const wrapper = await mountPage(); + const button = cleanupButton(wrapper); + + expect(button.attributes("type")).toBe("button"); + expect(button.text()).toBe("Clean Up"); + expect(button.element.closest("form")).toBeNull(); + + await openDialog(wrapper); + + expect(dialog()?.textContent).toContain("Clean up removed nodes?"); + expect(mutate).not.toHaveBeenCalled(); + + dialogButton("Cancel")!.click(); + await flushPromises(); + + expect(dialog()).toBeNull(); + expect(mutate).not.toHaveBeenCalled(); + expect(toast).not.toHaveBeenCalled(); + }); + + it("sends only the cleanup mutation, with the agreed fields", async () => { + const wrapper = await mountPage(); + + await confirmCleanup(wrapper); + + expect(mutate).toHaveBeenCalledTimes(1); + const sent = mutate.mock.calls[0][0].mutation as DocumentNode; + expect(sent.definitions).toHaveLength(1); + + const operation = sent.definitions[0] as OperationDefinitionNode; + expect(operation.operation).toBe("mutation"); + expect(operation.name?.value).toBe("CleanupRemovedNodes"); + expect(operation.selectionSet.selections).toHaveLength(1); + + const field = operation.selectionSet.selections[0] as FieldNode; + expect(field.name.value).toBe("cleanupRemovedNodes"); + expect( + field.selectionSet?.selections.map( + (selection) => (selection as FieldNode).name.value, + ), + ).toEqual([ + "nodes", + "jobs", + "volume_claims", + "volumes", + "failed", + "node_delete_forbidden", + "recently_ready", + ]); + expect(dialog()).toBeNull(); + }); + + it("says so when there was nothing to clean up", async () => { + expect(await runCleanup({})).toEqual({ + title: "Nothing to clean up", + description: "", + variant: "default", + }); + }); + + it("lists what it removed", async () => { + expect( + await runCleanup({ nodes: 2, jobs: 5, volume_claims: 3, volumes: 4 }), + ).toEqual({ + title: "Cleanup finished", + description: + "Node entries: 2, volume records: 4, volume claims: 3, jobs: 5.", + variant: "default", + }); + }); + + it("explains node entries it had no permission to delete", async () => { + expect( + await runCleanup({ + jobs: 2, + volume_claims: 1, + volumes: 1, + node_delete_forbidden: true, + }), + ).toEqual({ + title: "Cleanup finished", + description: `Node entries: 0, volume records: 1, volume claims: 1, jobs: 2.\n${FORBIDDEN_NOTE}`, + variant: "default", + }); + }); + + it("does not report nothing to clean up when node deletes were refused", async () => { + expect(await runCleanup({ node_delete_forbidden: true })).toEqual({ + title: "Cleanup finished", + description: FORBIDDEN_NOTE, + variant: "default", + }); + }); + + it("flags objects it could not remove", async () => { + expect(await runCleanup({ nodes: 1, failed: 2 })).toEqual({ + title: "Cleanup finished with errors", + description: + "Node entries: 1, volume records: 0, volume claims: 0, jobs: 0.\nObjects that could not be removed: 2. Check the API logs.", + variant: "destructive", + }); + }); + + it("flags failures even when nothing was removed", async () => { + expect( + await runCleanup({ failed: 3, node_delete_forbidden: true }), + ).toEqual({ + title: "Cleanup finished with errors", + description: `${FORBIDDEN_NOTE}\nObjects that could not be removed: 3. Check the API logs.`, + variant: "destructive", + }); + }); + + it("does not report nothing to clean up when removed nodes were skipped for being Ready too recently", async () => { + expect(await runCleanup({ recently_ready: 2 })).toEqual({ + title: "Cleanup finished", + description: recentlyReadyNote(2), + variant: "default", + }); + }); + + it("lists what it removed next to the nodes that were Ready too recently", async () => { + expect( + await runCleanup({ nodes: 1, volumes: 2, recently_ready: 1 }), + ).toEqual({ + title: "Cleanup finished", + description: `Node entries: 1, volume records: 2, volume claims: 0, jobs: 0.\n${recentlyReadyNote(1)}`, + variant: "default", + }); + }); + + it("keeps the error title when nodes were Ready too recently and deletes failed", async () => { + expect(await runCleanup({ failed: 1, recently_ready: 1 })).toEqual({ + title: "Cleanup finished with errors", + description: `${recentlyReadyNote(1)}\nObjects that could not be removed: 1. Check the API logs.`, + variant: "destructive", + }); + }); + + it("reports a request that failed", async () => { + const wrapper = await mountPage(async () => { + throw new Error("cleanupRemovedNodes: not found"); + }); + + await confirmCleanup(wrapper); + + expect(toast).toHaveBeenCalledTimes(1); + expect(toast).toHaveBeenCalledWith({ + title: "Cleanup failed", + description: "cleanupRemovedNodes: not found", + variant: "destructive", + }); + expect(cleanupButton(wrapper).attributes("disabled")).toBeUndefined(); + }); + + it("shows that it is running until the answer arrives", async () => { + let finish!: (value: unknown) => void; + const wrapper = await mountPage( + () => new Promise((resolve) => (finish = resolve)), + ); + + await confirmCleanup(wrapper); + + let button = cleanupButton(wrapper); + expect(button.attributes("disabled")).toBeDefined(); + expect(button.find('[role="status"]').exists()).toBe(true); + expect(button.text()).toBe("Cleaning up..."); + expect(toast).not.toHaveBeenCalled(); + + finish({ data: { cleanupRemovedNodes: NOTHING } }); + await flushPromises(); + + button = cleanupButton(wrapper); + expect(button.attributes("disabled")).toBeUndefined(); + expect(button.find('[role="status"]').exists()).toBe(false); + expect(button.text()).toBe("Clean Up"); + expect(toast).toHaveBeenCalledTimes(1); + }); +});