v0.248.0: i18n slice 1 release A — apps and settings in English, Hungarian byte-identical (R-556)
gates / gates (push) Successful in 20s

Ten pages converted against fixtures captured from unconverted templates (6be55a0); marker
coverage test; English page mask narrowed; English retrieval-promise stems; common keys;
title keys and infraMeta from the bundle; old wording tests read the Hungarian expansion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-17 17:36:08 +02:00
parent 6be55a0eb3
commit ff68b0b433
30 changed files with 1690 additions and 651 deletions
+6 -2
View File
@@ -111,7 +111,9 @@ func TestEmbeddedBundles(t *testing.T) {
}
var fieldRefRe = regexp.MustCompile(`\.[A-Z][A-Za-z0-9_]*(\.[A-Z][A-Za-z0-9_]*)*`)
var verbRe = regexp.MustCompile(`%[-+# 0-9.]*[sdvq]`)
// verbRe counts printf verbs OUTSIDE template actions ({{printf "%.0f" .X}} is a parameter, not a verb
// of the message), and without the space flag — „50% szabad" is prose, not „% s".
var verbRe = regexp.MustCompile(`%[-+#0-9.]*[sdvq]`)
func refs(s string) []string {
var out []string
@@ -153,10 +155,12 @@ func TestBundleParametersMatchAcrossLanguages(t *testing.T) {
if a, c := strings.Join(refs(hu), ","), strings.Join(refs(v), ","); a != c {
t.Errorf("%s %q: template fields %q, Hungarian has %q", lang, f, c, a)
}
if a, c := len(verbRe.FindAllString(hu, -1)), len(verbRe.FindAllString(v, -1)); a != c {
if a, c := len(verbRe.FindAllString(stripActions(hu), -1)), len(verbRe.FindAllString(stripActions(v), -1)); a != c {
t.Errorf("%s %q: %d printf verbs, Hungarian has %d", lang, f, c, a)
}
}
}
}
}
func stripActions(s string) string { return regexp.MustCompile(`\{\{.*?\}\}`).ReplaceAllString(s, "") }
+485 -20
View File
@@ -1,4 +1,20 @@
{
"common.bezaras": "Close",
"common.elavult": "Outdated",
"common.eltavolitas": "Remove",
"common.hdd_szukseges": "Needs a hard drive",
"common.hiba": "Error",
"common.hiba_kettospont": "Error:",
"common.hiba_tortent": "An error occurred",
"common.jelszo": "Password",
"common.levalasztva": "Disconnected",
"common.magyar_felulet": "Hungarian interface",
"common.megsem": "Cancel",
"common.pi_kompatibilis": "Runs on Pi",
"common.rendszer_gyoker": "System (/)",
"common.torles": "Delete",
"common.ugyfel_azonosito": "Customer ID",
"common.ujrainditas": "Restart",
"layout.html_lang": "en",
"launcher.inditopult": "Launcher",
"launcher.inditopult_megosztasa": "Share the launcher",
@@ -10,7 +26,6 @@
"launcher.megosztas": "Share…",
"launcher.qr_kod_a_megosztasi_linkhez": "QR code for the share link",
"launcher.olvassa_be_telefonnal_a_gyors": "Scan it with your phone to open it quickly.",
"launcher.jelszo": "Password",
"launcher.a_megosztas_jelszoval_vedett": "The share is protected by a password.",
"launcher.jelszo_torlese": "Remove password",
"launcher.jelszo_nem_kotelezo": "Password (optional)",
@@ -21,7 +36,6 @@
"launcher.biztosan_kikapcsolja_a_megosztast_a": "Turn off sharing? The link stops working immediately.",
"launcher.megosztas_kikapcsolasa": "Turn off sharing",
"launcher.a_megosztas_jelenleg_ki_van": "Sharing is off.",
"launcher.bezaras": "Close",
"launcher.megosztas_bekapcsolasa": "Turn on sharing",
"launcher.masolva": "Copied",
"launcher.inditopult_2": "Launcher — {{.Domain}}",
@@ -30,8 +44,6 @@
"backups.kijelolod_ezt_a_meghajtot_a": "Use this drive for the system backup?",
"backups.kijelolom": "Use this drive",
"backups.tarhely_attekintes": "Storage overview",
"backups.rendszer": "System (/)",
"backups.levalasztva": "Disconnected",
"backups.db_mentesek": "Database backups",
"backups.fajl": "{{$n := len .Backup.DumpFiles}}{{$n}} {{if eq $n 1}}file{{else}}files{{end}}",
"backups.rendszermentes_teljes_mentes": "System backup (full backup)",
@@ -68,9 +80,7 @@
"backups.beagyazott_db_k_a_kotetmentesben": "embedded databases, inside the volume backup",
"backups.elinditod_a_teljes_rendszermentest_most": "Start the full system backup now? Your apps stop during the backup — usually for a few minutes, longer with more data.",
"backups.mentes_inditasa": "Starting the backup…",
"backups.hiba": "Error",
"backups.mentes_folyamatban": "Backup in progress…",
"backups.hiba_2": "Error:",
"backups.allapot": "Status:",
"backups.a_rendszermentes_elkeszult": "The system backup is done.",
"backups.a_mentes_sikertelen": "The backup failed.",
@@ -79,20 +89,15 @@
"backups.a_meghajto_kijelolve_a_beallitas": "The drive is assigned. The setting takes effect after the host agent next restarts.",
"backups.a_meghajto_kijelolve": "The drive is assigned.",
"app_info.alkalmazasok": "← Apps",
"app_info.elavult": "Outdated",
"app_info.megnyitas": "Open ↗",
"app_info.naplo": "Log",
"app_info.torles": "Delete",
"app_info.exportalas": "Export",
"app_info.beallitasok": "Settings",
"app_info.telepites": "Install",
"app_info.mentesek": "Backups",
"app_info.hianyzo_tarhely": "Missing storage:",
"app_info.ennek_az_alkalmazasnak_az_adattaroloja": "This app’s data storage is not available right now, so the app is stopped. Connect the drive again, or move the data to another storage.",
"app_info.hdd_szukseges": "Needs a hard drive",
"app_info.pi_kompatibilis": "Runs on Pi",
"app_info.csak_x86": "x86 only",
"app_info.magyar_felulet": "Hungarian interface",
"app_info.az_alkalmazas_fejlesztoje_felhagyott_a": "The developer of this app has stopped working on it. The installed version still works,\n but it gets no more updates or security fixes.",
"app_info.hova_tegyem_a_fajlokat": "Where do I put my files?",
"app_info.ezeket_a_mappakat_a_fajlkezeloben": "You reach these folders in the File manager. You may need to sign in to the File manager first.",
@@ -113,14 +118,12 @@
"app_info.athelyezed_a_z": "Move the",
"app_info.adatait_ide": "data to:",
"app_info.az_alkalmazas_rovid_idore_leall": "? The app stops briefly. The old data is deleted only after a successful move.",
"app_info.hiba": "Error:",
"app_info.ismeretlen": "unknown",
"app_info.mire_hasznalhato": "What is it for?",
"app_info.elso_lepesek": "First steps",
"app_info.elofeltetelek": "Before you start",
"app_info.kezdeti_belepesi_adatok": "First sign-in details",
"app_info.felhasznalonev": "Username",
"app_info.jelszo": "Password",
"app_info.az_uzemelteto_allitotta_be": "set by the operator",
"app_info.megjelenites": "Show",
"app_info.masolas": "Copy",
@@ -169,7 +172,6 @@
"layout.ez_az_alkalmazas_mar_telepitve": "This app is already installed.",
"layout.frissites": "↻ Refreshing...",
"layout.sablonok_frissitve": "Templates refreshed",
"layout.hiba": "Error:",
"layout.ismeretlen_hiba": "Unknown error",
"layout.halozati_hiba": "Network error:",
"layout.folyamatban": "In progress...",
@@ -180,11 +182,7 @@
"layout.alkalmazas_torlese": "Delete app:",
"layout.ez_a_muvelet_eltavolitja_a": "This removes the containers, the volumes and the configuration files.",
"layout.ez_a_muvelet_nem_visszavonhato": "You cannot undo this!",
"layout.megsem": "Cancel",
"layout.torles": "Delete",
"layout.hiba_2": "Error",
"layout.nem_sikerult_lekerni_az_adatokat": "Could not load the details:",
"layout.bezaras": "Close",
"layout.torles_folyamatban": "Deleting...",
"layout.torolt_adatok": "Deleted data:",
"layout.megorzott_adatok": "Kept data:",
@@ -202,7 +200,6 @@
"layout.az_ejszakai_restic_pillanatkepek_nem": "The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.",
"layout.alkalmazas_eltavolitasa": "Remove app:",
"layout.az_alkalmazas_visszaall_nincs_telepitve": "The app goes back to \"Not installed\". The template stays, and you can install it again.",
"layout.eltavolitas": "Remove",
"layout.eltavolitas_folyamatban": "Removing...",
"layout.torolt_mentesek": "Deleted backups:",
"layout.nem_torolt_mentesek": "Backups not deleted:",
@@ -236,5 +233,473 @@
"func.restore_status.restoring": "Restoring...",
"func.restore_status.done": "Done",
"func.restore_status.failed": "Failed",
"func.restore_status.skipped": "Skipped"
"func.restore_status.skipped": "Skipped",
"dashboard.vezerlopult": "Dashboard",
"dashboard.futo_alkalmazas": "Running apps",
"dashboard.leallitva": "Stopped",
"dashboard.osszes_alkalmazas": "All apps",
"dashboard.memoria": "Memory",
"dashboard.homerseklet": "Temperature",
"dashboard.nem_ismert": "unknown",
"dashboard.a_tarhely_merete_most_nem": "The storage size cannot be read right now.",
"dashboard.fogyoban_a_hely": "Running out of space",
"dashboard.kritikusan_keves_hely": "Critically low on space",
"dashboard.lemezek_allapota": "Disk health",
"dashboard.nincs_adat": "No data",
"dashboard.biztonsagi_mentes": "Backup",
"dashboard.utolso_mentes": "Last backup:",
"dashboard.sikertelen": "Failed",
"dashboard.meg_nem_futott": "Not run yet",
"dashboard.adatbazisok": "Databases:",
"dashboard.mentve": "{{len .DBDumpStatus.Results}} backed up",
"dashboard.telepitett_alkalmazasok": "Installed apps",
"dashboard.az_alkalmazas_adattaroloja_nem_elerheto": "The app’s data storage is not available. Connect the drive again, or move the data to another storage.",
"dashboard.hianyzo_tarhely": "Missing storage: {{$ms}}",
"dashboard.az_alkalmazas_egyik_folyamatat_a": "The system stopped one of the app’s processes because it ran out of memory. A task may not have finished. Restart the app; if it happens again, tell the operator.",
"dashboard.memoria_elfogyott": "Out of memory",
"dashboard.az_alkalmazas_kornyezeteben_a_halozati": "In the app’s environment there is an empty local folder where the network storage should be — the data is not going to the NAS. Tell the operator.",
"dashboard.halozati_tarhely_hibas_az_alkalmazas": "Network storage broken — the app does not see the NAS",
"dashboard.a_halozati_tarhely_nas_jelenleg": "The network storage (NAS) is not reachable right now. The app is running; access to the data comes back when the NAS returns.",
"dashboard.halozati_tarhely_nem_elerheto": "Network storage not reachable: {{$nw}}",
"dashboard.a_proxy_traefik_csak_egeszseges": "The proxy (Traefik) publishes a public route only for a healthy container. While the app is not healthy, the URL returns 404, even though the container is running.",
"dashboard.url_nem_elerheto": "URL not reachable",
"dashboard.vedett": "Protected",
"dashboard.megnyitas": "Open",
"dashboard.telepites": "Install",
"dashboard.leallitas": "Stop",
"dashboard.inditas": "Start",
"dashboard.naplo": "Log",
"dashboard.nincs_elerheto_alkalmazas": "No app available.",
"stacks.alkalmazasok": "Apps",
"stacks.alkalmazas_visszaallitasa_exportalt_csom": "Restore an app from an exported package",
"stacks.importalas": "Import",
"stacks.sablonok_frissitese_a_kozponti_katalogus": "Refresh the templates from the central catalog",
"stacks.sablonok_frissitese": "↻ Refresh templates",
"stacks.futo": "Running",
"stacks.leallitva": "Stopped",
"stacks.telepitheto": "Installable",
"stacks.a_proxy_traefik_csak_egeszseges": "The proxy (Traefik) publishes a public route only for a healthy container. While the app is not healthy, the URL is not reachable (404), even though the container is running.",
"stacks.url_nem_elerheto_utvonal_nincs": "URL not reachable – route not published",
"stacks.az_alkalmazas_adattaroloja_nem_elerheto": "The app’s data storage is not available. Connect the drive again, or move the data.",
"stacks.hianyzo_tarhely": "Missing storage: {{$ms}}",
"stacks.az_alkalmazas_kornyezeteben_a_halozati": "In the app’s environment there is an empty local folder where the network storage should be — the data is not going to the NAS. Tell the operator.",
"stacks.halozati_tarhely_hibas_az_alkalmazas": "Network storage broken — the app does not see the NAS",
"stacks.a_halozati_tarhely_nas_jelenleg": "The network storage (NAS) is not reachable right now. The app is running; access to the data comes back when the NAS returns.",
"stacks.halozati_tarhely_nem_elerheto": "Network storage not reachable: {{$nw}}",
"stacks.adattarolo": "Data storage: {{index $.StorageLabels .Name}}",
"stacks.vedett_rendszerkomponens": "Protected system component",
"stacks.reszletek": "Details",
"stacks.telepites": "Install",
"stacks.mentesek": "Backups",
"stacks.frissites": "Update",
"stacks.leallitas": "Stop",
"stacks.inditas": "Start",
"stacks.naplok": "Logs",
"logs.vissza": "← Back",
"logs.naplok": "{{.Stack.Meta.DisplayName}} — Logs",
"logs.elo": "Live",
"logs.szuneteltetes": "Pause",
"logs.frissites": "Refresh",
"logs.szunetelve": "Paused",
"logs.folytatas": "Resume",
"monitoring.rendszermonitor": "System monitor",
"monitoring.szerver_allapota_gazdagep": "Server health (host)",
"monitoring.a_gazdagep_metrikai_jelenleg_nem": "The host metrics are not available right now.",
"monitoring.cpu_hasznalat": "CPU usage",
"monitoring.terheles_load": "Load",
"monitoring.memoria": "Memory",
"monitoring.cpu_homerseklet": "CPU temperature",
"monitoring.uzemido": "Uptime",
"monitoring.tarolok_kapacitasa": "Storage capacity",
"monitoring.rendszer_attekintes": "System overview",
"monitoring.gepnev": "Hostname",
"monitoring.operacios_rendszer": "Operating system",
"monitoring.inditas": "Boot time",
"monitoring.tarhely": "Storage",
"monitoring.hub_kapcsolat": "Hub connection",
"monitoring.kapcsolodva_a_kozponti_rendszer_aktivan": "Connected — the central system is actively watching the server.",
"monitoring.nem_elerheto_a_kozponti_rendszer": "Not reachable — the central system has not received a recent report.",
"monitoring.utolso_sikeres_jelentes": "Last successful report",
"monitoring.utolso_hiba": "Last error",
"monitoring.a_hub_kapcsolat_nincs_bekapcsolva": "The hub connection is not turned on — central monitoring is not active.",
"monitoring.rendszer_metrikak": "System metrics",
"monitoring.1_ora": "1 hour",
"monitoring.6_ora": "6 hours",
"monitoring.24_ora": "24 hours",
"monitoring.7_nap": "7 days",
"monitoring.30_nap": "30 days",
"monitoring.cpu_hasznalat_2": "CPU usage (%)",
"monitoring.memoria_hasznalat_gb": "Memory usage (GB)",
"monitoring.homerseklet_c": "Temperature (°C)",
"monitoring.terheles_load_average": "Load average",
"monitoring.meg_nincsenek_adatok_a_metrikak": "No data yet. Metric collection has started; the first data appears in a few minutes.",
"monitoring.memoria_eloszlas": "Memory breakdown",
"monitoring.alkalmazas_eroforrasok": "App resources",
"monitoring.memoria_hasznalat_mb": "Memory usage (MB)",
"monitoring.meg_nincsenek_kontener_adatok": "No container data yet.",
"monitoring.memoria_mb": "Memory (MB)",
"monitoring.hasznalt": "Used:",
"monitoring.rendszer_title": "System:",
"monitoring.rendszer_2": "System (",
"monitoring.szabad": "Free (",
"monitoring.eroforras_elozmenyek": "— Resource history",
"monitoring.nincs_tarolo_adat": "No storage data.",
"monitoring.nem_elerheto": "Not reachable",
"monitoring.kopas": "wear:",
"monitoring.frissitve": "Updated:",
"monitoring.nap": "d,",
"monitoring.ora": "h",
"monitoring.ora_2": "h,",
"monitoring.perc": "min",
"app_import.alkalmazasok": "&larr; Apps",
"app_import.alkalmazas_importalas": "Import an app",
"app_import.fab_csomag_feltoltese_huzza_ide": "Upload a .fab package &mdash; drag it here, or choose the file",
"app_import.fajl_kivalasztasa": "Choose file",
"app_import.feltoltes_0": "Upload: 0%",
"app_import.megszakitas": "Cancel upload",
"app_import.nem_talalhato_fab_csomag_a": "No .fab package found on the registered storages.",
"app_import.exportalj_egy_alkalmazast_az_alkalmazas": "Export an app from its page, upload a .fab file above, or copy one into the <code>{storage}/felhom-data/exports/</code> folder.",
"app_import.alkalmazas": "App",
"app_import.datum": "Date",
"app_import.meret": "Size",
"app_import.tarolo": "Storage",
"app_import.reszletek": "Details &raquo;",
"app_import.csomag_reszletei": "Package details",
"app_import.ez_a_csomag_jelszoval_vedett": "This package is protected by a password. Enter the password:",
"app_import.megnyitas": "Open",
"app_import.alkalmazas_2": "App:",
"app_import.exportalva": "Exported:",
"app_import.controller_verzio": "Controller version:",
"app_import.adatbazis": "Database:",
"app_import.adatok": "Data:",
"app_import.meret_2": "Size:",
"app_import.figyelem_a_meglevo": "<strong>&#9888; WARNING:</strong> The existing",
"app_import.alkalmazas_konfiguracioja_es_osszes_adat": "app’s configuration and all its data will be overwritten!",
"app_import.visszaallitas_inditasa": "Start restore",
"app_import.importalas_folyamata": "Import progress",
"app_import.importalas_kesz": "Import done!",
"app_import.az_alkalmazas_sikeresen_visszaallitva": "The app was restored.",
"app_import.alkalmazasok_megtekintese": "View apps",
"app_import.hibas_jelszo": "Wrong password",
"app_import.csomag_reszletei_2": "— Package details",
"app_import.nincs": "None",
"app_import.alkalmazas_megtekintese": "View app",
"app_import.a_feltoltes_megszakadt_ellenorizze_a": "The upload stopped — check the connection, then try again.",
"app_import.feltoltes": "Upload:",
"app_import.csak_fab_fajl_toltheto_fel": "Only a .fab file can be uploaded.",
"app_import.feltoltes_kesz": "Upload done:",
"app_export.alkalmazasok": "&larr; Apps",
"app_export.exportalas": "{{.Stack.Meta.DisplayName}} &mdash; Export",
"app_export.mentes_helye": "Save to",
"app_export.valassz_tarolot": "Choose a storage...",
"app_export.konfiguracio": "Configuration:",
"app_export.felhasznaloi_adatok": "Your data:",
"app_export.osszesen": "Total:",
"app_export.szabad": "(free: -)",
"app_export.becsult_ido": "Estimated time:",
"app_export.mindig_resze_a_mentesnek": "Always part of the export:",
"app_export.valaszthato_tartalom": "Optional content:",
"app_export.a_kihagyott_mappak_tartalma_a": "The content of the skipped folders is always reachable in the File manager, and you can save it separately.",
"app_export.kihagyott_tartalom_megjelenitese": "Show skipped content",
"app_export.jelszo_opcionalis": "Password (optional)",
"app_export.titkositasi_jelszo": "Encryption password",
"app_export.jelszo_mutatasa": "Show password",
"app_export.alkalmazas_leallitasa_mentes_elott_ajanl": "Stop the app before the export (recommended)",
"app_export.az_adatok_konzisztenciaja_erdekeben_java": "To keep the data consistent, stop the app during the export.",
"app_export.exportalas_inditasa": "Start export",
"app_export.kesz": "Done!",
"app_export.megnyitas_filebrowser_ben": "Open in FileBrowser &nearr;",
"app_export.szabad_2": "(free:",
"app_export.perc": "min",
"app_export.nincs_eleg_szabad_hely_a": "Not enough free space on the chosen storage!",
"app_export.alap_mentes": "Base export: ~",
"app_export.a_kihagyott_nagy_meretu_tartalommal": ". With the large skipped content: ~",
"app_export.kihagyott_tartalom_elrejtese": "Hide skipped content",
"deploy.vissza": "← Back",
"deploy.beallitasok": "Settings",
"deploy.telepites": "Install",
"deploy.leallitas": "Stop",
"deploy.inditas": "Start",
"deploy.megnyitas": "Open ↗",
"deploy.reszletek": "Details",
"deploy.reszletes_leiras_kepernyokepek": "Full description, screenshots",
"deploy.ez_az_alkalmazas_mar_telepitve": "This app is already installed. The settings below are read-only.",
"deploy.adattarolas": "Data storage",
"deploy.tarhely": "Storage",
"deploy.adatmeret": "Data size",
"deploy.szabad_hely": "Free space",
"deploy.szabad": "{{.StorageInfo.FreeHuman}} ({{printf \"%.0f\" .StorageInfo.FreePercent}}% free)",
"deploy.mozgatas_masik_tarolora": "Move to another storage",
"deploy.korabbi_adatok": "Earlier data",
"deploy.az_alkalmazas_adatainak_masolata_megtala": "A copy of this app’s data is also on another storage.\n This is usually left behind after a move.",
"deploy.meret": "Size",
"deploy.mappak": "Folders",
"deploy.korabbi_adatok_torlese": "Delete earlier data",
"deploy.biztonsagi_mentes": "Backup",
"deploy.az_alkalmazas_adatbazisa_es_docker": "The app’s database and Docker volumes go into the nightly backup automatically.\n <a href=\"/backups\" style=\"color:var(--blue)\">Backup status →</a> · <a href=\"/stacks/{{.Stack.Name}}/backup\" style=\"color:var(--blue)\">Backup settings →</a>",
"deploy.integraciok": "Integrations",
"deploy.mas_telepitett_alkalmazasokkal_valo_ossz": "Connections to other installed apps. An integration pauses by itself when either app stops, and turns on again when it starts.",
"deploy.nincs_telepitve": "Not installed",
"deploy.celalkalmazas_leallitva": "Target app stopped",
"deploy.aktiv": "Active",
"deploy.a_celalkalmazas_nincs_telepitve": "The target app is not installed",
"deploy.a_celalkalmazas_nem_fut": "The target app is not running",
"deploy.halozati_hiba": "Network error",
"deploy.foldrajzi_korlatozas": "Country restriction",
"deploy.az_alkalmazas_egyeni_orszagkorlatozas_ne": "Without its own country restriction, the app follows the global setting.",
"deploy.egyeni_orszagkorlatozas": "Own country restriction",
"deploy.engedelyezett_orszagok_ehhez_az_alkalmaz": "Allowed countries for this app",
"deploy.orszag_keresese": "Search country...",
"deploy.mentes": "Save",
"deploy.nincs_talalat": "No match",
"deploy.mentve": "Saved",
"deploy.opcionalis_beallitasok": "Optional settings",
"deploy.regisztracios_utmutato": "Sign-up guide ↗",
"deploy.mentes_2": "Saving...",
"deploy.email_kuldes": "Sending e-mail",
"deploy.ez_az_alkalmazas_tud_emailt": "This app can send e-mail (for example password resets, invitations) through Felhom,\n without setting up a separate provider. The sender address: <strong>{{.AppEmailFromAddress}}</strong>.",
"deploy.az_alkalmazas_email_jelenleg_ki": "App e-mail is turned off globally right now. Turn it on on the\n <a href=\"/settings\">Settings</a> page for it to work here too.",
"deploy.email_kuldes_engedelyezese_ennel_az": "Allow sending e-mail for this app",
"deploy.a_mentes_ujrainditja_az_alkalmazast": "Saving restarts the app.",
"deploy.nincs_eleg_memoria_foglalas_telepites": "Not enough memory! Reserved after install: {{.AfterMB}} MB / {{.UsableMB}} MB",
"deploy.memoria": "Memory",
"deploy.jelenlegi_hasznalat_mb": "Current usage: {{.UsedMB}} MB",
"deploy.jelenlegi_hasznalat_mb_2": "Current usage ({{.UsedMB}} MB)",
"deploy.az_alkalmazasok_csucsterhelese_meghaladh": "The apps’ peak load can go above the available memory.\n In normal use this causes no problem.",
"deploy.ennek_az_alkalmazasnak_van_korabbi": "This app has an earlier backup, so we filled in the fields below from its own backup.",
"deploy.korabbi_webcim": "Earlier web address: <strong>{{.RestoreRecordedAddress}}</strong>.",
"deploy.ha_most_mashova_szeretned_telepiteni": "If you want to install it somewhere else now, change it — we will point out the difference before the restore.",
"deploy.az_alkalmazas_adatai_az_alabb": "The app’s data goes to the data drive chosen below.",
"deploy.hol_lesznek_az_adatok_ennel": "<strong>Where the data goes:</strong> this app has no separate data drive choice,\n so its data goes to the system drive (<code>{{.SystemDataPath}}</code>). Its backups are made all the same.",
"deploy.nincs_eleg_szabad_tarhely_a": "Not enough free storage for the install. {{.DockerFreeHuman}} is free now, and the system keeps\n {{.DockerReserveHuman}} in reserve to protect the core services (controller, proxy).\n So the install is blocked for now — free up space, or add storage.",
"deploy.automatikusan_generalt_ertekek": "Automatically generated values",
"deploy.ezek_az_ertekek_automatikusan_jottek": "These values were created automatically at install.",
"deploy.ezek_az_ertekek_a_telepitessel": "These values are saved together with the install. Write down the passwords you need!",
"deploy.automatikusan_generalva": "Generated automatically",
"deploy.megjelenites": "Show",
"deploy.telepites_utan_nem_modosithato": "cannot be changed after install",
"deploy.az_aldomain_telepites_utan_nem": "The subdomain cannot be changed after install. To change it, remove the app and install it again (all data is deleted).",
"deploy.generalas": "Generate",
"deploy.telepiteskor_beallitott_kezdeti_jelszo_h": "The first password set at install — if you changed it in the app, it is not updated here.",
"deploy.jelszo_megerositese": "Confirm password",
"deploy.igen": "Yes",
"deploy.szabad_2": "{{.Label}} — {{.FreeHuman}} free",
"deploy.alapertelmezett": "(default)",
"deploy.nem_elerheto": "{{$.CurrentHDDPath}} (not available)",
"deploy.a_kivalasztott_tarhely_majdnem_megtelt": "The chosen storage is almost full.",
"deploy.a_kivalasztott_meghajton_az_alkalmazas": "The chosen drive stores the app’s <strong>files</strong> (media, documents).\n The <strong>database runs on the fast internal SSD</strong> — and it is backed up together with the app.",
"deploy.nincs_regisztralt_adattarolo_adja_meg": "No registered data storage — enter the path by hand",
"deploy.nincs_eleg_szabad_tarhely": "Not enough free storage",
"deploy.nincs_eleg_memoria": "Not enough memory",
"deploy.telepites_inditasa": "Start install",
"deploy.telepites_folyamatban": "Installing...",
"deploy.konfiguracio_mentese": "Saving the configuration...",
"deploy.kontener_ek_inditasa": "Starting the container(s)...",
"deploy.alkalmazas_inicializalasa": "Initialising the app...",
"deploy.alkalmazas_megnyitasa": "Open the app ↗",
"deploy.elso_lepesek": "First steps",
"deploy.masolas": "Copy",
"deploy.bejelentkezes": "Sign-in",
"deploy.dokumentacio": "Documentation ↗",
"deploy.beallitasok_es_jelszavak_megtekintese": "View settings and passwords",
"deploy.alkalmazasok": "← Apps",
"deploy.a_lekeres_nem_sikerult": "The request failed.",
"deploy.elrejtes": "Hide",
"deploy.masolva": "Copied!",
"deploy.biztosan_torolni_szeretned_a_korabbi": "Delete the earlier data? Storage:",
"deploy.figyelem_a_muvelet_visszavonhatatlan_elo": "— WARNING: this cannot be undone! First make sure the app works properly from the new storage.",
"deploy.utolso_figyelmeztetes_a_torles_visszavon": "LAST WARNING! The deletion cannot be undone. Continue?",
"deploy.torles_folyamatban": "Deleting...",
"deploy.ismeretlen_hiba": "Unknown error",
"deploy.korabbi_adatok_torolve_n_nfelszabaditott": "Earlier data deleted!\\n\\nSpace freed:",
"deploy.n_nnehany_hiba_tortent_n": "\\n\\nSome errors occurred:\\n",
"deploy.halozati_hiba_2": "Network error:",
"deploy.kotelezo_mezo": "Required field:",
"deploy.nhasznalja_a_generalas_gombot_vagy": "\\nUse the Generate button or type a password.",
"deploy.a_ket_jelszo_nem_egyezik": "The two passwords do not match:",
"deploy.az_aldomain_csak_kisbetuket_szamokat": "The subdomain may contain only lowercase letters, digits and hyphens, and cannot start or end with a hyphen.",
"deploy.konfiguracio_mentve": "Configuration saved",
"deploy.masodperce": "seconds ago...",
"deploy.idotullepes_az_alkalmazas_meg_indulhat": "Timed out — the app may still be starting",
"deploy.a_telepites_idotullepesbe_futott_az": "The install timed out. The app may still be starting.",
"deploy.telepites_sikertelen": "Install failed",
"deploy.a_telepites_nem_sikerult": "The install failed:",
"deploy.naplok_megtekintese": "View logs",
"deploy.alkalmazasok_2": "Apps",
"deploy.kepek_letoltese_kontenerek_inditasa": "Downloading images, starting containers...",
"deploy.kontenerek_elindultak": "Containers started",
"deploy.alkalmazas_kesz": "App ready!",
"deploy.telepites_sikeres": "Install succeeded!",
"deploy.az_alkalmazas_sikeresen_telepitve_es": "The app is installed and running!",
"deploy.allapotjelzo_meg_inicializal": "Health check: still initialising",
"deploy.az_alkalmazas_elindult_de_meg": "The app started, but it is still initialising.",
"deploy.ez_normalis_az_elso_percekben": "This is normal in the first minutes — the app may already be reachable.",
"deploy.a_kontener_leallt": "The container stopped",
"deploy.a_kontener_leallt_ellenorizze_a": "The container stopped. Check the logs.",
"settings_system.beallitasok_rendszer": "Settings — System",
"settings_system.rendszer_konfiguracio": "System configuration",
"settings_system.az_uzemelteto_altal_beallitott_ertekek": "Values set by the operator. Ask the operator to change them.",
"settings_system.ugyfel_neve": "Customer name",
"settings_system.alkalmazas_sablon_forras": "App template source",
"settings_system.sablon_szinkronizalas": "Template sync",
"settings_system.biztonsagi_mentes": "Backup",
"settings_system.aktiv": "Active",
"settings_system.inaktiv": "Inactive",
"settings_system.mentes_utemezes": "Backup schedule",
"settings_system.hub_jelentes": "Hub reporting",
"settings_system.verzio_es_frissites": "Version and update",
"settings_system.jelenlegi_verzio": "Current version",
"settings_system.legujabb_verzio": "Latest version",
"settings_system.frissites_elerheto": "● Update available",
"settings_system.naprakesz": "— up to date",
"settings_system.utolso_ellenorzes": "Last check",
"settings_system.nyilvanos_hitelesites_nelkul": "public (no authentication)",
"settings_system.hitelesitett": "authenticated",
"settings_system.automatikus_frissites": "Automatic update",
"settings_system.minimalis_verzio_uzemelteto": "Minimum version (operator)",
"settings_system.a_rendszer_automatikusan_erre_a": "— the system updates to this version automatically if it is older",
"settings_system.allapot": "Status",
"settings_system.frissites_folyamatban_a_vezerlo_hamarosa": "Update in progress — the controller restarts soon…",
"settings_system.utolso_frissites": "Last update",
"settings_system.sikeres": "Succeeded",
"settings_system.sikertelen": "Failed",
"settings_system.folyamatban": "In progress",
"settings_system.frissites_keresese": "Check for updates",
"settings_system.frissites_telepitese": "Install update",
"settings_system.halozat": "Network",
"settings_system.helyi_cim_lan": "Local address (LAN)",
"settings_system.nem_allapithato_meg": "cannot be determined",
"settings_system.halozati_nev": "Network name",
"settings_system.atjaro": "Gateway",
"settings_system.ezek_az_ertekek_a_helyi": "These values depend on the local network and can change. For remote troubleshooting, we ask you to read out this page.",
"settings_system.szerver_memoria_ram": "Server memory (RAM)",
"settings_system.jelenlegi_memoria": "Current memory",
"settings_system.felhasznalt": "Used",
"settings_system.megengedett_tartomany": "Allowed range",
"settings_system.uj_meret_mb": "New size (MB)",
"settings_system.atmeretezes": "Resize",
"settings_system.a_kiszolgalo_ugynok_jelenleg_nem": "The host agent is not reachable right now — try again later.",
"settings_system.a_memoria_atmeretezesehez_a_kiszolgalo": "Resizing memory needs a system update on the host.",
"settings_system.megerosites": "Confirm",
"settings_system.ellenorzes": "Checking...",
"settings_system.kapcsolodasi_hiba": "Connection error",
"settings_system.vezerlo_frissitese": "Update controller",
"settings_system.frissites": "Update",
"settings_system.biztosan_frissiti_a_controllert_n": "Update the controller?\\n\\nThe dashboard is unreachable for a short time during the update.",
"settings_system.frissites_2": "Updating...",
"settings_system.frissites_folyamatban": "Update in progress...",
"settings_system.ujraindulas": "Restarting...",
"settings_system.ervenytelen_memoria_ertek": "Invalid memory value",
"settings_system.memoria_csokkentese": "Reduce memory",
"settings_system.csokkentes": "Reduce",
"settings_system.a_memoria_csokkentese_hatassal_lehet": "Reducing memory can affect the performance of running apps. Continue?",
"settings_system.atmeretezes_2": "Resizing...",
"settings_system.kesz": "Done.",
"settings_system.a_muvelet_nem_sikerult": "The operation failed",
"settings_system.vezerlo_ujrainditasa": "Restart controller",
"settings_system.ha_a_vezerlo_hibasan_mukodik": "If the controller misbehaves, you can safely restart it here — no need to restart the whole server.\n Your apps keep running; only the controller restarts (a few seconds).",
"settings_system.kiszolgalo_ujrainditasa": "Restart server",
"settings_system.az_egesz_kiszolgalo_szerver_ujrainditasa": "Restarts the whole server. Every app stops for a short time, and the dashboard is unreachable for about 30 seconds. Use it when the whole system needs a restart — otherwise “Restart controller” above is enough.",
"settings_system.biztosan_ujrainditja_a_vezerlot_a": "Restart the controller? It takes a few seconds, and the page is unreachable for a short time.",
"settings_system.ujrainditas_folyamatban_ujracsatlakozas": "Restarting… reconnecting…",
"settings_system.biztosan_ujrainditja_a_kiszolgalot_az": "Restart the server? The apps and the dashboard become unreachable for about 30 seconds.",
"settings_system.ujrainditas_folyamatban_a_vezerlopult_ne": "Restarting… the dashboard reloads in a few seconds.",
"settings_system.az_ujrainditas_a_vartnal_tovabb": "The restart is taking longer than expected. Reload the page by hand.",
"settings_security.beallitasok_biztonsag_es_hozzaferes": "Settings — Security and access",
"settings_security.jelszo_modositas": "Change password",
"settings_security.jelenlegi_jelszo": "Current password",
"settings_security.adja_meg_a_jelenlegi_jelszavat": "Enter your current password",
"settings_security.uj_jelszo": "New password",
"settings_security.legalabb_8_karakter": "At least 8 characters",
"settings_security.uj_jelszo_megerositese": "Confirm new password",
"settings_security.jelszo_megegyszer": "Password again",
"settings_security.jelszo_modositasa": "Change password",
"settings_security.a_jelszavas_vedelem_nincs_beallitva": "Password protection is not set up. Ask the operator to set it up.",
"settings_security.foldrajzi_korlatozas": "Country restriction",
"settings_security.orszag_alapjan_korlatozhato_a_webes": "Access to the web apps can be limited by country with the Cloudflare WAF.",
"settings_security.a_helyi_halozati_hozzaferes_mindig": "Access from the local network is always allowed (it does not pass through Cloudflare).",
"settings_security.a_cloudflare_api_token_nincs": "The Cloudflare API token is not configured. Ask the operator to set it up.<br>\n <small>The token needs the <strong>Zone WAF:Edit</strong> permission.</small>",
"settings_security.geo_korlatozas_aktiv": "Country restriction active",
"settings_security.engedelyezett_orszagok_globalis": "Allowed countries (global)",
"settings_security.orszag_keresese": "Search country...",
"settings_security.csak_a_kivalasztott_orszagokbol_erheto": "The system is reachable only from the chosen countries.",
"settings_security.alkalmazas_specifikus_felulirasok": "Per-app overrides",
"settings_security.alkalmazas_kivalasztasa": "— Choose an app —",
"settings_security.hozzaadas": "+ Add",
"settings_security.mentes_es_szinkronizalas": "Save and sync",
"settings_security.kezi_szinkronizalas": "Sync by hand",
"settings_security.utolso_szinkronizalas": "Last sync: {{.GeoLastSync}}",
"settings_security.megerosites": "Confirm",
"settings_security.nincs_talalat": "No match",
"settings_security.figyelem_magyarorszag_eltavolitasa_azt_j": "Warning: removing Hungary means the system is not reachable remotely from a Hungarian IP either. Continue?",
"settings_security.magyarorszag_eltavolitasa_nem_ajanlott_f": "Removing Hungary is not recommended. Continue?",
"settings_security.nincs_alkalmazas_specifikus_beallitas_mi": "No per-app setting. Every app follows the global setting.",
"settings_security.szerkesztes": "Edit",
"settings_security.engedelyezett_orszagok": "Allowed countries:",
"settings_security.kesz": "Done",
"settings_security.mentes": "Saving...",
"settings_security.mentve": "Saved",
"settings_security.halozati_hiba": "Network error",
"settings_security.szinkronizalas_elinditva": "Sync started...",
"settings_security.utolso": "Last:",
"settings_security.veszhelyzeti_informaciok": "Emergency information",
"settings_security.ezeket_az_adatokat_mentse_el": "Keep these details in a safe place. You need them to restore the system after a reinstall.",
"settings_security.visszaallitasi_jelszo": "Retrieval password",
"settings_security.megjelenit": "Show",
"settings_security.tamogatas": "Support",
"settings_security.felhom_eu_kapcsolat": "<a href=\"mailto:{{.SupportEmail}}\" style=\"color: var(--blue);\">{{.SupportEmail}}</a>\n &nbsp;|&nbsp;\n <a href=\"{{.SupportURL}}\" target=\"_blank\" style=\"color: var(--blue);\">felhom.eu/kapcsolat</a>",
"settings_security.a_visszaallitasi_jelszo_lekerese_nem": "The retrieval password could not be loaded.",
"settings_security.elrejt": "Hide",
"settings_notifications.beallitasok_ertesitesek": "Settings — Notifications",
"settings_notifications.ertesitesek": "Notifications",
"settings_notifications.e_mail_cim": "E-mail address",
"settings_notifications.hibak_es_figyelmeztetesek": "Errors and warnings:",
"settings_notifications.biztonsagi_mentes_sikertelen": "Backup failed",
"settings_notifications.adatbazis_mentes_sikertelen": "Database backup failed",
"settings_notifications.mentes_serules_eszlelve": "Backup damage detected",
"settings_notifications.masodlagos_mentes_sikertelen": "Secondary backup failed",
"settings_notifications.tavoli_mentes_tarhelykeret_figyelmeztete": "Remote backup — storage quota warning",
"settings_notifications.lemez_beteloben_90_felett": "Disk filling up (over 90%)",
"settings_notifications.lemez_megtelt_vagy_meghibasodott": "Disk full or failed",
"settings_notifications.meghajto_levalasztva": "Drive disconnected",
"settings_notifications.szerver_nem_elerheto": "Server not reachable",
"settings_notifications.rendszer_allapot_kritikus": "System health critical",
"settings_notifications.elvart_fajlmentes_elmaradt": "Expected file backup missed",
"settings_notifications.elvart_adatbazismentes_elmaradt": "Expected database backup missed",
"settings_notifications.alkalmazas_nem_fut": "App not running",
"settings_notifications.tajekoztato": "For information:",
"settings_notifications.meghajto_ujra_csatlakoztatva": "Drive connected again",
"settings_notifications.rendszer_allapot_helyreallt": "System health recovered",
"settings_notifications.ertesitesi_szunet": "Notification pause",
"settings_notifications.ora_azonos_problema_eseten_ennyi": "hours (for the same problem, no new message is sent for this long)",
"settings_notifications.mentes": "Save",
"settings_notifications.teszt_email_kuldese": "Send test e-mail",
"settings_notifications.az_ertesitesek_a_kozponti_rendszeren": "Notifications work through the central system, which is not turned on right now.",
"settings_notifications.alkalmazas_email": "App e-mail",
"settings_notifications.az_alkalmazasok_a_felhom_on": "Apps can send e-mail through Felhom (for example password resets, invitations),\n without setting up a separate e-mail provider. Each app’s sender address is its own\n <em>&lt;app&gt;@felhom.eu</em> address.",
"settings_notifications.alkalmazas_email_engedelyezese": "Allow app e-mail",
"settings_notifications.felado_neve_opcionalis": "Sender name (optional)",
"settings_notifications.pl_a_haztartas_neve": "for example the household name",
"settings_notifications.ez_jelenik_meg_a_kimeno": "This appears as the sender of outgoing e-mails, next to the app name.",
"settings_notifications.bekapcsolas_utan_az_egyes_alkalmazasokna": "After turning it on, allow sending e-mail for each app too (on the app’s page).",
"stacks.mind": "All",
"app_export.folyamat": "Progress",
"app_import.titkos": "Encrypted",
"monitoring.processzor": "Processor",
"deploy.hamarosan": "Coming soon",
"deploy.az_adatai_itt_voltak": "Its data was here: <strong>{{.RestoreRecordedDrive}}</strong>.",
"deploy.aldomain_placeholder": "subdomain",
"settings_security.figyelem": "Warning",
"settings_notifications.pelda_email": "example@email.com",
"page.title.dashboard": "Dashboard",
"page.title.stacks": "Apps",
"page.title.monitoring": "System monitor",
"page.title.settings": "Settings",
"page.title.settings_notifications": "Notifications",
"page.title.settings_security": "Security and access",
"func.infra.cloudflared.description": "Secure internet connection — the server is reachable from outside without opening ports.",
"func.infra.traefik.description": "Traffic router (reverse proxy) — sends each request to the right app.",
"func.infra.filebrowser.description": "File manager — browse the storage files from your browser.",
"func.infra.samba.display": "Network sharing",
"func.infra.samba.description": "File sharing on the local network — the folders appear in Windows Explorer and the Mac Finder."
}
+485 -20
View File
@@ -1,4 +1,20 @@
{
"common.bezaras": "Bezárás",
"common.elavult": "Elavult",
"common.eltavolitas": "Eltávolítás",
"common.hdd_szukseges": "HDD szükséges",
"common.hiba": "Hiba",
"common.hiba_kettospont": "Hiba:",
"common.hiba_tortent": "Hiba történt",
"common.jelszo": "Jelszó",
"common.levalasztva": "Leválasztva",
"common.magyar_felulet": "Magyar felület",
"common.megsem": "Mégsem",
"common.pi_kompatibilis": "Pi kompatibilis",
"common.rendszer_gyoker": "Rendszer (/)",
"common.torles": "Törlés",
"common.ugyfel_azonosito": "Ügyfél azonosító",
"common.ujrainditas": "Újraindítás",
"layout.html_lang": "hu",
"launcher.inditopult": "Indítópult",
"launcher.inditopult_megosztasa": "Indítópult megosztása",
@@ -10,7 +26,6 @@
"launcher.megosztas": "Megosztás…",
"launcher.qr_kod_a_megosztasi_linkhez": "QR-kód a megosztási linkhez",
"launcher.olvassa_be_telefonnal_a_gyors": "Olvassa be telefonnal a gyors megnyitáshoz.",
"launcher.jelszo": "Jelszó",
"launcher.a_megosztas_jelszoval_vedett": "A megosztás jelszóval védett.",
"launcher.jelszo_torlese": "Jelszó törlése",
"launcher.jelszo_nem_kotelezo": "Jelszó (nem kötelező)",
@@ -21,7 +36,6 @@
"launcher.biztosan_kikapcsolja_a_megosztast_a": "Biztosan kikapcsolja a megosztást? A link azonnal érvénytelenné válik.",
"launcher.megosztas_kikapcsolasa": "Megosztás kikapcsolása",
"launcher.a_megosztas_jelenleg_ki_van": "A megosztás jelenleg ki van kapcsolva.",
"launcher.bezaras": "Bezárás",
"launcher.megosztas_bekapcsolasa": "Megosztás bekapcsolása",
"launcher.masolva": "Másolva",
"launcher.inditopult_2": "Indítópult — {{.Domain}}",
@@ -30,8 +44,6 @@
"backups.kijelolod_ezt_a_meghajtot_a": "Kijelölöd ezt a meghajtót a rendszermentés helyéül?",
"backups.kijelolom": "Kijelölöm",
"backups.tarhely_attekintes": "Tárhely áttekintés",
"backups.rendszer": "Rendszer (/)",
"backups.levalasztva": "Leválasztva",
"backups.db_mentesek": "DB mentések",
"backups.fajl": "{{len .Backup.DumpFiles}} fájl",
"backups.rendszermentes_teljes_mentes": "Rendszermentés (teljes mentés)",
@@ -68,9 +80,7 @@
"backups.beagyazott_db_k_a_kotetmentesben": "beágyazott DB-k a kötetmentésben",
"backups.elinditod_a_teljes_rendszermentest_most": "Elindítod a teljes rendszermentést most? A mentés alatt az alkalmazások leállnak — általában néhány perc, nagyobb adatnál több.",
"backups.mentes_inditasa": "Mentés indítása…",
"backups.hiba": "Hiba",
"backups.mentes_folyamatban": "Mentés folyamatban…",
"backups.hiba_2": "Hiba:",
"backups.allapot": "Állapot:",
"backups.a_rendszermentes_elkeszult": "A rendszermentés elkészült.",
"backups.a_mentes_sikertelen": "A mentés sikertelen.",
@@ -79,20 +89,15 @@
"backups.a_meghajto_kijelolve_a_beallitas": "A meghajtó kijelölve. A beállítás a host-ügynök következő újraindulása után lép életbe.",
"backups.a_meghajto_kijelolve": "A meghajtó kijelölve.",
"app_info.alkalmazasok": "← Alkalmazások",
"app_info.elavult": "Elavult",
"app_info.megnyitas": "Megnyitás ↗",
"app_info.naplo": "Napló",
"app_info.torles": "Törlés",
"app_info.exportalas": "Exportálás",
"app_info.beallitasok": "Beállítások",
"app_info.telepites": "Telepítés",
"app_info.mentesek": "Mentések",
"app_info.hianyzo_tarhely": "Hiányzó tárhely:",
"app_info.ennek_az_alkalmazasnak_az_adattaroloja": "Ennek az alkalmazásnak az adattárolója jelenleg nem elérhető, ezért le van állítva. Csatlakoztasd újra a meghajtót, vagy helyezd át az adatokat egy másik tárhelyre.",
"app_info.hdd_szukseges": "HDD szükséges",
"app_info.pi_kompatibilis": "Pi kompatibilis",
"app_info.csak_x86": "Csak x86",
"app_info.magyar_felulet": "Magyar felület",
"app_info.az_alkalmazas_fejlesztoje_felhagyott_a": "Az alkalmazás fejlesztője felhagyott a fejlesztéssel. A telepített verzió továbbra is használható,\n de frissítések és biztonsági javítások már nem érkeznek hozzá.",
"app_info.hova_tegyem_a_fajlokat": "Hova tegyem a fájlokat?",
"app_info.ezeket_a_mappakat_a_fajlkezeloben": "Ezeket a mappákat a Fájlkezelőben éred el. Előfordulhat, hogy először be kell jelentkezned a Fájlkezelőbe.",
@@ -113,14 +118,12 @@
"app_info.athelyezed_a_z": "Áthelyezed a(z)",
"app_info.adatait_ide": "adatait ide:",
"app_info.az_alkalmazas_rovid_idore_leall": "? Az alkalmazás rövid időre leáll. A régi adatok csak sikeres áthelyezés után törlődnek.",
"app_info.hiba": "Hiba:",
"app_info.ismeretlen": "ismeretlen",
"app_info.mire_hasznalhato": "Mire használható?",
"app_info.elso_lepesek": "Első lépések",
"app_info.elofeltetelek": "Előfeltételek",
"app_info.kezdeti_belepesi_adatok": "Kezdeti belépési adatok",
"app_info.felhasznalonev": "Felhasználónév",
"app_info.jelszo": "Jelszó",
"app_info.az_uzemelteto_allitotta_be": "az üzemeltető állította be",
"app_info.megjelenites": "Megjelenítés",
"app_info.masolas": "Másolás",
@@ -169,7 +172,6 @@
"layout.ez_az_alkalmazas_mar_telepitve": "Ez az alkalmazás már telepítve van.",
"layout.frissites": "↻ Frissítés...",
"layout.sablonok_frissitve": "Sablonok frissítve",
"layout.hiba": "Hiba:",
"layout.ismeretlen_hiba": "Ismeretlen hiba",
"layout.halozati_hiba": "Hálózati hiba:",
"layout.folyamatban": "Folyamatban...",
@@ -180,11 +182,7 @@
"layout.alkalmazas_torlese": "Alkalmazás törlése:",
"layout.ez_a_muvelet_eltavolitja_a": "Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.",
"layout.ez_a_muvelet_nem_visszavonhato": "Ez a művelet nem visszavonható!",
"layout.megsem": "Mégsem",
"layout.torles": "Törlés",
"layout.hiba_2": "Hiba",
"layout.nem_sikerult_lekerni_az_adatokat": "Nem sikerült lekérni az adatokat:",
"layout.bezaras": "Bezárás",
"layout.torles_folyamatban": "Törlés folyamatban...",
"layout.torolt_adatok": "Törölt adatok:",
"layout.megorzott_adatok": "Megőrzött adatok:",
@@ -202,7 +200,6 @@
"layout.az_ejszakai_restic_pillanatkepek_nem": "Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.",
"layout.alkalmazas_eltavolitasa": "Alkalmazás eltávolítása:",
"layout.az_alkalmazas_visszaall_nincs_telepitve": "Az alkalmazás visszaáll \"Nincs telepítve\" állapotba. A sablon megmarad, újratelepíthető.",
"layout.eltavolitas": "Eltávolítás",
"layout.eltavolitas_folyamatban": "Eltávolítás folyamatban...",
"layout.torolt_mentesek": "Törölt mentések:",
"layout.nem_torolt_mentesek": "Nem törölt mentések:",
@@ -233,5 +230,473 @@
"func.restore_status.restoring": "Visszaállítás...",
"func.restore_status.done": "Kész",
"func.restore_status.failed": "Sikertelen",
"func.restore_status.skipped": "Kihagyva"
"func.restore_status.skipped": "Kihagyva",
"dashboard.vezerlopult": "Vezérlőpult",
"dashboard.futo_alkalmazas": "Futó alkalmazás",
"dashboard.leallitva": "Leállítva",
"dashboard.osszes_alkalmazas": "Összes alkalmazás",
"dashboard.memoria": "Memória",
"dashboard.homerseklet": "Hőmérséklet",
"dashboard.nem_ismert": "nem ismert",
"dashboard.a_tarhely_merete_most_nem": "A tárhely mérete most nem olvasható ki.",
"dashboard.fogyoban_a_hely": "Fogyóban a hely",
"dashboard.kritikusan_keves_hely": "Kritikusan kevés hely",
"dashboard.lemezek_allapota": "Lemezek állapota",
"dashboard.nincs_adat": "Nincs adat",
"dashboard.biztonsagi_mentes": "Biztonsági mentés",
"dashboard.utolso_mentes": "Utolsó mentés:",
"dashboard.sikertelen": "Sikertelen",
"dashboard.meg_nem_futott": "Még nem futott",
"dashboard.adatbazisok": "Adatbázisok:",
"dashboard.mentve": "{{len .DBDumpStatus.Results}} mentve",
"dashboard.telepitett_alkalmazasok": "Telepített alkalmazások",
"dashboard.az_alkalmazas_adattaroloja_nem_elerheto": "Az alkalmazás adattárolója nem elérhető. Csatlakoztasd újra a meghajtót, vagy helyezd át az adatokat egy másik tárhelyre.",
"dashboard.hianyzo_tarhely": "Hiányzó tárhely: {{$ms}}",
"dashboard.az_alkalmazas_egyik_folyamatat_a": "Az alkalmazás egyik folyamatát a rendszer leállította, mert elfogyott a memóriája. Lehet, hogy egy feladat nem fejeződött be. Indítsd újra az alkalmazást; ha ismétlődik, jelezd az üzemeltetőnek.",
"dashboard.memoria_elfogyott": "Memória elfogyott",
"dashboard.az_alkalmazas_kornyezeteben_a_halozati": "Az alkalmazás környezetében a hálózati tárhely helyén üres helyi könyvtár van — az adatok nem a NAS-ra kerülnek. Jelezze az üzemeltetőnek.",
"dashboard.halozati_tarhely_hibas_az_alkalmazas": "Hálózati tárhely hibás — az alkalmazás nem a NAS-t látja",
"dashboard.a_halozati_tarhely_nas_jelenleg": "A hálózati tárhely (NAS) jelenleg nem érhető el. Az alkalmazás fut; az adatok elérése a NAS visszatértével helyreáll.",
"dashboard.halozati_tarhely_nem_elerheto": "Hálózati tárhely nem elérhető: {{$nw}}",
"dashboard.a_proxy_traefik_csak_egeszseges": "A proxy (Traefik) csak egészséges konténerhez publikál nyilvános útvonalat. Amíg az alkalmazás nem egészséges, az URL 404-et ad, pedig a konténer fut.",
"dashboard.url_nem_elerheto": "URL nem elérhető",
"dashboard.vedett": "Védett",
"dashboard.megnyitas": "Megnyitás",
"dashboard.telepites": "Telepítés",
"dashboard.leallitas": "Leállítás",
"dashboard.inditas": "Indítás",
"dashboard.naplo": "Napló",
"dashboard.nincs_elerheto_alkalmazas": "Nincs elérhető alkalmazás.",
"stacks.alkalmazasok": "Alkalmazások",
"stacks.alkalmazas_visszaallitasa_exportalt_csom": "Alkalmazás visszaállítása exportált csomagból",
"stacks.importalas": "Importálás",
"stacks.sablonok_frissitese_a_kozponti_katalogus": "Sablonok frissítése a központi katalógusból",
"stacks.sablonok_frissitese": "↻ Sablonok frissítése",
"stacks.futo": "Futó",
"stacks.leallitva": "Leállítva",
"stacks.telepitheto": "Telepíthető",
"stacks.a_proxy_traefik_csak_egeszseges": "A proxy (Traefik) csak egészséges konténerhez publikál nyilvános útvonalat. Amíg az alkalmazás nem egészséges, az URL nem érhető el (404), pedig a konténer fut.",
"stacks.url_nem_elerheto_utvonal_nincs": "URL nem elérhető – útvonal nincs publikálva",
"stacks.az_alkalmazas_adattaroloja_nem_elerheto": "Az alkalmazás adattárolója nem elérhető. Csatlakoztasd újra a meghajtót, vagy helyezd át az adatokat.",
"stacks.hianyzo_tarhely": "Hiányzó tárhely: {{$ms}}",
"stacks.az_alkalmazas_kornyezeteben_a_halozati": "Az alkalmazás környezetében a hálózati tárhely helyén üres helyi könyvtár van — az adatok nem a NAS-ra kerülnek. Jelezze az üzemeltetőnek.",
"stacks.halozati_tarhely_hibas_az_alkalmazas": "Hálózati tárhely hibás — az alkalmazás nem a NAS-t látja",
"stacks.a_halozati_tarhely_nas_jelenleg": "A hálózati tárhely (NAS) jelenleg nem érhető el. Az alkalmazás fut; az adatok elérése a NAS visszatértével helyreáll.",
"stacks.halozati_tarhely_nem_elerheto": "Hálózati tárhely nem elérhető: {{$nw}}",
"stacks.adattarolo": "Adattároló: {{index $.StorageLabels .Name}}",
"stacks.vedett_rendszerkomponens": "Védett rendszerkomponens",
"stacks.reszletek": "Részletek",
"stacks.telepites": "Telepítés",
"stacks.mentesek": "Mentések",
"stacks.frissites": "Frissítés",
"stacks.leallitas": "Leállítás",
"stacks.inditas": "Indítás",
"stacks.naplok": "Naplók",
"logs.vissza": "← Vissza",
"logs.naplok": "{{.Stack.Meta.DisplayName}} — Naplók",
"logs.elo": "Élő",
"logs.szuneteltetes": "Szüneteltetés",
"logs.frissites": "Frissítés",
"logs.szunetelve": "Szünetelve",
"logs.folytatas": "Folytatás",
"monitoring.rendszermonitor": "Rendszermonitor",
"monitoring.szerver_allapota_gazdagep": "Szerver állapota (gazdagép)",
"monitoring.a_gazdagep_metrikai_jelenleg_nem": "A gazdagép metrikái jelenleg nem elérhetők.",
"monitoring.cpu_hasznalat": "CPU használat",
"monitoring.terheles_load": "Terhelés (load)",
"monitoring.memoria": "Memória",
"monitoring.cpu_homerseklet": "CPU hőmérséklet",
"monitoring.uzemido": "Üzemidő",
"monitoring.tarolok_kapacitasa": "Tárolók kapacitása",
"monitoring.rendszer_attekintes": "Rendszer áttekintés",
"monitoring.gepnev": "Gépnév",
"monitoring.operacios_rendszer": "Operációs rendszer",
"monitoring.inditas": "Indítás",
"monitoring.tarhely": "Tárhely",
"monitoring.hub_kapcsolat": "Hub kapcsolat",
"monitoring.kapcsolodva_a_kozponti_rendszer_aktivan": "Kapcsolódva — a központi rendszer aktívan figyeli a szervert.",
"monitoring.nem_elerheto_a_kozponti_rendszer": "Nem elérhető — a központi rendszer nem kapott friss jelentést.",
"monitoring.utolso_sikeres_jelentes": "Utolsó sikeres jelentés",
"monitoring.utolso_hiba": "Utolsó hiba",
"monitoring.a_hub_kapcsolat_nincs_bekapcsolva": "A Hub kapcsolat nincs bekapcsolva — a központi monitoring nem aktív.",
"monitoring.rendszer_metrikak": "Rendszer metrikák",
"monitoring.1_ora": "1 óra",
"monitoring.6_ora": "6 óra",
"monitoring.24_ora": "24 óra",
"monitoring.7_nap": "7 nap",
"monitoring.30_nap": "30 nap",
"monitoring.cpu_hasznalat_2": "CPU használat (%)",
"monitoring.memoria_hasznalat_gb": "Memória használat (GB)",
"monitoring.homerseklet_c": "Hőmérséklet (°C)",
"monitoring.terheles_load_average": "Terhelés (Load Average)",
"monitoring.meg_nincsenek_adatok_a_metrikak": "Még nincsenek adatok. A metrikák gyűjtése elindult, az első adatok néhány perc múlva megjelennek.",
"monitoring.memoria_eloszlas": "Memória eloszlás",
"monitoring.alkalmazas_eroforrasok": "Alkalmazás erőforrások",
"monitoring.memoria_hasznalat_mb": "Memória használat (MB)",
"monitoring.meg_nincsenek_kontener_adatok": "Még nincsenek konténer adatok.",
"monitoring.memoria_mb": "Memória (MB)",
"monitoring.hasznalt": "Használt:",
"monitoring.rendszer_title": "Rendszer:",
"monitoring.rendszer_2": "Rendszer (",
"monitoring.szabad": "Szabad (",
"monitoring.eroforras_elozmenyek": "— Erőforrás előzmények",
"monitoring.nincs_tarolo_adat": "Nincs tároló adat.",
"monitoring.nem_elerheto": "Nem elérhető",
"monitoring.kopas": "kopás:",
"monitoring.frissitve": "Frissítve:",
"monitoring.nap": "nap,",
"monitoring.ora": "óra",
"monitoring.ora_2": "óra,",
"monitoring.perc": "perc",
"app_import.alkalmazasok": "&larr; Alkalmazások",
"app_import.alkalmazas_importalas": "Alkalmazás importálás",
"app_import.fab_csomag_feltoltese_huzza_ide": ".fab csomag feltöltése &mdash; húzza ide, vagy válassza ki a fájlt",
"app_import.fajl_kivalasztasa": "Fájl kiválasztása",
"app_import.feltoltes_0": "Feltöltés: 0%",
"app_import.megszakitas": "Megszakítás",
"app_import.nem_talalhato_fab_csomag_a": "Nem található .fab csomag a regisztrált tárolókon.",
"app_import.exportalj_egy_alkalmazast_az_alkalmazas": "Exportálj egy alkalmazást az alkalmazás oldaláról, tölts fel egy .fab fájlt itt fent, vagy másolj egyet a <code>{tároló}/felhom-data/exports/</code> könyvtárba.",
"app_import.alkalmazas": "Alkalmazás",
"app_import.datum": "Dátum",
"app_import.meret": "Méret",
"app_import.tarolo": "Tároló",
"app_import.reszletek": "Részletek &raquo;",
"app_import.csomag_reszletei": "Csomag részletei",
"app_import.ez_a_csomag_jelszoval_vedett": "Ez a csomag jelszóval védett. Kérlek add meg a jelszót:",
"app_import.megnyitas": "Megnyitás",
"app_import.alkalmazas_2": "Alkalmazás:",
"app_import.exportalva": "Exportálva:",
"app_import.controller_verzio": "Controller verzió:",
"app_import.adatbazis": "Adatbázis:",
"app_import.adatok": "Adatok:",
"app_import.meret_2": "Méret:",
"app_import.figyelem_a_meglevo": "<strong>&#9888; FIGYELEM:</strong> A meglévő",
"app_import.alkalmazas_konfiguracioja_es_osszes_adat": "alkalmazás konfigurációja és összes adata felül lesz írva!",
"app_import.visszaallitas_inditasa": "Visszaállítás indítása",
"app_import.importalas_folyamata": "Importálás folyamata",
"app_import.importalas_kesz": "Importálás kész!",
"app_import.az_alkalmazas_sikeresen_visszaallitva": "Az alkalmazás sikeresen visszaállítva.",
"app_import.alkalmazasok_megtekintese": "Alkalmazások megtekintése",
"app_import.hibas_jelszo": "Hibás jelszó",
"app_import.csomag_reszletei_2": "— Csomag részletei",
"app_import.nincs": "Nincs",
"app_import.alkalmazas_megtekintese": "Alkalmazás megtekintése",
"app_import.a_feltoltes_megszakadt_ellenorizze_a": "A feltöltés megszakadt — ellenőrizze a kapcsolatot, majd próbálja újra.",
"app_import.feltoltes": "Feltöltés:",
"app_import.csak_fab_fajl_toltheto_fel": "Csak .fab fájl tölthető fel.",
"app_import.feltoltes_kesz": "Feltöltés kész:",
"app_export.alkalmazasok": "&larr; Alkalmazások",
"app_export.exportalas": "{{.Stack.Meta.DisplayName}} &mdash; Exportálás",
"app_export.mentes_helye": "Mentés helye",
"app_export.valassz_tarolot": "Válassz tárolót...",
"app_export.konfiguracio": "Konfiguráció:",
"app_export.felhasznaloi_adatok": "Felhasználói adatok:",
"app_export.osszesen": "Összesen:",
"app_export.szabad": "(szabad: -)",
"app_export.becsult_ido": "Becsült idő:",
"app_export.mindig_resze_a_mentesnek": "Mindig része a mentésnek:",
"app_export.valaszthato_tartalom": "Választható tartalom:",
"app_export.a_kihagyott_mappak_tartalma_a": "A kihagyott mappák tartalma a Fájlkezelőben bármikor elérhető, és külön is lementhető.",
"app_export.kihagyott_tartalom_megjelenitese": "Kihagyott tartalom megjelenítése",
"app_export.jelszo_opcionalis": "Jelszó (opcionális)",
"app_export.titkositasi_jelszo": "Titkosítási jelszó",
"app_export.jelszo_mutatasa": "Jelszó mutatása",
"app_export.alkalmazas_leallitasa_mentes_elott_ajanl": "Alkalmazás leállítása mentés előtt (ajánlott)",
"app_export.az_adatok_konzisztenciaja_erdekeben_java": "Az adatok konzisztenciája érdekében javasolt leállítani az alkalmazást mentés közben.",
"app_export.exportalas_inditasa": "Exportálás indítása",
"app_export.kesz": "Kész!",
"app_export.megnyitas_filebrowser_ben": "Megnyitás FileBrowser-ben &nearr;",
"app_export.szabad_2": "(szabad:",
"app_export.perc": "perc",
"app_export.nincs_eleg_szabad_hely_a": "Nincs elég szabad hely a kiválasztott tárolón!",
"app_export.alap_mentes": "Alap mentés: ~",
"app_export.a_kihagyott_nagy_meretu_tartalommal": ". A kihagyott, nagy méretű tartalommal együtt: ~",
"app_export.kihagyott_tartalom_elrejtese": "Kihagyott tartalom elrejtése",
"deploy.vissza": "← Vissza",
"deploy.beallitasok": "Beállítások",
"deploy.telepites": "Telepítés",
"deploy.leallitas": "Leállítás",
"deploy.inditas": "Indítás",
"deploy.megnyitas": "Megnyitás ↗",
"deploy.reszletek": "Részletek",
"deploy.reszletes_leiras_kepernyokepek": "Részletes leírás, képernyőképek",
"deploy.ez_az_alkalmazas_mar_telepitve": "Ez az alkalmazás már telepítve van. Az alábbi beállítások csak olvashatók.",
"deploy.adattarolas": "Adattárolás",
"deploy.tarhely": "Tárhely",
"deploy.adatmeret": "Adatméret",
"deploy.szabad_hely": "Szabad hely",
"deploy.szabad": "{{.StorageInfo.FreeHuman}} ({{printf \"%.0f\" .StorageInfo.FreePercent}}% szabad)",
"deploy.mozgatas_masik_tarolora": "Mozgatás másik tárolóra",
"deploy.korabbi_adatok": "Korábbi adatok",
"deploy.az_alkalmazas_adatainak_masolata_megtala": "Az alkalmazás adatainak másolata megtalálható egy másik tárolón is.\n Ez általában áthelyezés után marad hátra.",
"deploy.meret": "Méret",
"deploy.mappak": "Mappák",
"deploy.korabbi_adatok_torlese": "Korábbi adatok törlése",
"deploy.biztonsagi_mentes": "Biztonsági mentés",
"deploy.az_alkalmazas_adatbazisa_es_docker": "Az alkalmazás adatbázisa és Docker kötetei automatikusan bekerülnek az éjszakai biztonsági mentésbe.\n <a href=\"/backups\" style=\"color:var(--blue)\">Mentési állapot →</a> · <a href=\"/stacks/{{.Stack.Name}}/backup\" style=\"color:var(--blue)\">Mentési beállítások →</a>",
"deploy.integraciok": "Integrációk",
"deploy.mas_telepitett_alkalmazasokkal_valo_ossz": "Más telepített alkalmazásokkal való összekapcsolás. Az integráció automatikusan felfüggesztődik, ha bármelyik alkalmazás leáll, és újraaktiválódik indításkor.",
"deploy.nincs_telepitve": "Nincs telepítve",
"deploy.celalkalmazas_leallitva": "Célalkalmazás leállítva",
"deploy.aktiv": "Aktív",
"deploy.a_celalkalmazas_nincs_telepitve": "A célalkalmazás nincs telepítve",
"deploy.a_celalkalmazas_nem_fut": "A célalkalmazás nem fut",
"deploy.halozati_hiba": "Hálózati hiba",
"deploy.foldrajzi_korlatozas": "Földrajzi korlátozás",
"deploy.az_alkalmazas_egyeni_orszagkorlatozas_ne": "Az alkalmazás egyéni országkorlátozás nélkül a globális beállítást követi.",
"deploy.egyeni_orszagkorlatozas": "Egyéni országkorlátozás",
"deploy.engedelyezett_orszagok_ehhez_az_alkalmaz": "Engedélyezett országok ehhez az alkalmazáshoz",
"deploy.orszag_keresese": "Ország keresése...",
"deploy.mentes": "Mentés",
"deploy.nincs_talalat": "Nincs találat",
"deploy.mentve": "Mentve",
"deploy.opcionalis_beallitasok": "Opcionális beállítások",
"deploy.regisztracios_utmutato": "Regisztrációs útmutató ↗",
"deploy.mentes_2": "Mentés...",
"deploy.email_kuldes": "Email-küldés",
"deploy.ez_az_alkalmazas_tud_emailt": "Ez az alkalmazás tud emailt küldeni (pl. jelszó-visszaállítás, meghívók) a Felhom-on keresztül,\n külön szolgáltató beállítása nélkül. A feladó címe: <strong>{{.AppEmailFromAddress}}</strong>.",
"deploy.az_alkalmazas_email_jelenleg_ki": "Az alkalmazás-email jelenleg ki van kapcsolva globálisan. Kapcsold be a\n <a href=\"/settings\">Beállítások</a> oldalon, hogy itt is működjön.",
"deploy.email_kuldes_engedelyezese_ennel_az": "Email-küldés engedélyezése ennél az alkalmazásnál",
"deploy.a_mentes_ujrainditja_az_alkalmazast": "A mentés újraindítja az alkalmazást.",
"deploy.nincs_eleg_memoria_foglalas_telepites": "Nincs elég memória! Foglalás telepítés után: {{.AfterMB}} MB / {{.UsableMB}} MB",
"deploy.memoria": "Memória",
"deploy.jelenlegi_hasznalat_mb": "Jelenlegi használat: {{.UsedMB}} MB",
"deploy.jelenlegi_hasznalat_mb_2": "Jelenlegi használat ({{.UsedMB}} MB)",
"deploy.az_alkalmazasok_csucsterhelese_meghaladh": "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát.\n Normál használat mellett ez nem okoz problémát.",
"deploy.ennek_az_alkalmazasnak_van_korabbi": "Ennek az alkalmazásnak van korábbi mentése, ezért az alábbi mezőket a saját mentése alapján töltöttük ki.",
"deploy.korabbi_webcim": "Korábbi webcím: <strong>{{.RestoreRecordedAddress}}</strong>.",
"deploy.ha_most_mashova_szeretned_telepiteni": "Ha most máshová szeretnéd telepíteni, nyugodtan átírhatod — a visszaállítás előtt jelezni fogjuk az eltérést.",
"deploy.az_alkalmazas_adatai_az_alabb": "Az alkalmazás adatai az alább kiválasztott adatmeghajtóra kerülnek.",
"deploy.hol_lesznek_az_adatok_ennel": "<strong>Hol lesznek az adatok:</strong> ennél az alkalmazásnál nincs külön adatmeghajtó-választás,\n ezért az adatai a rendszermeghajtóra kerülnek (<code>{{.SystemDataPath}}</code>). A mentései így is elkészülnek.",
"deploy.nincs_eleg_szabad_tarhely_a": "Nincs elég szabad tárhely a telepítéshez. Jelenleg {{.DockerFreeHuman}} szabad, és a rendszer\n {{.DockerReserveHuman}} tartalékot tart fenn az alapszolgáltatások (vezérlő, proxy) védelmében.\n A telepítés ezért átmenetileg le van tiltva — szabadítson fel helyet, vagy bővítse a tárhelyet.",
"deploy.automatikusan_generalt_ertekek": "Automatikusan generált értékek",
"deploy.ezek_az_ertekek_automatikusan_jottek": "Ezek az értékek automatikusan jöttek létre a telepítéskor.",
"deploy.ezek_az_ertekek_a_telepitessel": "Ezek az értékek a telepítéssel együtt mentésre kerülnek. Jegyezze fel a szükséges jelszavakat!",
"deploy.automatikusan_generalva": "Automatikusan generálva",
"deploy.megjelenites": "Megjelenítés",
"deploy.telepites_utan_nem_modosithato": "telepítés után nem módosítható",
"deploy.az_aldomain_telepites_utan_nem": "Az aldomain telepítés után nem módosítható. Megváltoztatáshoz az alkalmazás eltávolítása és újratelepítése szükséges (minden adat törlődik).",
"deploy.generalas": "Generálás",
"deploy.telepiteskor_beallitott_kezdeti_jelszo_h": "Telepítéskor beállított kezdeti jelszó — ha az alkalmazásban megváltoztattad, az itt nem frissül.",
"deploy.jelszo_megerositese": "Jelszó megerősítése",
"deploy.igen": "Igen",
"deploy.szabad_2": "{{.Label}} — {{.FreeHuman}} szabad",
"deploy.alapertelmezett": "(alapértelmezett)",
"deploy.nem_elerheto": "{{$.CurrentHDDPath}} (nem elérhető)",
"deploy.a_kivalasztott_tarhely_majdnem_megtelt": "A kiválasztott tárhely majdnem megtelt.",
"deploy.a_kivalasztott_meghajton_az_alkalmazas": "A kiválasztott meghajtón az alkalmazás <strong>fájljai</strong> (média, dokumentumok) tárolódnak.\n Az <strong>adatbázis a gyors belső SSD-n</strong> fut — és az alkalmazással együtt készül róla biztonsági mentés.",
"deploy.nincs_regisztralt_adattarolo_adja_meg": "Nincs regisztrált adattároló — adja meg kézzel az útvonalat",
"deploy.nincs_eleg_szabad_tarhely": "Nincs elég szabad tárhely",
"deploy.nincs_eleg_memoria": "Nincs elég memória",
"deploy.telepites_inditasa": "Telepítés indítása",
"deploy.telepites_folyamatban": "Telepítés folyamatban...",
"deploy.konfiguracio_mentese": "Konfiguráció mentése...",
"deploy.kontener_ek_inditasa": "Konténer(ek) indítása...",
"deploy.alkalmazas_inicializalasa": "Alkalmazás inicializálása...",
"deploy.alkalmazas_megnyitasa": "Alkalmazás megnyitása ↗",
"deploy.elso_lepesek": "Első lépések",
"deploy.masolas": "Másolás",
"deploy.bejelentkezes": "Bejelentkezés",
"deploy.dokumentacio": "Dokumentáció ↗",
"deploy.beallitasok_es_jelszavak_megtekintese": "Beállítások és jelszavak megtekintése",
"deploy.alkalmazasok": "← Alkalmazások",
"deploy.a_lekeres_nem_sikerult": "A lekérés nem sikerült.",
"deploy.elrejtes": "Elrejtés",
"deploy.masolva": "Másolva!",
"deploy.biztosan_torolni_szeretned_a_korabbi": "Biztosan törölni szeretnéd a korábbi adatokat? Tárhely:",
"deploy.figyelem_a_muvelet_visszavonhatatlan_elo": "— FIGYELEM: a művelet visszavonhatatlan! Előtte győződj meg róla, hogy az alkalmazás az új tárolóról megfelelően működik.",
"deploy.utolso_figyelmeztetes_a_torles_visszavon": "UTOLSÓ FIGYELMEZTETÉS! A törlés visszavonhatatlan. Biztosan folytatod?",
"deploy.torles_folyamatban": "Törlés folyamatban...",
"deploy.ismeretlen_hiba": "Ismeretlen hiba",
"deploy.korabbi_adatok_torolve_n_nfelszabaditott": "Korábbi adatok törölve!\\n\\nFelszabadított hely:",
"deploy.n_nnehany_hiba_tortent_n": "\\n\\nNéhány hiba történt:\\n",
"deploy.halozati_hiba_2": "Hálózati hiba:",
"deploy.kotelezo_mezo": "Kötelező mező:",
"deploy.nhasznalja_a_generalas_gombot_vagy": "\\nHasználja a Generálás gombot vagy írjon be egy jelszót.",
"deploy.a_ket_jelszo_nem_egyezik": "A két jelszó nem egyezik:",
"deploy.az_aldomain_csak_kisbetuket_szamokat": "Az aldomain csak kisbetűket, számokat és kötőjelet tartalmazhat, és nem kezdődhet/végződhet kötőjellel.",
"deploy.konfiguracio_mentve": "Konfiguráció mentve",
"deploy.masodperce": "másodperce...",
"deploy.idotullepes_az_alkalmazas_meg_indulhat": "Időtúllépés — az alkalmazás még indulhat",
"deploy.a_telepites_idotullepesbe_futott_az": "A telepítés időtúllépésbe futott. Az alkalmazás még indulhat.",
"deploy.telepites_sikertelen": "Telepítés sikertelen",
"deploy.a_telepites_nem_sikerult": "A telepítés nem sikerült:",
"deploy.naplok_megtekintese": "Naplók megtekintése",
"deploy.alkalmazasok_2": "Alkalmazások",
"deploy.kepek_letoltese_kontenerek_inditasa": "Képek letöltése, konténerek indítása...",
"deploy.kontenerek_elindultak": "Konténerek elindultak",
"deploy.alkalmazas_kesz": "Alkalmazás kész!",
"deploy.telepites_sikeres": "Telepítés sikeres!",
"deploy.az_alkalmazas_sikeresen_telepitve_es": "Az alkalmazás sikeresen telepítve és fut!",
"deploy.allapotjelzo_meg_inicializal": "Állapotjelző: még inicializál",
"deploy.az_alkalmazas_elindult_de_meg": "Az alkalmazás elindult, de még inicializálódik.",
"deploy.ez_normalis_az_elso_percekben": "Ez normális az első percekben — az alkalmazás már elérhető lehet.",
"deploy.a_kontener_leallt": "A konténer leállt",
"deploy.a_kontener_leallt_ellenorizze_a": "A konténer leállt. Ellenőrizze a naplókat.",
"settings_system.beallitasok_rendszer": "Beállítások — Rendszer",
"settings_system.rendszer_konfiguracio": "Rendszer konfiguráció",
"settings_system.az_uzemelteto_altal_beallitott_ertekek": "Az üzemeltető által beállított értékek. Módosításhoz kérd az üzemeltetőt.",
"settings_system.ugyfel_neve": "Ügyfél neve",
"settings_system.alkalmazas_sablon_forras": "Alkalmazás sablon forrás",
"settings_system.sablon_szinkronizalas": "Sablon szinkronizálás",
"settings_system.biztonsagi_mentes": "Biztonsági mentés",
"settings_system.aktiv": "Aktív",
"settings_system.inaktiv": "Inaktív",
"settings_system.mentes_utemezes": "Mentés ütemezés",
"settings_system.hub_jelentes": "Hub jelentés",
"settings_system.verzio_es_frissites": "Verzió és frissítés",
"settings_system.jelenlegi_verzio": "Jelenlegi verzió",
"settings_system.legujabb_verzio": "Legújabb verzió",
"settings_system.frissites_elerheto": "● Frissítés elérhető",
"settings_system.naprakesz": "— naprakész",
"settings_system.utolso_ellenorzes": "Utolsó ellenőrzés",
"settings_system.nyilvanos_hitelesites_nelkul": "nyilvános (hitelesítés nélkül)",
"settings_system.hitelesitett": "hitelesített",
"settings_system.automatikus_frissites": "Automatikus frissítés",
"settings_system.minimalis_verzio_uzemelteto": "Minimális verzió (üzemeltető)",
"settings_system.a_rendszer_automatikusan_erre_a": "— a rendszer automatikusan erre a verzióra frissít, ha régebbi",
"settings_system.allapot": "Állapot",
"settings_system.frissites_folyamatban_a_vezerlo_hamarosa": "Frissítés folyamatban — a vezérlő hamarosan újraindul…",
"settings_system.utolso_frissites": "Utolsó frissítés",
"settings_system.sikeres": "Sikeres",
"settings_system.sikertelen": "Sikertelen",
"settings_system.folyamatban": "Folyamatban",
"settings_system.frissites_keresese": "Frissítés keresése",
"settings_system.frissites_telepitese": "Frissítés telepítése",
"settings_system.halozat": "Hálózat",
"settings_system.helyi_cim_lan": "Helyi cím (LAN)",
"settings_system.nem_allapithato_meg": "nem állapítható meg",
"settings_system.halozati_nev": "Hálózati név",
"settings_system.atjaro": "Átjáró",
"settings_system.ezek_az_ertekek_a_helyi": "Ezek az értékek a helyi hálózattól függenek és változhatnak. Távoli hibaelhárításnál ezt az oldalt kérjük felolvasni.",
"settings_system.szerver_memoria_ram": "Szerver memória (RAM)",
"settings_system.jelenlegi_memoria": "Jelenlegi memória",
"settings_system.felhasznalt": "Felhasznált",
"settings_system.megengedett_tartomany": "Megengedett tartomány",
"settings_system.uj_meret_mb": "Új méret (MB)",
"settings_system.atmeretezes": "Átméretezés",
"settings_system.a_kiszolgalo_ugynok_jelenleg_nem": "A kiszolgáló ügynök jelenleg nem elérhető — próbáld meg később.",
"settings_system.a_memoria_atmeretezesehez_a_kiszolgalo": "A memória átméretezéséhez a kiszolgáló rendszerfrissítése szükséges.",
"settings_system.megerosites": "Megerősítés",
"settings_system.ellenorzes": "Ellenőrzés...",
"settings_system.kapcsolodasi_hiba": "Kapcsolódási hiba",
"settings_system.vezerlo_frissitese": "Vezérlő frissítése",
"settings_system.frissites": "Frissítés",
"settings_system.biztosan_frissiti_a_controllert_n": "Biztosan frissíti a controllert?\\n\\nA folyamat alatt a vezérlőpult rövid időre elérhetetlenné válik.",
"settings_system.frissites_2": "Frissítés...",
"settings_system.frissites_folyamatban": "Frissítés folyamatban...",
"settings_system.ujraindulas": "Újraindulás...",
"settings_system.ervenytelen_memoria_ertek": "Érvénytelen memória érték",
"settings_system.memoria_csokkentese": "Memória csökkentése",
"settings_system.csokkentes": "Csökkentés",
"settings_system.a_memoria_csokkentese_hatassal_lehet": "A memória csökkentése hatással lehet a futó alkalmazások teljesítményére. Biztosan folytatod?",
"settings_system.atmeretezes_2": "Átméretezés...",
"settings_system.kesz": "Kész.",
"settings_system.a_muvelet_nem_sikerult": "A művelet nem sikerült",
"settings_system.vezerlo_ujrainditasa": "Vezérlő újraindítása",
"settings_system.ha_a_vezerlo_hibasan_mukodik": "Ha a vezérlő hibásan működik, itt biztonságosan újraindíthatja — nem kell az egész szervert újraindítani.\n Az alkalmazásai futnak tovább; csak a vezérlő indul újra (néhány másodperc).",
"settings_system.kiszolgalo_ujrainditasa": "Kiszolgáló újraindítása",
"settings_system.az_egesz_kiszolgalo_szerver_ujrainditasa": "Az egész kiszolgáló (szerver) újraindítása. Minden alkalmazás rövid időre leáll, és a vezérlőpult kb. 30 másodpercig nem elérhető. Akkor használja, ha a teljes rendszer újraindítására van szükség — egyébként a fenti „Vezérlő újraindítása” elegendő.",
"settings_system.biztosan_ujrainditja_a_vezerlot_a": "Biztosan újraindítja a vezérlőt? A művelet néhány másodpercig tart, és a felület rövid időre elérhetetlen lesz.",
"settings_system.ujrainditas_folyamatban_ujracsatlakozas": "Újraindítás folyamatban… újracsatlakozás…",
"settings_system.biztosan_ujrainditja_a_kiszolgalot_az": "Biztosan újraindítja a kiszolgálót? Az alkalmazások és a vezérlőpult kb. 30 másodpercre elérhetetlenné válnak.",
"settings_system.ujrainditas_folyamatban_a_vezerlopult_ne": "Újraindítás folyamatban… a vezérlőpult néhány másodperc múlva újratölt.",
"settings_system.az_ujrainditas_a_vartnal_tovabb": "Az újraindítás a vártnál tovább tart. Töltse újra az oldalt kézzel.",
"settings_security.beallitasok_biztonsag_es_hozzaferes": "Beállítások — Biztonság és hozzáférés",
"settings_security.jelszo_modositas": "Jelszó módosítás",
"settings_security.jelenlegi_jelszo": "Jelenlegi jelszó",
"settings_security.adja_meg_a_jelenlegi_jelszavat": "Adja meg a jelenlegi jelszavát",
"settings_security.uj_jelszo": "Új jelszó",
"settings_security.legalabb_8_karakter": "Legalább 8 karakter",
"settings_security.uj_jelszo_megerositese": "Új jelszó megerősítése",
"settings_security.jelszo_megegyszer": "Jelszó mégegyszer",
"settings_security.jelszo_modositasa": "Jelszó módosítása",
"settings_security.a_jelszavas_vedelem_nincs_beallitva": "A jelszavas védelem nincs beállítva. Kérd az üzemeltetőt a beállításhoz.",
"settings_security.foldrajzi_korlatozas": "Földrajzi korlátozás",
"settings_security.orszag_alapjan_korlatozhato_a_webes": "Ország alapján korlátozható a webes alkalmazások elérése a Cloudflare WAF segítségével.",
"settings_security.a_helyi_halozati_hozzaferes_mindig": "A helyi hálózati hozzáférés mindig engedélyezett (nem halad át a Cloudflare-en).",
"settings_security.a_cloudflare_api_token_nincs": "A Cloudflare API token nincs konfigurálva. Kérd az üzemeltetőt a beállításhoz.<br>\n <small>A tokennek <strong>Zone WAF:Edit</strong> jogosultsággal kell rendelkeznie.</small>",
"settings_security.geo_korlatozas_aktiv": "Geo-korlátozás aktív",
"settings_security.engedelyezett_orszagok_globalis": "Engedélyezett országok (globális)",
"settings_security.orszag_keresese": "Ország keresése...",
"settings_security.csak_a_kivalasztott_orszagokbol_erheto": "Csak a kiválasztott országokból érhető el a rendszer.",
"settings_security.alkalmazas_specifikus_felulirasok": "Alkalmazás-specifikus felülírások",
"settings_security.alkalmazas_kivalasztasa": "— Alkalmazás kiválasztása —",
"settings_security.hozzaadas": "+ Hozzáadás",
"settings_security.mentes_es_szinkronizalas": "Mentés és szinkronizálás",
"settings_security.kezi_szinkronizalas": "Kézi szinkronizálás",
"settings_security.utolso_szinkronizalas": "Utolsó szinkronizálás: {{.GeoLastSync}}",
"settings_security.megerosites": "Megerősítés",
"settings_security.nincs_talalat": "Nincs találat",
"settings_security.figyelem_magyarorszag_eltavolitasa_azt_j": "Figyelem: Magyarország eltávolítása azt jelenti, hogy magyar IP-ről sem lesz elérhető a rendszer távolról. Biztosan folytatja?",
"settings_security.magyarorszag_eltavolitasa_nem_ajanlott_f": "Magyarország eltávolítása nem ajánlott. Folytatja?",
"settings_security.nincs_alkalmazas_specifikus_beallitas_mi": "Nincs alkalmazás-specifikus beállítás. Minden alkalmazás a globális beállítást követi.",
"settings_security.szerkesztes": "Szerkesztés",
"settings_security.engedelyezett_orszagok": "Engedélyezett országok:",
"settings_security.kesz": "Kész",
"settings_security.mentes": "Mentés...",
"settings_security.mentve": "Mentve",
"settings_security.halozati_hiba": "Hálózati hiba",
"settings_security.szinkronizalas_elinditva": "Szinkronizálás elindítva...",
"settings_security.utolso": "Utolsó:",
"settings_security.veszhelyzeti_informaciok": "Vészhelyzeti információk",
"settings_security.ezeket_az_adatokat_mentse_el": "Ezeket az adatokat mentse el biztos helyre. Újratelepítés esetén szükség lesz rájuk a rendszer visszaállításához.",
"settings_security.visszaallitasi_jelszo": "Visszaállítási jelszó",
"settings_security.megjelenit": "Megjelenít",
"settings_security.tamogatas": "Támogatás",
"settings_security.felhom_eu_kapcsolat": "<a href=\"mailto:{{.SupportEmail}}\" style=\"color: var(--blue);\">{{.SupportEmail}}</a>\n &nbsp;|&nbsp;\n <a href=\"{{.SupportURL}}\" target=\"_blank\" style=\"color: var(--blue);\">felhom.eu/kapcsolat</a>",
"settings_security.a_visszaallitasi_jelszo_lekerese_nem": "A visszaállítási jelszó lekérése nem sikerült.",
"settings_security.elrejt": "Elrejt",
"settings_notifications.beallitasok_ertesitesek": "Beállítások — Értesítések",
"settings_notifications.ertesitesek": "Értesítések",
"settings_notifications.e_mail_cim": "E-mail cím",
"settings_notifications.hibak_es_figyelmeztetesek": "Hibák és figyelmeztetések:",
"settings_notifications.biztonsagi_mentes_sikertelen": "Biztonsági mentés sikertelen",
"settings_notifications.adatbazis_mentes_sikertelen": "Adatbázis mentés sikertelen",
"settings_notifications.mentes_serules_eszlelve": "Mentés sérülés észlelve",
"settings_notifications.masodlagos_mentes_sikertelen": "Másodlagos mentés sikertelen",
"settings_notifications.tavoli_mentes_tarhelykeret_figyelmeztete": "Távoli mentés — tárhelykeret-figyelmeztetés",
"settings_notifications.lemez_beteloben_90_felett": "Lemez betelőben (90% felett)",
"settings_notifications.lemez_megtelt_vagy_meghibasodott": "Lemez megtelt vagy meghibásodott",
"settings_notifications.meghajto_levalasztva": "Meghajtó leválasztva",
"settings_notifications.szerver_nem_elerheto": "Szerver nem elérhető",
"settings_notifications.rendszer_allapot_kritikus": "Rendszer állapot kritikus",
"settings_notifications.elvart_fajlmentes_elmaradt": "Elvárt fájlmentés elmaradt",
"settings_notifications.elvart_adatbazismentes_elmaradt": "Elvárt adatbázismentés elmaradt",
"settings_notifications.alkalmazas_nem_fut": "Alkalmazás nem fut",
"settings_notifications.tajekoztato": "Tájékoztató:",
"settings_notifications.meghajto_ujra_csatlakoztatva": "Meghajtó újra csatlakoztatva",
"settings_notifications.rendszer_allapot_helyreallt": "Rendszer állapot helyreállt",
"settings_notifications.ertesitesi_szunet": "Értesítési szünet",
"settings_notifications.ora_azonos_problema_eseten_ennyi": "óra (azonos probléma esetén ennyi ideig nem küld újat)",
"settings_notifications.mentes": "Mentés",
"settings_notifications.teszt_email_kuldese": "Teszt email küldése",
"settings_notifications.az_ertesitesek_a_kozponti_rendszeren": "Az értesítések a központi rendszeren keresztül működnek, ami jelenleg nincs bekapcsolva.",
"settings_notifications.alkalmazas_email": "Alkalmazás-email",
"settings_notifications.az_alkalmazasok_a_felhom_on": "Az alkalmazások a Felhom-on keresztül küldhetnek emailt (pl. jelszó-visszaállítás, meghívók),\n külön email-szolgáltató beállítása nélkül. A feladó címe minden alkalmazásnál a saját\n <em>&lt;alkalmazás&gt;@felhom.eu</em> címe lesz.",
"settings_notifications.alkalmazas_email_engedelyezese": "Alkalmazás-email engedélyezése",
"settings_notifications.felado_neve_opcionalis": "Feladó neve (opcionális)",
"settings_notifications.pl_a_haztartas_neve": "pl. a háztartás neve",
"settings_notifications.ez_jelenik_meg_a_kimeno": "Ez jelenik meg a kimenő emailek feladójaként az alkalmazás neve mellett.",
"settings_notifications.bekapcsolas_utan_az_egyes_alkalmazasokna": "Bekapcsolás után az egyes alkalmazásoknál is engedélyezni kell az email-küldést (az alkalmazás oldalán).",
"stacks.mind": "Mind",
"app_export.folyamat": "Folyamat",
"app_import.titkos": "Titkos",
"monitoring.processzor": "Processzor",
"deploy.hamarosan": "Hamarosan",
"deploy.az_adatai_itt_voltak": "Az adatai itt voltak: <strong>{{.RestoreRecordedDrive}}</strong>.",
"deploy.aldomain_placeholder": "aldomain",
"settings_security.figyelem": "Figyelem",
"settings_notifications.pelda_email": "pelda@email.hu",
"page.title.dashboard": "Vezérlőpult",
"page.title.stacks": "Alkalmazások",
"page.title.monitoring": "Rendszermonitor",
"page.title.settings": "Beállítások",
"page.title.settings_notifications": "Értesítések",
"page.title.settings_security": "Biztonság és hozzáférés",
"func.infra.cloudflared.description": "Biztonságos internetkapcsolat — a szerver portnyitás nélkül érhető el kívülről.",
"func.infra.traefik.description": "Forgalomirányító (reverse proxy) — a kéréseket a megfelelő alkalmazáshoz irányítja.",
"func.infra.filebrowser.description": "Fájlkezelő — a tárhely fájljainak böngészése a böngészőből.",
"func.infra.samba.display": "Hálózati megosztás",
"func.infra.samba.description": "Fájlmegosztás a helyi hálózaton — a mappák a Windows Intézőben és a Mac Finderben jelennek meg."
}
@@ -20,7 +20,7 @@ import (
// now hit twice (R-262, and the OOB fixture in hub v0.99.0).
func diskMeterBlock(t *testing.T) string {
t.Helper()
raw, err := templateFS.ReadFile("templates/dashboard.html")
raw, err := huTemplateSource("templates/dashboard.html")
if err != nil {
t.Fatalf("read dashboard.html: %v", err)
}
+6
View File
@@ -176,6 +176,7 @@ func (s *Server) dashboardHandler(w http.ResponseWriter, r *http.Request) {
sysInfo := system.GetInfo(s.primaryHDDPath(), s.cpuCollector)
data := s.baseData("dashboard", "Vezérlőpult")
data["TitleKey"] = "page.title.dashboard" // i18n: the Hungarian title above is what hu renders
s.addRecoveryBanner(data, r) // R-241: the reminder bar, per visit
data["SettingsWarning"] = s.settings.LoadWarning // non-empty if settings.json was recovered from corruption
data["Stacks"] = deployedStacks
@@ -356,6 +357,7 @@ func visibleCatalogStacks(in []stacks.Stack) []stacks.Stack {
func (s *Server) stacksHandler(w http.ResponseWriter, r *http.Request) {
data := s.baseData("stacks", "Alkalmazások")
data["TitleKey"] = "page.title.stacks" // i18n: the Hungarian title above is what hu renders
allStacks := visibleCatalogStacks(s.stackMgr.GetStacks())
data["Stacks"] = allStacks
data["MissingStorage"] = s.missingStorageMap(allStacks)
@@ -793,6 +795,7 @@ func (s *Server) appDetailHandler(w http.ResponseWriter, r *http.Request, slug s
func (s *Server) monitoringHandler(w http.ResponseWriter, r *http.Request) {
data := s.baseData("monitoring", "Rendszermonitor")
data["TitleKey"] = "page.title.monitoring" // i18n: the Hungarian title above is what hu renders
data["SystemInfo"] = system.GetInfo(s.primaryHDDPath(), s.cpuCollector)
data["StorageBars"] = s.buildStorageBars()
@@ -2043,6 +2046,7 @@ func (s *Server) settingsBaseData(page, title string) map[string]interface{} {
// controller + server restart.
func (s *Server) systemPageData() map[string]interface{} {
data := s.settingsBaseData("settings", "Beállítások")
data["TitleKey"] = "page.title.settings" // i18n: the Hungarian title above is what hu renders
data["GitRepoURL"] = s.cfg.Git.RepoURL
data["GitSyncInterval"] = s.cfg.Git.SyncInterval
data["BackupEnabled"] = s.cfg.Backup.Enabled
@@ -2157,6 +2161,7 @@ func (s *Server) networkStoragePageData() map[string]interface{} {
// notificationsPageData builds the Értesítések subpage: notification prefs + app-email.
func (s *Server) notificationsPageData() map[string]interface{} {
data := s.settingsBaseData("settings-notifications", "Értesítések")
data["TitleKey"] = "page.title.settings_notifications" // i18n: the Hungarian title above is what hu renders
data["HubEnabled"] = s.cfg.Hub.Enabled
data["NotificationPrefs"] = s.settings.GetNotificationPrefs()
@@ -2173,6 +2178,7 @@ func (s *Server) notificationsPageData() map[string]interface{} {
// emergency/recovery info.
func (s *Server) securityPageData() map[string]interface{} {
data := s.settingsBaseData("settings-security", "Biztonság és hozzáférés")
data["TitleKey"] = "page.title.settings_security" // i18n: the Hungarian title above is what hu renders
// Recovery info for emergency section.
//
+18 -3
View File
@@ -35,6 +35,7 @@ import (
// TestI18nParity fails naming the page and the first differing line.
var updateI18nGolden = flag.Bool("update-i18n-golden", false, "write MISSING testdata/i18n_parity fixtures from the current templates (an existing fixture is never overwritten)")
var i18nDumpDir = flag.String("i18n-dump-dir", "", "TestI18nEnglishPages: also write every case's hu and en render to this directory (evidence, per-page review)")
var i18nGoldenOnly = flag.String("i18n-golden-only", "", "with -update-i18n-golden: only cases whose name matches this regexp")
var relativeAgeRe = regexp.MustCompile(`\d+ (napja|órája|perce)`)
@@ -52,6 +53,12 @@ func i18nFixtureTime() time.Time { return time.Date(2026, 9, 14, 3, 12, 0, 0, ti
var i18nTitleKeys = map[string]string{
"Indítópult": "page.title.launcher",
"Biztonsági mentés": "page.title.backups",
"Vezérlőpult": "page.title.dashboard",
"Alkalmazások": "page.title.stacks",
"Rendszermonitor": "page.title.monitoring",
"Beállítások": "page.title.settings",
"Értesítések": "page.title.settings_notifications",
"Biztonság és hozzáférés": "page.title.settings_security",
}
func i18nLayoutData(page, title string) map[string]interface{} {
@@ -376,7 +383,7 @@ func fixtureStrings(v reflect.Value, out map[string]bool) {
}
}
var i18nKeyLikeRe = regexp.MustCompile(`\b(launcher|backups|backups_shared|app_info|layout|func)\.[a-z0-9_]+\b`)
var i18nKeyLikeRe = regexp.MustCompile(`\b[a-z][a-z_]*\.[a-z0-9_.]+\b`)
var emptyElemRe = regexp.MustCompile(`<(h[1-6]|button|label|li|strong|option|p)(\s[^>]*)?>\s*</(h[1-6]|button|label|li|strong|option|p)>`)
// TestI18nEnglishPages — the three pages in English: no Hungarian letter from the TEMPLATE copy, no
@@ -395,14 +402,22 @@ func TestI18nEnglishPages(t *testing.T) {
for _, c := range i18nCases() {
hu := renderI18nCase(t, s, "hu", c)
en := renderI18nCase(t, s, "en", c)
if *i18nDumpDir != "" {
_ = os.MkdirAll(*i18nDumpDir, 0o755)
_ = os.WriteFile(filepath.Join(*i18nDumpDir, c.name+".hu.html"), []byte(hu), 0o644)
_ = os.WriteFile(filepath.Join(*i18nDumpDir, c.name+".en.html"), []byte(en), 0o644)
}
if !huLetter(hu) {
t.Fatalf("%s: positive control failed — the Hungarian render carries no Hungarian letter", c.name)
}
if strings.Contains(en, `{{T `) {
t.Errorf("%s: an unexpanded marker reached the English page", c.name)
}
if m := i18nKeyLikeRe.FindString(en); m != "" && s.i18n.Has(i18n.Default, m) {
t.Errorf("%s: a raw bundle key reached the English page: %q", c.name, m)
for _, m := range i18nKeyLikeRe.FindAllString(en, -1) {
if s.i18n.Has(i18n.Default, m) {
t.Errorf("%s: a raw bundle key reached the English page: %q", c.name, m)
break
}
}
if a, b := len(emptyElemRe.FindAllString(hu, -1)), len(emptyElemRe.FindAllString(en, -1)); b > a {
t.Errorf("%s: English has %d empty elements, Hungarian %d — a translation rendered blank", c.name, b, a)
+16
View File
@@ -129,6 +129,22 @@ func (s *Server) localeFuncs(lang string) template.FuncMap {
}
return lt.Format("2006-01-02") + " " + hm
},
// infraMeta: the protected infra stacks' curated identity (inframeta.go). Names that are product
// names stay; descriptions — and samba's display name — come from the bundle.
"infraMeta": func(name string) *InfraMeta {
m := infraMetaFor(name)
if m == nil {
return nil
}
out := *m
if v, _, ok := b.Text(lang, "func.infra."+name+".description"); ok {
out.Description = v
}
if v, _, ok := b.Text(lang, "func.infra."+name+".display"); ok {
out.DisplayName = v
}
return &out
},
"statusText": func(status string) string {
switch status {
case "pending", "restoring", "done", "failed", "skipped":
@@ -156,6 +156,15 @@ func TestLocaleFuncsHungarianBundleMatchesFuncMap(t *testing.T) {
if got, want := b.Msg("hu", "func.time.yesterday"), timeAgo(now.Add(-30*time.Hour)); got != want {
t.Errorf("yesterday: %q vs %q", got, want)
}
// infraMeta (slice 1): the Hungarian bundle carries the same curated words as inframeta.go.
for name, m := range infraMetaMap {
if got := b.Msg("hu", "func.infra."+name+".description"); got != m.Description {
t.Errorf("infra %s description: hu.json %q, inframeta.go %q", name, got, m.Description)
}
if k := "func.infra." + name + ".display"; b.Has("hu", k) && b.Msg("hu", k) != m.DisplayName {
t.Errorf("infra %s display: hu.json %q, inframeta.go %q", name, b.Msg("hu", k), m.DisplayName)
}
}
// And the English funcs really are English.
en := s.localeFuncs("en")
if got := en["timeAgo"].(func(time.Time) string)(now.Add(-time.Minute - time.Second)); got != "1 minute ago" {
@@ -238,3 +247,19 @@ func TestLanguageSwitch_EndToEnd(t *testing.T) {
t.Error("back on Hungarian: the page must be Hungarian with no switch")
}
}
// huTemplateSource returns an embedded template AS THE HUNGARIAN SET PARSES IT — markers expanded. Tests
// that assert on a template's Hungarian wording read this, never the raw file: since slice 1 the
// wording lives in hu.json, and a raw read would find a marker where the sentence used to be.
func huTemplateSource(name string) ([]byte, error) {
raw, err := templateFS.ReadFile(name)
if err != nil {
return nil, err
}
b, err := i18n.Shared()
if err != nil {
return nil, err
}
out, _ := b.Expand(i18n.Default, string(raw))
return []byte(out), nil
}
+1 -1
View File
@@ -92,7 +92,7 @@ func TestNetworkStorageWarningsIn_UnreachableAloneUnchanged(t *testing.T) {
// must never reuse the recoverable-unreachable copy; the unreachable line stays byte-identical).
func TestNetworkBadgeTemplates_DistinctStrings(t *testing.T) {
for _, tpl := range []string{"templates/dashboard.html", "templates/stacks.html"} {
body, err := os.ReadFile(tpl)
body, err := huTemplateSource(tpl)
if err != nil {
t.Fatal(err)
}
@@ -114,7 +114,7 @@ func TestSettingsSectionInventory(t *testing.T) {
}
var union strings.Builder
for _, f := range []string{"settings_system.html", "settings_notifications.html", "settings_security.html", "storage.html", "storage_network.html"} {
b, err := templateFS.ReadFile("templates/" + f)
b, err := huTemplateSource("templates/" + f)
if err != nil {
t.Fatalf("read %s: %v", f, err)
}
@@ -224,7 +224,7 @@ func TestStorageNoNativeConfirm(t *testing.T) {
// allow the pre-existing type-to-confirm overlay helper names
allow := regexp.MustCompile(`openConfirm|__closeConfirm|confirmEject|confirmWipe|typeToConfirm|confirm-`)
for _, f := range []string{"storage.html", "storage_network.html", "settings_system.html", "settings_notifications.html", "settings_security.html"} {
b, err := templateFS.ReadFile("templates/" + f)
b, err := huTemplateSource("templates/" + f)
if err != nil {
t.Fatalf("read %s: %v", f, err)
}
@@ -239,7 +239,7 @@ func TestStorageNoNativeConfirm(t *testing.T) {
// TestStorageAgentDownNote (D1 Scenario C, static): the enrichment JS has an error path that
// renders the exact warn note into #agent-warn-note (graceful degradation when the agent is down).
func TestStorageAgentDownNote(t *testing.T) {
b, err := templateFS.ReadFile("templates/storage.html")
b, err := huTemplateSource("templates/storage.html")
if err != nil {
t.Fatal(err)
}
@@ -282,7 +282,7 @@ func TestNoEmojiInTemplates(t *testing.T) {
if !strings.HasSuffix(e.Name(), ".html") {
continue
}
b, err := templateFS.ReadFile("templates/" + e.Name())
b, err := huTemplateSource("templates/" + e.Name())
if err != nil {
t.Fatal(err)
}
@@ -342,7 +342,7 @@ func TestStorageInit_PollsAgentFormatStatusOnTimeout(t *testing.T) {
// /storage, not /settings. One assertion per template.
func TestStorageWizardBackAnchors_PointToStorage(t *testing.T) {
for _, file := range []string{"templates/storage_init.html", "templates/storage_attach.html"} {
b, err := templateFS.ReadFile(file)
b, err := huTemplateSource(file)
if err != nil {
t.Fatalf("read %s: %v", file, err)
}
@@ -3,15 +3,15 @@
<div class="page-header">
<div style="display:flex;align-items:center;gap:1rem">
<a href="/stacks" class="btn btn-sm btn-outline">&larr; Alkalmazások</a>
<h2>{{.Stack.Meta.DisplayName}} &mdash; Exportálás</h2>
<a href="/stacks" class="btn btn-sm btn-outline">{{T "app_export.alkalmazasok"}}</a>
<h2>{{T "app_export.exportalas"}}</h2>
</div>
</div>
<div class="card" style="max-width:700px">
<h3>Mentés helye</h3>
<h3>{{T "app_export.mentes_helye"}}</h3>
<select id="destDrive" onchange="loadEstimate()" style="width:100%;padding:.5rem;margin-bottom:1rem">
<option value="">Válassz tárolót...</option>
<option value="">{{T "app_export.valassz_tarolot"}}</option>
{{range .Drives}}
<option value="{{.Path}}">{{if .Label}}{{.Label}} ({{.Path}}){{else}}{{.Path}}{{end}}</option>
{{end}}
@@ -19,19 +19,19 @@
<div id="estimateBox" style="display:none;background:var(--bg-2);border-radius:8px;padding:1rem;margin-bottom:1.5rem">
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Konfiguráció:</span>
<span>{{T "app_export.konfiguracio"}}</span>
<span id="estConfig">-</span>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Felhasználói adatok:</span>
<span>{{T "app_export.felhasznaloi_adatok"}}</span>
<span id="estData">-</span>
</div>
<div style="display:flex;justify-content:space-between;font-weight:600;margin-bottom:.25rem;border-top:1px solid var(--line);padding-top:.5rem">
<span>Összesen:</span>
<span><span id="estTotal">-</span> <span id="estFree" style="color:var(--text-3)">(szabad: -)</span></span>
<span>{{T "app_export.osszesen"}}</span>
<span><span id="estTotal">-</span> <span id="estFree" style="color:var(--text-3)">{{T "app_export.szabad"}}</span></span>
</div>
<div style="display:flex;justify-content:space-between">
<span>Becsült idő:</span>
<span>{{T "app_export.becsult_ido"}}</span>
<span id="estTime">-</span>
</div>
<div id="estWarning" style="display:none;color:var(--crit);margin-top:.5rem;font-weight:600"></div>
@@ -39,53 +39,53 @@
<div id="classSelect" style="display:none;margin-bottom:1.5rem">
<div id="mandBox" style="display:none;margin-bottom:1rem">
<div style="font-weight:600;margin-bottom:.25rem">Mindig része a mentésnek:</div>
<div style="font-weight:600;margin-bottom:.25rem">{{T "app_export.mindig_resze_a_mentesnek"}}</div>
<div id="mandList" style="color:var(--text-3);font-size:.9rem"></div>
</div>
<div id="optBox" style="display:none;margin-bottom:1rem">
<div style="font-weight:600;margin-bottom:.25rem">Választható tartalom:</div>
<div style="font-weight:600;margin-bottom:.25rem">{{T "app_export.valaszthato_tartalom"}}</div>
<div id="optList"></div>
</div>
<div id="exclBox" style="display:none">
<div id="exclSummary" style="margin-bottom:.25rem"></div>
<div style="color:var(--text-3);font-size:.85rem;margin-bottom:.5rem">A kihagyott mappák tartalma a Fájlkezelőben bármikor elérhető, és külön is lementhető.</div>
<button type="button" class="btn btn-xs btn-outline" onclick="toggleExcl()" id="exclToggle">Kihagyott tartalom megjelenítése</button>
<div style="color:var(--text-3);font-size:.85rem;margin-bottom:.5rem">{{T "app_export.a_kihagyott_mappak_tartalma_a"}}</div>
<button type="button" class="btn btn-xs btn-outline" onclick="toggleExcl()" id="exclToggle">{{T "app_export.kihagyott_tartalom_megjelenitese"}}</button>
<div id="exclList" style="display:none;margin-top:.5rem"></div>
</div>
</div>
<h3>Jelszó (opcionális)</h3>
<h3>{{T "app_export.jelszo_opcionalis"}}</h3>
<div style="display:flex;gap:.5rem;margin-bottom:1rem">
<input type="password" id="exportPassword" placeholder="Titkosítási jelszó" style="flex:1;padding:.5rem">
<button class="btn btn-sm btn-outline" onclick="togglePw()" type="button" title="Jelszó mutatása">&#128065;</button>
<input type="password" id="exportPassword" placeholder="{{T "app_export.titkositasi_jelszo"}}" style="flex:1;padding:.5rem">
<button class="btn btn-sm btn-outline" onclick="togglePw()" type="button" title="{{T "app_export.jelszo_mutatasa"}}">&#128065;</button>
</div>
<label style="display:flex;align-items:flex-start;gap:.5rem;margin-bottom:.5rem;cursor:pointer">
<input type="checkbox" id="stopApp" checked style="margin-top:3px">
<div>
<strong>Alkalmazás leállítása mentés előtt (ajánlott)</strong>
<div style="color:var(--text-3);font-size:.85rem">Az adatok konzisztenciája érdekében javasolt leállítani az alkalmazást mentés közben.</div>
<strong>{{T "app_export.alkalmazas_leallitasa_mentes_elott_ajanl"}}</strong>
<div style="color:var(--text-3);font-size:.85rem">{{T "app_export.az_adatok_konzisztenciaja_erdekeben_java"}}</div>
</div>
</label>
<button id="startBtn" class="btn btn-primary" onclick="startExport()" style="margin-top:1rem;width:100%" disabled>
Exportálás indítása
{{T "app_export.exportalas_inditasa"}}
</button>
</div>
<div id="progressCard" class="card" style="max-width:700px;display:none">
<h3>Folyamat</h3>
<h3>{{T "app_export.folyamat"}}</h3>
<div id="progressSteps"></div>
<div id="progressError" style="display:none;color:var(--crit);margin-top:1rem;font-weight:600"></div>
</div>
<div id="doneCard" class="card" style="max-width:700px;display:none">
<h3 style="color:var(--blue)">Kész!</h3>
<h3 style="color:var(--blue)">{{T "app_export.kesz"}}</h3>
<div style="margin-bottom:1rem">
<span id="doneFile" style="font-weight:600"></span>
<span id="doneSize" style="color:var(--text-3)"></span>
</div>
<a id="doneFBLink" href="#" target="_blank" class="btn btn-outline">Megnyitás FileBrowser-ben &nearr;</a>
<a id="doneFBLink" href="#" target="_blank" class="btn btn-outline">{{T "app_export.megnyitas_filebrowser_ben"}}</a>
</div>
<script>
@@ -126,12 +126,12 @@ async function loadEstimate() {
document.getElementById('estConfig').textContent = est.config_size_human;
document.getElementById('estData').textContent = est.data_size_human;
document.getElementById('estTotal').textContent = '~' + est.total_size_human;
document.getElementById('estFree').textContent = '(szabad: ' + est.dest_free_human + ')';
document.getElementById('estTime').textContent = '~' + est.estimated_minutes + ' perc';
document.getElementById('estFree').textContent = '{{T "app_export.szabad_2"}} ' + est.dest_free_human + ')';
document.getElementById('estTime').textContent = '~' + est.estimated_minutes + ' {{T "app_export.perc"}}';
var warn = document.getElementById('estWarning');
if (!est.fits_on_dest) {
warn.textContent = 'Nincs elég szabad hely a kiválasztott tárolón!';
warn.textContent = '{{T "app_export.nincs_eleg_szabad_hely_a"}}';
warn.style.display = 'block';
btn.disabled = true;
} else {
@@ -200,14 +200,14 @@ function recomputeTotal() {
var full = alap + exclAll;
var s = document.getElementById('exclSummary');
if ((lastEst.excluded_items||[]).length) {
s.innerHTML = 'Alap mentés: ~' + humanBytes(alap) + '. A kihagyott, nagy méretű tartalommal együtt: ~' + humanBytes(full) + '.';
s.innerHTML = '{{T "app_export.alap_mentes"}}' + humanBytes(alap) + '{{T "app_export.a_kihagyott_nagy_meretu_tartalommal"}}' + humanBytes(full) + '.';
}
}
function toggleExcl() {
var l = document.getElementById('exclList');
var shown = l.style.display !== 'none';
l.style.display = shown ? 'none' : 'block';
document.getElementById('exclToggle').textContent = shown ? 'Kihagyott tartalom megjelenítése' : 'Kihagyott tartalom elrejtése';
document.getElementById('exclToggle').textContent = shown ? '{{T "app_export.kihagyott_tartalom_megjelenitese"}}' : '{{T "app_export.kihagyott_tartalom_elrejtese"}}';
}
function gatherSelections() {
var deselect = [], optIn = [];
@@ -242,7 +242,7 @@ async function startExport() {
});
var data = await resp.json();
if (!data.ok) {
showError(data.error || 'Hiba történt');
showError(data.error || '{{T "common.hiba_tortent"}}');
return;
}
pollStatus();
@@ -3,22 +3,22 @@
<div class="page-header">
<div style="display:flex;align-items:center;gap:1rem">
<a href="/stacks" class="btn btn-sm btn-outline">&larr; Alkalmazások</a>
<h2>Alkalmazás importálás</h2>
<a href="/stacks" class="btn btn-sm btn-outline">{{T "app_import.alkalmazasok"}}</a>
<h2>{{T "app_import.alkalmazas_importalas"}}</h2>
</div>
</div>
<!-- Browser .fab upload (v0.128.0) — chunked, because the Cloudflare tunnel caps request bodies (~100 MB) -->
<div class="card" style="max-width:900px;margin-bottom:1rem">
<div id="fabUploadZone" style="border:1px dashed var(--line);border-radius:var(--radius);padding:1.25rem;text-align:center;cursor:pointer" onclick="fabPick()">
<p style="color:var(--text-2);margin:0 0 .75rem">.fab csomag feltöltése &mdash; húzza ide, vagy válassza ki a fájlt</p>
<button type="button" class="btn btn-sm btn-outline">Fájl kiválasztása</button>
<p style="color:var(--text-2);margin:0 0 .75rem">{{T "app_import.fab_csomag_feltoltese_huzza_ide"}}</p>
<button type="button" class="btn btn-sm btn-outline">{{T "app_import.fajl_kivalasztasa"}}</button>
<input type="file" id="fabFileInput" accept=".fab" style="display:none">
</div>
<div id="fabUpProgress" style="display:none;margin-top:1rem">
<div style="display:flex;justify-content:space-between;align-items:center;gap:1rem">
<span id="fabUpText" style="color:var(--text-2)">Feltöltés: 0%</span>
<button type="button" class="btn btn-sm btn-outline" onclick="fabCancel()">Megszakítás</button>
<span id="fabUpText" style="color:var(--text-2)">{{T "app_import.feltoltes_0"}}</span>
<button type="button" class="btn btn-sm btn-outline" onclick="fabCancel()">{{T "app_import.megszakitas"}}</button>
</div>
<div style="background:var(--bg-2);border-radius:var(--radius);height:6px;margin-top:.5rem;overflow:hidden">
<div id="fabUpBar" style="background:var(--blue);height:100%;width:0%"></div>
@@ -29,19 +29,19 @@
{{if not .Bundles}}
<div class="card" style="max-width:700px">
<p style="color:var(--text-3)">Nem található .fab csomag a regisztrált tárolókon.</p>
<p style="color:var(--text-3);font-size:.85rem">Exportálj egy alkalmazást az alkalmazás oldaláról, tölts fel egy .fab fájlt itt fent, vagy másolj egyet a <code>{tároló}/felhom-data/exports/</code> könyvtárba.</p>
<p style="color:var(--text-3)">{{T "app_import.nem_talalhato_fab_csomag_a"}}</p>
<p style="color:var(--text-3);font-size:.85rem">{{T "app_import.exportalj_egy_alkalmazast_az_alkalmazas"}}</p>
</div>
{{else}}
<div class="card" style="max-width:900px">
<table class="table" style="width:100%">
<thead>
<tr>
<th>Alkalmazás</th>
<th>Dátum</th>
<th>Méret</th>
<th>Tároló</th>
<th>Titkos</th>
<th>{{T "app_import.alkalmazas"}}</th>
<th>{{T "app_import.datum"}}</th>
<th>{{T "app_import.meret"}}</th>
<th>{{T "app_import.tarolo"}}</th>
<th>{{T "app_import.titkos"}}</th>
<th></th>
</tr>
</thead>
@@ -53,7 +53,7 @@
<td>{{.SizeHuman}}</td>
<td>{{if .DriveLabel}}{{.DriveLabel}}{{else}}{{.DrivePath}}{{end}}</td>
<td>{{if .Encrypted}}&#128274;{{end}}</td>
<td><button class="btn btn-sm btn-outline" onclick="showPreview('{{.Path}}', {{.Encrypted}})">Részletek &raquo;</button></td>
<td><button class="btn btn-sm btn-outline" onclick="showPreview('{{.Path}}', {{.Encrypted}})">{{T "app_import.reszletek"}}</button></td>
</tr>
{{end}}
</tbody>
@@ -63,13 +63,13 @@
<!-- Preview / password modal -->
<div id="previewCard" class="card" style="max-width:700px;display:none">
<h3 id="previewTitle">Csomag részletei</h3>
<h3 id="previewTitle">{{T "app_import.csomag_reszletei"}}</h3>
<div id="passwordPrompt" style="display:none;margin-bottom:1rem">
<p>Ez a csomag jelszóval védett. Kérlek add meg a jelszót:</p>
<p>{{T "app_import.ez_a_csomag_jelszoval_vedett"}}</p>
<div style="display:flex;gap:.5rem">
<input type="password" id="importPassword" class="form-input" autocomplete="off" placeholder="Jelszó" style="flex:1">
<button class="btn btn-primary" onclick="loadManifest()">Megnyitás</button>
<input type="password" id="importPassword" class="form-input" autocomplete="off" placeholder="{{T "common.jelszo"}}" style="flex:1">
<button class="btn btn-primary" onclick="loadManifest()">{{T "app_import.megnyitas"}}</button>
</div>
<div id="passwordError" style="display:none;color:var(--crit);margin-top:.5rem"></div>
</div>
@@ -77,52 +77,52 @@
<div id="manifestInfo" style="display:none">
<div style="background:var(--bg-2);border-radius:8px;padding:1rem;margin-bottom:1rem">
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Alkalmazás:</span>
<span>{{T "app_import.alkalmazas_2"}}</span>
<strong id="mfName">-</strong>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Exportálva:</span>
<span>{{T "app_import.exportalva"}}</span>
<span id="mfDate">-</span>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Controller verzió:</span>
<span>{{T "app_import.controller_verzio"}}</span>
<span id="mfVersion">-</span>
</div>
<div id="mfDBRow" style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Adatbázis:</span>
<span>{{T "app_import.adatbazis"}}</span>
<span id="mfDB">-</span>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:.25rem">
<span>Adatok:</span>
<span>{{T "app_import.adatok"}}</span>
<span id="mfDataType">-</span>
</div>
<div style="display:flex;justify-content:space-between">
<span>Méret:</span>
<span>{{T "app_import.meret_2"}}</span>
<span id="mfSize">-</span>
</div>
</div>
<div id="overwriteWarning" style="display:none;background:var(--warning-bg, #fff3cd);border:1px solid var(--warning-border, #ffc107);border-radius:8px;padding:1rem;margin-bottom:1rem">
<strong>&#9888; FIGYELEM:</strong> A meglévő <span id="overwriteAppName"></span> alkalmazás konfigurációja és összes adata felül lesz írva!
{{T "app_import.figyelem_a_meglevo"}} <span id="overwriteAppName"></span> {{T "app_import.alkalmazas_konfiguracioja_es_osszes_adat"}}
</div>
<button id="importBtn" class="btn btn-primary" onclick="startImport()" style="width:100%">
Visszaállítás indítása
{{T "app_import.visszaallitas_inditasa"}}
</button>
</div>
</div>
<!-- Progress -->
<div id="progressCard" class="card" style="max-width:700px;display:none">
<h3>Importálás folyamata</h3>
<h3>{{T "app_import.importalas_folyamata"}}</h3>
<div id="progressSteps"></div>
<div id="progressError" style="display:none;color:var(--crit);margin-top:1rem;font-weight:600"></div>
</div>
<div id="doneCard" class="card" style="max-width:700px;display:none">
<h3 style="color:var(--blue)">Importálás kész!</h3>
<p>Az alkalmazás sikeresen visszaállítva.</p>
<a id="doneLink" href="/stacks" class="btn btn-primary">Alkalmazások megtekintése</a>
<h3 style="color:var(--blue)">{{T "app_import.importalas_kesz"}}</h3>
<p>{{T "app_import.az_alkalmazas_sikeresen_visszaallitva"}}</p>
<a id="doneLink" href="/stacks" class="btn btn-primary">{{T "app_import.alkalmazasok_megtekintese"}}</a>
</div>
<script>
@@ -170,7 +170,7 @@ async function loadManifest() {
if (!data.ok) {
if (selectedEncrypted) {
document.getElementById('passwordError').textContent = data.error || 'Hibás jelszó';
document.getElementById('passwordError').textContent = data.error || '{{T "app_import.hibas_jelszo"}}';
document.getElementById('passwordError').style.display = 'block';
}
return;
@@ -193,7 +193,7 @@ function showManifest(m) {
document.getElementById('passwordPrompt').style.display = 'none';
document.getElementById('manifestInfo').style.display = 'block';
document.getElementById('previewTitle').textContent = (m.display_name || m.app_name) + ' — Csomag részletei';
document.getElementById('previewTitle').textContent = (m.display_name || m.app_name) + ' {{T "app_import.csomag_reszletei_2"}}';
document.getElementById('mfName').textContent = m.display_name || m.app_name;
document.getElementById('mfDate').textContent = m.exported_at ? new Date(m.exported_at).toLocaleString('hu-HU') : '-';
document.getElementById('mfVersion').textContent = m.controller_version || '-';
@@ -208,7 +208,7 @@ function showManifest(m) {
var dataType = [];
if (m.has_hdd_data) dataType.push('HDD');
if (m.has_volume_data) dataType.push('Docker volume');
document.getElementById('mfDataType').textContent = dataType.length ? dataType.join(', ') : 'Nincs';
document.getElementById('mfDataType').textContent = dataType.length ? dataType.join(', ') : '{{T "app_import.nincs"}}';
// Format size
var bytes = m.total_size_bytes || 0;
@@ -240,7 +240,7 @@ async function startImport() {
});
var data = await resp.json();
if (!data.ok) {
showError(data.error || 'Hiba történt');
showError(data.error || '{{T "common.hiba_tortent"}}');
return;
}
pollStatus();
@@ -269,7 +269,7 @@ function pollStatus() {
document.getElementById('doneCard').style.display = 'block';
if (data.stack_name) {
document.getElementById('doneLink').href = '/stacks/' + data.stack_name + '/deploy';
document.getElementById('doneLink').textContent = 'Alkalmazás megtekintése';
document.getElementById('doneLink').textContent = '{{T "app_import.alkalmazas_megtekintese"}}';
}
}
} catch(e) {
@@ -305,7 +305,7 @@ function showError(msg) {
// network error, re-syncing from the server's 409 received_bytes echo; progress from bytes acked.
var fabUploadId = null;
var fabCancelled = false;
var fabNetErrText = 'A feltöltés megszakadt — ellenőrizze a kapcsolatot, majd próbálja újra.';
var fabNetErrText = '{{T "app_import.a_feltoltes_megszakadt_ellenorizze_a"}}';
function csrfRawH() {
var el = document.querySelector('meta[name="csrf-token"]');
@@ -323,7 +323,7 @@ function fabGB(b) {
function fabProgress(done, total) {
var pct = total > 0 ? Math.floor(done * 100 / total) : 0;
document.getElementById('fabUpText').textContent =
'Feltöltés: ' + pct + '% (' + fabGB(done) + ' / ' + fabGB(total) + ' GB)';
'{{T "app_import.feltoltes"}} ' + pct + '% (' + fabGB(done) + ' / ' + fabGB(total) + ' GB)';
document.getElementById('fabUpBar').style.width = pct + '%';
}
@@ -355,7 +355,7 @@ async function fabStart(file) {
if (!file) return;
document.getElementById('fabUpError').style.display = 'none';
if (!file.name.toLowerCase().endsWith('.fab')) {
fabFail('Csak .fab fájl tölthető fel.');
fabFail('{{T "app_import.csak_fab_fajl_toltheto_fel"}}');
return;
}
fabCancelled = false;
@@ -410,7 +410,7 @@ async function fabStart(file) {
var fd = await fin.json();
if (!fd.ok) { fabFail(fd.error || fabNetErrText); return; }
fabUploadId = null;
document.getElementById('fabUpText').textContent = 'Feltöltés kész: ' + fd.filename;
document.getElementById('fabUpText').textContent = '{{T "app_import.feltoltes_kesz"}} ' + fd.filename;
document.getElementById('fabUpBar').style.width = '100%';
// Refresh the bundle list through the EXISTING scan (the page render runs it).
setTimeout(function(){ window.location.reload(); }, 800);
@@ -9,13 +9,13 @@
<div style="display:flex;align-items:center;gap:.5rem">
{{if .Stack.Deployed}}
<span class="stack-state-badge state-{{stateColor .Stack.State}}">{{stateLabel .Stack.State}}</span>
{{if .Stack.Orphaned}}<span class="badge badge-orphaned">{{T "app_info.elavult"}}</span>{{end}}
{{if .Stack.Orphaned}}<span class="badge badge-orphaned">{{T "common.elavult"}}</span>{{end}}
{{template "meta_badge" (lifecycleBadge .Meta)}}
{{template "meta_badge" (updateBadge .Stack)}}
{{if .EffectiveSubdomain}}<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">{{T "app_info.megnyitas"}}</a>{{end}}
<a href="/stacks/{{.Stack.Name}}/logs" class="btn btn-sm btn-outline">{{T "app_info.naplo"}}</a>
{{if .Stack.Orphaned}}
<button class="btn btn-sm btn-danger" onclick="deleteOrphanStack('{{.Stack.Name}}')">{{T "app_info.torles"}}</button>
<button class="btn btn-sm btn-danger" onclick="deleteOrphanStack('{{.Stack.Name}}')">{{T "common.torles"}}</button>
{{else}}
<a href="/stacks/{{.Stack.Name}}/export" class="btn btn-sm btn-outline">{{T "app_info.exportalas"}}</a>
<a href="/stacks/{{.Stack.Name}}/deploy" class="btn btn-sm btn-outline">{{T "app_info.beallitasok"}}</a>
@@ -57,9 +57,9 @@
<div class="stack-meta-badges">
<span class="meta-badge">~{{.Meta.Resources.MemRequest}} RAM</span>
<span class="meta-badge">{{.Meta.Category}}</span>
{{if .Meta.Resources.NeedsHDD}}<span class="meta-badge meta-badge-warn">{{T "app_info.hdd_szukseges"}}</span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="meta-badge meta-badge-ok">{{T "app_info.pi_kompatibilis"}}</span>{{else}}<span class="meta-badge meta-badge-warn">{{T "app_info.csak_x86"}}</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="meta-badge meta-badge-ok">{{T "app_info.magyar_felulet"}}</span>{{end}}
{{if .Meta.Resources.NeedsHDD}}<span class="meta-badge meta-badge-warn">{{T "common.hdd_szukseges"}}</span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="meta-badge meta-badge-ok">{{T "common.pi_kompatibilis"}}</span>{{else}}<span class="meta-badge meta-badge-warn">{{T "app_info.csak_x86"}}</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="meta-badge meta-badge-ok">{{T "common.magyar_felulet"}}</span>{{end}}
</div>
{{if .Meta.IsAbandoned}}
<div class="alert alert-warning" style="margin-top:.75rem">
@@ -139,8 +139,8 @@ function appMigrate(btn,app,label){
if(!target){ showAlert('{{T "app_info.valassz_celtarhelyet_2"}}'); return; }
felhomConfirm(btn,'{{T "app_info.athelyezed_a_z"}} '+label+' {{T "app_info.adatait_ide"}} '+target+'{{T "app_info.az_alkalmazas_rovid_idore_leall"}}',function(){
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('{{T "app_info.hiba"}} '+(d.error||'{{T "app_info.ismeretlen"}}')); } })
.catch(function(e){ showAlert('{{T "app_info.hiba"}} '+e); });
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('{{T "common.hiba_kettospont"}} '+(d.error||'{{T "app_info.ismeretlen"}}')); } })
.catch(function(e){ showAlert('{{T "common.hiba_kettospont"}} '+e); });
});
}
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
@@ -188,12 +188,12 @@ function appMigrate(btn,app,label){
{{end}}
{{if .InitialCredsOperatorSet}}
<tr>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">{{T "app_info.jelszo"}}</td>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">{{T "common.jelszo"}}</td>
<td>{{T "app_info.az_uzemelteto_allitotta_be"}}</td>
</tr>
{{else}}
<tr>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap;vertical-align:middle">{{T "app_info.jelszo"}}</td>
<td class="initcred-label" style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap;vertical-align:middle">{{T "common.jelszo"}}</td>
<!-- R-254: the value is NOT in this page. It used to be rendered into a `hidden`
span, which stops a browser drawing it and nothing else — a fetch of this page
returned a real per-install password. Both buttons now ask the server. -->
@@ -38,7 +38,7 @@
{{with $.SystemInfo}}
<div class="storage-item meter {{usageColor .DiskPercent}}">
<div class="storage-header">
<span class="storage-label">{{T "backups.rendszer"}}</span>
<span class="storage-label">{{T "common.rendszer_gyoker"}}</span>
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
</div>
<div class="meter-track">
@@ -50,7 +50,7 @@
<div class="storage-item meter meter-empty storage-disconnected">
<div class="storage-header">
<span class="storage-label">{{.Label}}</span>
<span class="storage-value">— <span class="state-text-neutral">{{T "backups.levalasztva"}}</span></span>
<span class="storage-value">— <span class="state-text-neutral">{{T "common.levalasztva"}}</span></span>
</div>
<div class="meter-track"></div>
</div>
@@ -227,11 +227,11 @@ function triggerGuestBackup() {
fetch('/api/guest-backup/trigger', { method: 'POST', headers: Object.assign({'Content-Type':'application/json'}, csrfHeaders()) })
.then(r => r.json())
.then(data => {
if (!data.ok) { out.innerHTML = '<div class="alert alert-error">' + (data.error || '{{T "backups.hiba"}}') + '</div>'; btn.disabled = false; return; }
if (!data.ok) { out.innerHTML = '<div class="alert alert-error">' + (data.error || '{{T "common.hiba"}}') + '</div>'; btn.disabled = false; return; }
out.innerHTML = '<span class="form-hint">{{T "backups.mentes_folyamatban"}}</span>';
pollGuestBackup(out, btn);
})
.catch(e => { out.innerHTML = '<div class="alert alert-error">{{T "backups.hiba_2"}} ' + e + '</div>'; btn.disabled = false; });
.catch(e => { out.innerHTML = '<div class="alert alert-error">{{T "common.hiba_kettospont"}} ' + e + '</div>'; btn.disabled = false; });
});
}
@@ -285,7 +285,7 @@ function pollGuestBackup(out, btn) {
}
})
.catch(function (e) {
out.innerHTML = '<div class="alert alert-error">{{T "backups.hiba_2"}} ' + e.message + '</div>';
out.innerHTML = '<div class="alert alert-error">{{T "common.hiba_kettospont"}} ' + e.message + '</div>';
btn.disabled = false;
});
});
@@ -2,22 +2,22 @@
{{template "layout_start" .}}
<div class="page-header">
<h2>Vezérlőpult</h2>
<h2>{{T "dashboard.vezerlopult"}}</h2>
<span class="domain-badge">{{.Domain}}</span>
</div>
<div class="stats-grid">
<a href="/stacks?filter=running" class="stat-card">
<div class="stat-value">{{.RunningCount}}</div>
<div class="stat-label">Futó alkalmazás</div>
<div class="stat-label">{{T "dashboard.futo_alkalmazas"}}</div>
</a>
<a href="/stacks?filter=stopped" class="stat-card {{if gt .StoppedCount 0}}stat-warn{{else}}stat-zero{{end}}">
<div class="stat-value">{{.StoppedCount}}</div>
<div class="stat-label">Leállítva</div>
<div class="stat-label">{{T "dashboard.leallitva"}}</div>
</a>
<a href="/stacks" class="stat-card">
<div class="stat-value">{{.TotalCount}}</div>
<div class="stat-label">Összes alkalmazás</div>
<div class="stat-label">{{T "dashboard.osszes_alkalmazas"}}</div>
</a>
</div>
@@ -26,7 +26,7 @@
<div class="system-info-items">
<div class="system-info-item meter {{usageColor .SystemInfo.MemPercent}}">
<div class="system-info-header">
<span class="system-info-label">Memória</span>
<span class="system-info-label">{{T "dashboard.memoria"}}</span>
<span class="system-info-value">{{fmtMB .SystemInfo.UsedMemMB}} / {{fmtMB .SystemInfo.TotalMemMB}} ({{printf "%.0f" .SystemInfo.MemPercent}}%)</span>
</div>
<div class="meter-track">
@@ -46,7 +46,7 @@
{{if .SystemInfo.TemperatureCelsius}}
<div class="system-info-item system-info-item-compact">
<div class="system-info-header">
<span class="system-info-label"><svg class="ico ico-sm"><use href="#i-thermometer"/></svg> Hőmérséklet</span>
<span class="system-info-label"><svg class="ico ico-sm"><use href="#i-thermometer"/></svg> {{T "dashboard.homerseklet"}}</span>
<span class="system-info-value temp-value state-text-{{tempColor .SystemInfo.TemperatureCelsius}}">{{fmtTemp .SystemInfo.TemperatureCelsius}}</span>
</div>
</div>
@@ -61,23 +61,23 @@
{{if not .SystemInfo.DiskKnown}}
<div class="system-info-item meter meter-empty">
<div class="system-info-header">
<span class="system-info-label">Rendszer (/)</span>
<span class="system-info-value">— <span class="state-text-neutral">nem ismert</span></span>
<span class="system-info-label">{{T "common.rendszer_gyoker"}}</span>
<span class="system-info-value">— <span class="state-text-neutral">{{T "dashboard.nem_ismert"}}</span></span>
</div>
<div class="meter-track"></div>
<div class="meter-flag">A tárhely mérete most nem olvasható ki.</div>
<div class="meter-flag">{{T "dashboard.a_tarhely_merete_most_nem"}}</div>
</div>
{{else}}
{{$duc := usageColor .SystemInfo.DiskPercent}}
<div class="system-info-item meter {{$duc}}">
<div class="system-info-header">
<span class="system-info-label">Rendszer (/)</span>
<span class="system-info-label">{{T "common.rendszer_gyoker"}}</span>
<span class="system-info-value">{{fmtGB .SystemInfo.DiskUsedGB}} / {{fmtGB .SystemInfo.DiskTotalGB}} ({{printf "%.0f" .SystemInfo.DiskPercent}}%)</span>
</div>
<div class="meter-track">
<div class="meter-fill" style="width:{{printf "%.0f" .SystemInfo.DiskPercent}}%"></div>
</div>
{{if eq $duc "warn"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>Fogyóban a hely</div>{{else if eq $duc "crit"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>Kritikusan kevés hely</div>{{end}}
{{if eq $duc "warn"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>{{T "dashboard.fogyoban_a_hely"}}</div>{{else if eq $duc "crit"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>{{T "dashboard.kritikusan_keves_hely"}}</div>{{end}}
</div>
{{end}}
{{range .StorageBars}}
@@ -85,7 +85,7 @@
<div class="system-info-item meter meter-empty storage-disconnected">
<div class="system-info-header">
<span class="system-info-label">{{.Label}}</span>
<span class="system-info-value">— <span class="state-text-neutral">Leválasztva</span></span>
<span class="system-info-value">— <span class="state-text-neutral">{{T "common.levalasztva"}}</span></span>
</div>
<div class="meter-track"></div>
</div>
@@ -99,7 +99,7 @@
<div class="meter-track">
<div class="meter-fill" style="width:{{printf "%.0f" .Percent}}%"></div>
</div>
{{if eq $uc "warn"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>Fogyóban a hely</div>{{else if eq $uc "crit"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>Kritikusan kevés hely</div>{{end}}
{{if eq $uc "warn"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>{{T "dashboard.fogyoban_a_hely"}}</div>{{else if eq $uc "crit"}}<div class="meter-flag"><svg class="ico"><use href="#i-triangle-alert"/></svg>{{T "dashboard.kritikusan_keves_hely"}}</div>{{end}}
</div>
{{end}}
{{end}}
@@ -127,7 +127,7 @@
{{end}}
<div class="disk-health-card">
<h3>Lemezek állapota</h3>
<h3>{{T "dashboard.lemezek_allapota"}}</h3>
{{if .DiskHealthRows}}
<ul class="disk-health-list">
{{range .DiskHealthRows}}
@@ -139,34 +139,34 @@
{{end}}
</ul>
{{else}}
<p class="form-hint state-text-neutral">Nincs adat</p>
<p class="form-hint state-text-neutral">{{T "dashboard.nincs_adat"}}</p>
{{end}}
</div>
{{if .BackupEnabled}}
<div class="backup-status-card">
<h3><a href="/backups" class="backup-card-link">Biztonsági mentés</a></h3>
<h3><a href="/backups" class="backup-card-link">{{T "dashboard.biztonsagi_mentes"}}</a></h3>
{{if .BackupStatus}}
<div class="backup-info-row">
<span class="backup-label">Utolsó mentés:</span>
<span class="backup-label">{{T "dashboard.utolso_mentes"}}</span>
<span class="backup-value">
{{if .BackupStatus.Success}}
<span>{{.BackupStatus.LastRun.Format "2006-01-02 15:04"}}</span>
{{else}}
<span class="backup-status-fail">Sikertelen</span>
<span class="backup-status-fail">{{T "dashboard.sikertelen"}}</span>
{{end}}
</span>
</div>
{{else}}
<div class="backup-info-row">
<span class="backup-label">Utolsó mentés:</span>
<span class="backup-value backup-status-none">Még nem futott</span>
<span class="backup-label">{{T "dashboard.utolso_mentes"}}</span>
<span class="backup-value backup-status-none">{{T "dashboard.meg_nem_futott"}}</span>
</div>
{{end}}
{{if .DBDumpStatus}}
<div class="backup-info-row">
<span class="backup-label">Adatbázisok:</span>
<span class="backup-value">{{len .DBDumpStatus.Results}} mentve</span>
<span class="backup-label">{{T "dashboard.adatbazisok"}}</span>
<span class="backup-value">{{T "dashboard.mentve"}}</span>
</div>
{{end}}
{{/* Cross-drive ("2. mentés") moved to the host agent in slice 8C — the dashboard handler no
@@ -175,7 +175,7 @@
</div>
{{end}}
<h3>Telepített alkalmazások</h3>
<h3>{{T "dashboard.telepitett_alkalmazasok"}}</h3>
<div class="stack-list">
{{range .Stacks}}
@@ -186,44 +186,44 @@
{{template "app_list_row" dict "Slug" .Meta.Slug "Name" .Meta.DisplayName "Secondary" .Meta.Description "RowClass" (printf "stack-state-%s" (stateColor .State)) "Href" (appHref .Meta.Slug)}}
{{end}}
<span class="tag tag-{{stateColor .State}}"><span class="dot"></span>{{stateLabel .State}}</span>
{{if .Orphaned}}<span class="tag tag-warn">Elavult</span>{{end}}
{{if .Orphaned}}<span class="tag tag-warn">{{T "common.elavult"}}</span>{{end}}
{{template "meta_badge" (lifecycleBadge .Meta)}}
{{$ms := index $.MissingStorage .Name}}{{if $ms}}<span class="tag tag-warn" title="Az alkalmazás adattárolója nem elérhető. Csatlakoztasd újra a meghajtót, vagy helyezd át az adatokat egy másik tárhelyre."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Hiányzó tárhely: {{$ms}}</span>{{end}}
{{if $.OOMKilled}}{{if index $.OOMKilled .Name}}<span class="tag tag-warn" title="Az alkalmazás egyik folyamatát a rendszer leállította, mert elfogyott a memóriája. Lehet, hogy egy feladat nem fejeződött be. Indítsd újra az alkalmazást; ha ismétlődik, jelezd az üzemeltetőnek."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Memória elfogyott</span>{{end}}{{end}}
{{$ns := index $.NetworkStubs .Name}}{{if $ns}}<span class="tag tag-warn" title="Az alkalmazás környezetében a hálózati tárhely helyén üres helyi könyvtár van — az adatok nem a NAS-ra kerülnek. Jelezze az üzemeltetőnek."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Hálózati tárhely hibás — az alkalmazás nem a NAS-t látja</span>{{end}}
{{$nw := index $.NetworkWarnings .Name}}{{if $nw}}<span class="tag tag-warn" title="A hálózati tárhely (NAS) jelenleg nem érhető el. Az alkalmazás fut; az adatok elérése a NAS visszatértével helyreáll."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Hálózati tárhely nem elérhető: {{$nw}}</span>{{end}}
{{if and .Deployed (routeUnpublished .State)}}<span class="tag tag-warn" title="A proxy (Traefik) csak egészséges konténerhez publikál nyilvános útvonalat. Amíg az alkalmazás nem egészséges, az URL 404-et ad, pedig a konténer fut."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>URL nem elérhető</span>{{end}}
{{$ms := index $.MissingStorage .Name}}{{if $ms}}<span class="tag tag-warn" title="{{T "dashboard.az_alkalmazas_adattaroloja_nem_elerheto"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "dashboard.hianyzo_tarhely"}}</span>{{end}}
{{if $.OOMKilled}}{{if index $.OOMKilled .Name}}<span class="tag tag-warn" title="{{T "dashboard.az_alkalmazas_egyik_folyamatat_a"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "dashboard.memoria_elfogyott"}}</span>{{end}}{{end}}
{{$ns := index $.NetworkStubs .Name}}{{if $ns}}<span class="tag tag-warn" title="{{T "dashboard.az_alkalmazas_kornyezeteben_a_halozati"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "dashboard.halozati_tarhely_hibas_az_alkalmazas"}}</span>{{end}}
{{$nw := index $.NetworkWarnings .Name}}{{if $nw}}<span class="tag tag-warn" title="{{T "dashboard.a_halozati_tarhely_nas_jelenleg"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "dashboard.halozati_tarhely_nem_elerheto"}}</span>{{end}}
{{if and .Deployed (routeUnpublished .State)}}<span class="tag tag-warn" title="{{T "dashboard.a_proxy_traefik_csak_egeszseges"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "dashboard.url_nem_elerheto"}}</span>{{end}}
{{if .Protected}}
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>Védett</span>
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>{{T "dashboard.vedett"}}</span>
{{/* Customer link ONLY for Linked infra (filebrowser → files.<domain>) —
cloudflared/traefik have no customer UI (guarded by render test). */}}
{{if and $im $im.Linked}}
{{$subdomain := index $.Subdomains .Name}}
{{if $subdomain}}<a href="https://{{$subdomain}}.{{$.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">Megnyitás <svg class="ico ico-sm"><use href="#i-external-link"/></svg></a>{{end}}
{{if $subdomain}}<a href="https://{{$subdomain}}.{{$.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">{{T "dashboard.megnyitas"}} <svg class="ico ico-sm"><use href="#i-external-link"/></svg></a>{{end}}
{{end}}
{{if isOperational .State}}
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')" title="Újraindítás"><svg class="ico ico-sm"><use href="#i-rotate-cw"/></svg></button>
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')" title="{{T "common.ujrainditas"}}"><svg class="ico ico-sm"><use href="#i-rotate-cw"/></svg></button>
{{end}}
{{else if not .Deployed}}
<a href="/stacks/{{.Name}}/deploy" class="btn btn-sm btn-primary" onclick="return checkBeforeDeploy(event, '{{.Name}}')">Telepítés</a>
<a href="/stacks/{{.Name}}/deploy" class="btn btn-sm btn-primary" onclick="return checkBeforeDeploy(event, '{{.Name}}')">{{T "dashboard.telepites"}}</a>
{{else}}
{{if isOperational .State}}
{{$subdomain := index $.Subdomains .Name}}
{{if $subdomain}}<a href="https://{{$subdomain}}.{{$.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">Megnyitás <svg class="ico ico-sm"><use href="#i-external-link"/></svg></a>{{end}}
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')" title="Újraindítás"><svg class="ico ico-sm"><use href="#i-rotate-cw"/></svg></button>
<button class="btn btn-sm btn-danger" onclick="stackAction(event, '{{.Name}}', 'stop')" title="Leállítás"><svg class="ico ico-sm"><use href="#i-square"/></svg></button>
{{if $subdomain}}<a href="https://{{$subdomain}}.{{$.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">{{T "dashboard.megnyitas"}} <svg class="ico ico-sm"><use href="#i-external-link"/></svg></a>{{end}}
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')" title="{{T "common.ujrainditas"}}"><svg class="ico ico-sm"><use href="#i-rotate-cw"/></svg></button>
<button class="btn btn-sm btn-danger" onclick="stackAction(event, '{{.Name}}', 'stop')" title="{{T "dashboard.leallitas"}}"><svg class="ico ico-sm"><use href="#i-square"/></svg></button>
{{else}}
<button class="btn btn-sm btn-success" onclick="stackAction(event, '{{.Name}}', 'start')" title="Indítás"><svg class="ico ico-sm"><use href="#i-play"/></svg></button>
{{if not .Orphaned}}<button class="btn btn-sm btn-danger" onclick="removeStack('{{.Name}}')">Eltávolítás</button>{{end}}
<button class="btn btn-sm btn-success" onclick="stackAction(event, '{{.Name}}', 'start')" title="{{T "dashboard.inditas"}}"><svg class="ico ico-sm"><use href="#i-play"/></svg></button>
{{if not .Orphaned}}<button class="btn btn-sm btn-danger" onclick="removeStack('{{.Name}}')">{{T "common.eltavolitas"}}</button>{{end}}
{{end}}
<a href="/stacks/{{.Name}}/logs" class="btn btn-sm btn-outline">Napló</a>
{{if .Orphaned}}<button class="btn btn-sm btn-danger" onclick="deleteOrphanStack('{{.Name}}')">Törlés</button>{{end}}
<a href="/stacks/{{.Name}}/logs" class="btn btn-sm btn-outline">{{T "dashboard.naplo"}}</a>
{{if .Orphaned}}<button class="btn btn-sm btn-danger" onclick="deleteOrphanStack('{{.Name}}')">{{T "common.torles"}}</button>{{end}}
{{end}}
{{template "app_list_row_end"}}
{{else}}
<div class="empty-state">
<p>Nincs elérhető alkalmazás.</p>
<p>{{T "dashboard.nincs_elerheto_alkalmazas"}}</p>
</div>
{{end}}
</div>
+161 -170
View File
@@ -3,22 +3,22 @@
<div class="page-header">
<div style="display:flex;align-items:center;gap:.5rem">
<a href="/stacks" class="btn btn-sm btn-outline">← Vissza</a>
<h2>{{.Meta.DisplayName}} — {{if .AlreadyDeployed}}Beállítások{{else}}Telepítés{{end}}</h2>
<a href="/stacks" class="btn btn-sm btn-outline">{{T "deploy.vissza"}}</a>
<h2>{{.Meta.DisplayName}} — {{if .AlreadyDeployed}}{{T "deploy.beallitasok"}}{{else}}{{T "deploy.telepites"}}{{end}}</h2>
</div>
<div style="display:flex;align-items:center;gap:.5rem">
{{if .AlreadyDeployed}}
{{if isOperational .Stack.State}}
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Stack.Name}}', 'restart')">Újraindítás</button>
<button class="btn btn-sm btn-danger" onclick="stackAction(event, '{{.Stack.Name}}', 'stop')">Leállítás</button>
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Stack.Name}}', 'restart')">{{T "common.ujrainditas"}}</button>
<button class="btn btn-sm btn-danger" onclick="stackAction(event, '{{.Stack.Name}}', 'stop')">{{T "deploy.leallitas"}}</button>
{{else}}
<button class="btn btn-sm btn-success" onclick="stackAction(event, '{{.Stack.Name}}', 'start')">Indítás</button>
<button class="btn btn-sm btn-success" onclick="stackAction(event, '{{.Stack.Name}}', 'start')">{{T "deploy.inditas"}}</button>
{{end}}
{{if and (isOperational .Stack.State) .EffectiveSubdomain}}
<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
<a href="https://{{.EffectiveSubdomain}}.{{.Domain}}{{.Meta.OpenPath}}" target="_blank" class="btn btn-sm btn-outline">{{T "deploy.megnyitas"}}</a>
{{end}}
{{end}}
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline"><svg class="ico ico-sm"><use href="#i-info"/></svg> Részletek</a>
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline"><svg class="ico ico-sm"><use href="#i-info"/></svg> {{T "deploy.reszletek"}}</a>
</div>
</div>
@@ -33,73 +33,72 @@
{{if .Meta.Description}}<p>{{.Meta.Description}}</p>{{end}}
<div class="metarows">
{{if .Meta.Resources.MemRequest}}<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~{{.Meta.Resources.MemRequest}}</span></span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Pi kompatibilis</span>{{end}}
{{if .Meta.Resources.NeedsHDD}}<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>HDD szükséges</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Magyar felület</span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>{{T "common.pi_kompatibilis"}}</span>{{end}}
{{if .Meta.Resources.NeedsHDD}}<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>{{T "common.hdd_szukseges"}}</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>{{T "common.magyar_felulet"}}</span>{{end}}
</div>
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline" style="margin-top:0.5rem">
Részletes leírás, képernyőképek
{{T "deploy.reszletes_leiras_kepernyokepek"}}
</a>
</div>
</div>
{{if .AlreadyDeployed}}
<div class="alert alert-info">
Ez az alkalmazás már telepítve van. Az alábbi beállítások csak olvashatók.
{{T "deploy.ez_az_alkalmazas_mar_telepitve"}}
</div>
{{if .StorageInfo}}
<div class="deploy-storage-info">
<h4>Adattárolás</h4>
<h4>{{T "deploy.adattarolas"}}</h4>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Tárhely</span>
<span class="settings-label">{{T "deploy.tarhely"}}</span>
<span class="settings-value">{{.StorageInfo.Label}} <span class="mono" style="color:var(--text-2)">({{.StorageInfo.Path}})</span></span>
</div>
{{if .StorageInfo.DataSizeHuman}}
<div class="settings-row">
<span class="settings-label">Adatméret</span>
<span class="settings-label">{{T "deploy.adatmeret"}}</span>
<span class="settings-value mono">{{.StorageInfo.DataSizeHuman}}</span>
</div>
{{end}}
{{if .StorageInfo.FreeHuman}}
<div class="settings-row">
<span class="settings-label">Szabad hely</span>
<span class="settings-value mono">{{.StorageInfo.FreeHuman}} ({{printf "%.0f" .StorageInfo.FreePercent}}% szabad)</span>
<span class="settings-label">{{T "deploy.szabad_hely"}}</span>
<span class="settings-value mono">{{T "deploy.szabad"}}</span>
</div>
{{end}}
</div>
{{if .OtherStoragePaths}}
<span class="btn btn-sm btn-outline" style="margin-top:.75rem;opacity:.45;cursor:not-allowed" title="Hamarosan">
<svg class="ico ico-sm"><use href="#i-upload"/></svg> Mozgatás másik tárolóra
<span class="btn btn-sm btn-outline" style="margin-top:.75rem;opacity:.45;cursor:not-allowed" title="{{T "deploy.hamarosan"}}">
<svg class="ico ico-sm"><use href="#i-upload"/></svg> {{T "deploy.mozgatas_masik_tarolora"}}
</span>
{{end}}
</div>
{{end}}
{{if .StaleData}}
<div class="deploy-stale-data">
<h4>Korábbi adatok</h4>
<h4>{{T "deploy.korabbi_adatok"}}</h4>
<p class="form-hint" style="margin-bottom:1rem">
Az alkalmazás adatainak másolata megtalálható egy másik tárolón is.
Ez általában áthelyezés után marad hátra.
{{T "deploy.az_alkalmazas_adatainak_masolata_megtala"}}
</p>
{{range .StaleData}}
<div class="stale-data-item">
<div class="settings-grid" style="margin-bottom:.75rem">
<div class="settings-row">
<span class="settings-label">Tárhely</span>
<span class="settings-label">{{T "deploy.tarhely"}}</span>
<span class="settings-value">{{.Label}} <span class="mono" style="color:var(--text-2)">({{.Path}})</span></span>
</div>
<div class="settings-row">
<span class="settings-label">Méret</span>
<span class="settings-label">{{T "deploy.meret"}}</span>
<span class="settings-value mono">{{.SizeHuman}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Mappák</span>
<span class="settings-label">{{T "deploy.mappak"}}</span>
<span class="settings-value mono" style="font-size:.85rem">{{range .Mounts}}{{.}}<br>{{end}}</span>
</div>
</div>
<button class="btn btn-sm btn-danger" onclick="deleteStaleData('{{$.Meta.Slug}}', '{{.Path}}', this)">
Korábbi adatok törlése
{{T "deploy.korabbi_adatok_torlese"}}
</button>
</div>
{{end}}
@@ -109,12 +108,11 @@
{{if .AlreadyDeployed}}
<div class="deploy-cross-drive">
<h4>Biztonsági mentés</h4>
<h4>{{T "deploy.biztonsagi_mentes"}}</h4>
<div class="cross-drive-nightly">
<span class="form-hint" style="display:block;margin-top:.25rem">
Az alkalmazás adatbázisa és Docker kötetei automatikusan bekerülnek az éjszakai biztonsági mentésbe.
<a href="/backups" style="color:var(--blue)">Mentési állapot →</a> · <a href="/stacks/{{.Stack.Name}}/backup" style="color:var(--blue)">Mentési beállítások →</a>
{{T "deploy.az_alkalmazas_adatbazisa_es_docker"}}
</span>
</div>
</div>
@@ -122,9 +120,9 @@
{{if .HasIntegrations}}
<div class="app-optional-config">
<h3>Integrációk</h3>
<h3>{{T "deploy.integraciok"}}</h3>
<p class="config-group-desc">
Más telepített alkalmazásokkal való összekapcsolás. Az integráció automatikusan felfüggesztődik, ha bármelyik alkalmazás leáll, és újraaktiválódik indításkor.
{{T "deploy.mas_telepitett_alkalmazasokkal_valo_ossz"}}
</p>
{{range .Integrations}}
@@ -133,20 +131,20 @@
<strong>{{.Label}}</strong>
<p class="config-field-help" style="margin:0">{{.Description}}</p>
{{if not .TargetDeployed}}
<span class="tag tag-off" style="margin-top:.25rem"><span class="dot"></span>Nincs telepítve</span>
<span class="tag tag-off" style="margin-top:.25rem"><span class="dot"></span>{{T "deploy.nincs_telepitve"}}</span>
{{else if not .TargetRunning}}
<span class="tag tag-neutral" style="margin-top:.25rem"><span class="dot"></span>Célalkalmazás leállítva</span>
<span class="tag tag-neutral" style="margin-top:.25rem"><span class="dot"></span>{{T "deploy.celalkalmazas_leallitva"}}</span>
{{else if eq .Status "error"}}
<span class="tag tag-crit" style="margin-top:.25rem"><span class="dot"></span>Hiba</span>
<span class="tag tag-crit" style="margin-top:.25rem"><span class="dot"></span>{{T "common.hiba"}}</span>
{{else if .Enabled}}
<span class="tag tag-run" style="margin-top:.25rem"><span class="dot"></span>Aktív</span>
<span class="tag tag-run" style="margin-top:.25rem"><span class="dot"></span>{{T "deploy.aktiv"}}</span>
{{end}}
</div>
<label class="toggle">
<input type="checkbox"
{{if .Enabled}}checked{{end}}
{{if not .TargetDeployed}}disabled title="A célalkalmazás nincs telepítve"{{end}}
{{if and .TargetDeployed (not .TargetRunning)}}disabled title="A célalkalmazás nem fut"{{end}}
{{if not .TargetDeployed}}disabled title="{{T "deploy.a_celalkalmazas_nincs_telepitve"}}"{{end}}
{{if and .TargetDeployed (not .TargetRunning)}}disabled title="{{T "deploy.a_celalkalmazas_nem_fut"}}"{{end}}
onchange="toggleIntegration('{{$.Stack.Name}}', '{{.Target}}', this.checked, this)">
<span class="toggle-label"></span>
</label>
@@ -166,14 +164,14 @@
var data = await resp.json();
if (!data.ok) {
checkbox.checked = !enable;
showAlert(data.error || 'Hiba történt');
showAlert(data.error || '{{T "common.hiba_tortent"}}');
} else {
setTimeout(function(){ location.reload(); }, 500);
return;
}
} catch(err) {
checkbox.checked = !enable;
showAlert('Hálózati hiba');
showAlert('{{T "deploy.halozati_hiba"}}');
}
checkbox.disabled = false;
}
@@ -182,24 +180,24 @@
{{if .GeoGlobalEnabled}}
<div class="app-optional-config">
<h3>Földrajzi korlátozás</h3>
<h3>{{T "deploy.foldrajzi_korlatozas"}}</h3>
<p class="config-group-desc">
Az alkalmazás egyéni országkorlátozás nélkül a globális beállítást követi.
{{T "deploy.az_alkalmazas_egyeni_orszagkorlatozas_ne"}}
</p>
<label class="toggle" style="margin-bottom:1rem">
<input type="checkbox" id="app-geo-override-toggle"
{{if .GeoAppOverride}}checked{{end}}
onchange="toggleAppGeoOverride(this.checked)">
<span class="toggle-label">Egyéni országkorlátozás</span>
<span class="toggle-label">{{T "deploy.egyeni_orszagkorlatozas"}}</span>
</label>
<div id="app-geo-override-details" {{if not .GeoAppOverride}}style="display:none"{{end}}>
<div class="form-group">
<label>Engedélyezett országok ehhez az alkalmazáshoz</label>
<label>{{T "deploy.engedelyezett_orszagok_ehhez_az_alkalmaz"}}</label>
<div class="geo-country-selector">
<input type="text" id="app-geo-search" class="form-control config-input"
placeholder="Ország keresése..." autocomplete="off"
placeholder="{{T "deploy.orszag_keresese"}}" autocomplete="off"
oninput="filterAppGeoCountries(this.value)"
onfocus="showAppGeoList()"
onblur="setTimeout(function(){hideAppGeoList()},200)">
@@ -208,7 +206,7 @@
<div class="geo-selected-tags" id="app-geo-tags"></div>
</div>
<div class="config-actions">
<button class="btn btn-primary" onclick="saveAppGeoOverride()">Mentés</button>
<button class="btn btn-primary" onclick="saveAppGeoOverride()">{{T "deploy.mentes"}}</button>
<span id="app-geo-status" class="config-save-status"></span>
</div>
</div>
@@ -255,7 +253,7 @@
+ esc(c.name) + ' <small>(' + c.code + ')</small></div>';
count++;
}
list.innerHTML = html || '<div class="geo-country-option" style="opacity:.5">Nincs találat</div>';
list.innerHTML = html || '<div class="geo-country-option" style="opacity:.5">{{T "deploy.nincs_talalat"}}</div>';
list.style.display = (count > 0 || q) ? '' : 'none';
};
@@ -296,12 +294,12 @@
})
.then(function(r){return r.json()})
.then(function(d){
status.textContent = d.ok ? (d.message || 'Mentve') : (d.error || 'Hiba');
status.textContent = d.ok ? (d.message || '{{T "deploy.mentve"}}') : (d.error || '{{T "common.hiba"}}');
status.className = 'config-save-status ' + (d.ok ? 'config-save-ok' : 'config-save-err');
setTimeout(function(){ status.textContent=''; }, 5000);
})
.catch(function(){
status.textContent = 'Hálózati hiba';
status.textContent = '{{T "deploy.halozati_hiba"}}';
status.className = 'config-save-status config-save-err';
});
};
@@ -317,7 +315,7 @@
{{if .HasOptionalConfig}}
<div class="app-optional-config">
<h3>Opcionális beállítások</h3>
<h3>{{T "deploy.opcionalis_beallitasok"}}</h3>
{{range .OptionalConfig}}
<div class="config-group">
<h4>{{.Group}}</h4>
@@ -328,7 +326,7 @@
<div class="config-field">
<label for="opt-{{.EnvVar}}">{{.Label}}</label>
{{if .HelpText}}<p class="config-field-help">{{.HelpText}}</p>{{end}}
{{if .HelpURL}}<p class="config-field-help"><a href="{{.HelpURL}}" target="_blank">Regisztrációs útmutató ↗</a></p>{{end}}
{{if .HelpURL}}<p class="config-field-help"><a href="{{.HelpURL}}" target="_blank">{{T "deploy.regisztracios_utmutato"}}</a></p>{{end}}
<input type="{{if eq .Type "secret_input"}}password{{else}}text{{end}}"
id="opt-{{.EnvVar}}"
name="{{.EnvVar}}"
@@ -344,7 +342,7 @@
<div class="config-actions">
<button class="btn btn-primary" id="save-optional-config" onclick="saveOptionalConfig('{{.Stack.Name}}')">
Mentés
{{T "deploy.mentes"}}
</button>
<span id="config-save-status" class="config-save-status"></span>
</div>
@@ -362,7 +360,7 @@
});
btn.disabled = true;
btn.textContent = 'Mentés...';
btn.textContent = '{{T "deploy.mentes_2"}}';
status.textContent = '';
status.className = 'config-save-status';
@@ -375,19 +373,19 @@
var data = await resp.json();
if (data.ok) {
status.textContent = (data.message || 'Mentve');
status.textContent = (data.message || '{{T "deploy.mentve"}}');
status.className = 'config-save-status config-save-ok';
} else {
status.textContent = (data.error || 'Hiba');
status.textContent = (data.error || '{{T "common.hiba"}}');
status.className = 'config-save-status config-save-err';
}
} catch(err) {
status.textContent = 'Hálózati hiba';
status.textContent = '{{T "deploy.halozati_hiba"}}';
status.className = 'config-save-status config-save-err';
}
btn.disabled = false;
btn.textContent = 'Mentés';
btn.textContent = '{{T "deploy.mentes"}}';
setTimeout(function() { status.textContent = ''; }, 5000);
}
@@ -396,26 +394,24 @@
{{if .AppEmailSupported}}
<div class="app-optional-config">
<h3>Email-küldés</h3>
<h3>{{T "deploy.email_kuldes"}}</h3>
<p class="config-group-desc">
Ez az alkalmazás tud emailt küldeni (pl. jelszó-visszaállítás, meghívók) a Felhom-on keresztül,
külön szolgáltató beállítása nélkül. A feladó címe: <strong>{{.AppEmailFromAddress}}</strong>.
{{T "deploy.ez_az_alkalmazas_tud_emailt"}}
</p>
{{if not .AppEmailGlobalOn}}
<p class="alert alert-warning" style="margin-bottom:1rem">
Az alkalmazás-email jelenleg ki van kapcsolva globálisan. Kapcsold be a
<a href="/settings">Beállítások</a> oldalon, hogy itt is működjön.
{{T "deploy.az_alkalmazas_email_jelenleg_ki"}}
</p>
{{end}}
<form method="POST" action="/stacks/{{.Stack.Name}}/app-email">
{{$.CSRFField}}
<label style="display:flex;align-items:center;gap:.5rem;margin-bottom:1rem">
<input type="checkbox" name="app_email_enabled" value="on" {{if .AppEmailAppOn}}checked{{end}}>
Email-küldés engedélyezése ennél az alkalmazásnál
{{T "deploy.email_kuldes_engedelyezese_ennel_az"}}
</label>
<div class="config-actions">
<button class="btn btn-primary" type="submit">Mentés</button>
<span class="config-group-desc" style="margin-left:.5rem">A mentés újraindítja az alkalmazást.</span>
<button class="btn btn-primary" type="submit">{{T "deploy.mentes"}}</button>
<span class="config-group-desc" style="margin-left:.5rem">{{T "deploy.a_mentes_ujrainditja_az_alkalmazast"}}</span>
</div>
</form>
</div>
@@ -427,25 +423,24 @@
<div class="memory-summary{{if .Blocked}} memory-blocked{{end}}">
{{if .Blocked}}
<div class="alert alert-error" style="margin-bottom:0">
Nincs elég memória! Foglalás telepítés után: {{.AfterMB}} MB / {{.UsableMB}} MB
{{T "deploy.nincs_eleg_memoria_foglalas_telepites"}}
</div>
{{else}}
<div class="memory-summary-header">
<span class="memory-summary-label">Memória</span>
<span class="memory-summary-label">{{T "deploy.memoria"}}</span>
<span class="memory-summary-value">{{.AfterMB}} MB / {{.UsableMB}} MB ({{.Percent}}%)</span>
</div>
<div class="meter-track meter-track-stacked">
<div class="memory-bar-segment memory-bar-committed" style="width:{{.UsedPercent}}%" title="Jelenlegi használat: {{.UsedMB}} MB"></div>
<div class="memory-bar-segment memory-bar-committed" style="width:{{.UsedPercent}}%" title="{{T "deploy.jelenlegi_hasznalat_mb"}}"></div>
<div class="memory-bar-segment memory-bar-new" style="width:{{subtract .Percent .UsedPercent}}%" title="{{$.Meta.DisplayName}}: +{{.NewRequestMB}} MB"></div>
</div>
<div class="memory-bar-legend">
<span class="memory-legend-item"><span class="memory-legend-dot memory-legend-committed"></span>Jelenlegi használat ({{.UsedMB}} MB)</span>
<span class="memory-legend-item"><span class="memory-legend-dot memory-legend-committed"></span>{{T "deploy.jelenlegi_hasznalat_mb_2"}}</span>
<span class="memory-legend-item"><span class="memory-legend-dot memory-legend-new"></span>{{$.Meta.DisplayName}} (+{{.NewRequestMB}} MB)</span>
</div>
{{if .OvercommitWarn}}
<div class="alert alert-warning" style="margin-top:0.5rem;margin-bottom:0">
Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát.
Normál használat mellett ez nem okoz problémát.
{{T "deploy.az_alkalmazasok_csucsterhelese_meghaladh"}}
</div>
{{end}}
{{end}}
@@ -461,10 +456,10 @@
{{if and (not .AlreadyDeployed) .RestoreHasRecord}}
<div class="alert alert-info" style="margin-bottom:1rem">
<svg class="ico ico-sm"><use href="#i-info"/></svg>
Ennek az alkalmazásnak van korábbi mentése, ezért az alábbi mezőket a saját mentése alapján töltöttük ki.
{{if .RestoreRecordedAddress}}Korábbi webcím: <strong>{{.RestoreRecordedAddress}}</strong>.{{end}}
{{if .RestoreRecordedDrive}}Az adatai itt voltak: <strong>{{.RestoreRecordedDrive}}</strong>.{{end}}
Ha most máshová szeretnéd telepíteni, nyugodtan átírhatod — a visszaállítás előtt jelezni fogjuk az eltérést.
{{T "deploy.ennek_az_alkalmazasnak_van_korabbi"}}
{{if .RestoreRecordedAddress}}{{T "deploy.korabbi_webcim"}}{{end}}
{{if .RestoreRecordedDrive}}{{T "deploy.az_adatai_itt_voltak"}}{{end}}
{{T "deploy.ha_most_mashova_szeretned_telepiteni"}}
</div>
{{end}}
{{/* R-351 / Part 1 — WHERE THE DATA WILL LIVE, before the button is pressed. Measured
@@ -474,27 +469,24 @@
{{if not .AlreadyDeployed}}
<div class="form-hint" style="margin-bottom:1rem">
{{if .RestoreRecordedDeclaresPath}}
Az alkalmazás adatai az alább kiválasztott adatmeghajtóra kerülnek.
{{T "deploy.az_alkalmazas_adatai_az_alabb"}}
{{else}}
<strong>Hol lesznek az adatok:</strong> ennél az alkalmazásnál nincs külön adatmeghajtó-választás,
ezért az adatai a rendszermeghajtóra kerülnek (<code>{{.SystemDataPath}}</code>). A mentései így is elkészülnek.
{{T "deploy.hol_lesznek_az_adatok_ennel"}}
{{end}}
</div>
{{end}}
{{if .DockerBelowReserve}}
<div class="alert alert-warning" style="margin-bottom:1rem">
<svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> Nincs elég szabad tárhely a telepítéshez. Jelenleg {{.DockerFreeHuman}} szabad, és a rendszer
{{.DockerReserveHuman}} tartalékot tart fenn az alapszolgáltatások (vezérlő, proxy) védelmében.
A telepítés ezért átmenetileg le van tiltva — szabadítson fel helyet, vagy bővítse a tárhelyet.
<svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> {{T "deploy.nincs_eleg_szabad_tarhely_a"}}
</div>
{{end}}
{{if .AutoFields}}
<div class="form-section">
<h4>Automatikusan generált értékek</h4>
<h4>{{T "deploy.automatikusan_generalt_ertekek"}}</h4>
{{if .AlreadyDeployed}}
<p class="form-section-desc">Ezek az értékek automatikusan jöttek létre a telepítéskor.</p>
<p class="form-section-desc">{{T "deploy.ezek_az_ertekek_automatikusan_jottek"}}</p>
{{else}}
<p class="form-section-desc">Ezek az értékek a telepítéssel együtt mentésre kerülnek. Jegyezze fel a szükséges jelszavakat!</p>
<p class="form-section-desc">{{T "deploy.ezek_az_ertekek_a_telepitessel"}}</p>
{{end}}
{{$autoValues := .AutoFieldValues}}
{{$stackName := .Stack.Name}}
@@ -502,7 +494,7 @@
{{range .AutoFields}}
{{$val := index $autoValues .EnvVar}}
<div class="form-group form-group-auto">
<label>{{.Label}} {{if eq .Type "secret"}}<span class="auto-generated-badge"><svg class="ico ico-sm"><use href="#i-check"/></svg> Automatikusan generálva</span>{{end}}</label>
<label>{{.Label}} {{if eq .Type "secret"}}<span class="auto-generated-badge"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "deploy.automatikusan_generalva"}}</span>{{end}}</label>
{{if $val}}
{{if eq .Type "secret"}}
{{if $isDeployed}}
@@ -512,12 +504,12 @@
submits, so the saved value is the one the customer was shown (README §318). -->
<div class="input-with-button">
<input type="password" id="auto-field-{{.EnvVar}}" class="form-control" value="" placeholder="••••••••••••" readonly>
<button type="button" class="btn btn-sm btn-outline" onclick="revealAutoField('{{$stackName}}','{{.EnvVar}}', this)">Megjelenítés</button>
<button type="button" class="btn btn-sm btn-outline" onclick="revealAutoField('{{$stackName}}','{{.EnvVar}}', this)">{{T "deploy.megjelenites"}}</button>
</div>
{{else}}
<div class="input-with-button">
<input type="password" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
<button type="button" class="btn btn-sm btn-outline" onclick="toggleAutoField('auto-field-{{.EnvVar}}', this)">Megjelenítés</button>
<button type="button" class="btn btn-sm btn-outline" onclick="toggleAutoField('auto-field-{{.EnvVar}}', this)">{{T "deploy.megjelenites"}}</button>
</div>
{{end}}
{{else}}
@@ -534,13 +526,13 @@
{{if .UserFields}}
<div class="form-section">
<h4>Beállítások</h4>
<h4>{{T "deploy.beallitasok"}}</h4>
{{range .UserFields}}
<div class="form-group">
<label for="field-{{.EnvVar}}">
{{.Label}}
{{if or .Required (eq .Type "password")}}<span class="required">*</span>{{end}}
{{if .LockedAfterDeploy}}<span class="locked-hint">telepítés után nem módosítható</span>{{end}}
{{if .LockedAfterDeploy}}<span class="locked-hint">{{T "deploy.telepites_utan_nem_modosithato"}}</span>{{end}}
</label>
{{if eq .Type "subdomain"}}
@@ -550,7 +542,7 @@
{{/* R-351: a reinstall offers the address the customer's own backup recorded,
ahead of the catalog default. Editable — a domain can legitimately change. */}}
value="{{if and $.AlreadyDeployed $.DeployedFieldValues}}{{index $.DeployedFieldValues .EnvVar}}{{else if index $.RestoreFieldValues .EnvVar}}{{index $.RestoreFieldValues .EnvVar}}{{else}}{{.Default}}{{end}}"
placeholder="aldomain"
placeholder="{{T "deploy.aldomain_placeholder"}}"
pattern="[a-z0-9]([a-z0-9-]*[a-z0-9])?"
required
{{if $.AlreadyDeployed}}disabled{{end}}
@@ -558,7 +550,7 @@
<span class="subdomain-suffix">.{{$.Domain}}</span>
</div>
{{if not $.AlreadyDeployed}}
<span class="form-hint">Az aldomain telepítés után nem módosítható. Megváltoztatáshoz az alkalmazás eltávolítása és újratelepítése szükséges (minden adat törlődik).</span>
<span class="form-hint">{{T "deploy.az_aldomain_telepites_utan_nem"}}</span>
{{end}}
{{else if eq .Type "select"}}
<select id="field-{{.EnvVar}}" name="{{.EnvVar}}" class="form-control"
@@ -577,18 +569,18 @@
{{if $.AlreadyDeployed}}disabled{{end}}>
<button type="button" class="btn btn-sm btn-outline pw-toggle-btn"
onclick="togglePasswordField('field-{{.EnvVar}}', 'field-confirm-{{.EnvVar}}', this)"
title="Megjelenítés">&#128065;</button>
title="{{T "deploy.megjelenites"}}">&#128065;</button>
{{if not $.AlreadyDeployed}}
<button type="button" class="btn btn-sm btn-outline"
onclick="generatePassword('field-{{.EnvVar}}', 'field-confirm-{{.EnvVar}}')">Generálás</button>
onclick="generatePassword('field-{{.EnvVar}}', 'field-confirm-{{.EnvVar}}')">{{T "deploy.generalas"}}</button>
{{end}}
</div>
{{if $.AlreadyDeployed}}
<span class="form-hint">Telepítéskor beállított kezdeti jelszó — ha az alkalmazásban megváltoztattad, az itt nem frissül.</span>
<span class="form-hint">{{T "deploy.telepiteskor_beallitott_kezdeti_jelszo_h"}}</span>
{{else}}
<div class="input-with-button" style="margin-top:.25rem">
<input type="password" id="field-confirm-{{.EnvVar}}"
class="form-control" placeholder="Jelszó megerősítése"
class="form-control" placeholder="{{T "deploy.jelszo_megerositese"}}"
data-confirm-for="field-{{.EnvVar}}">
</div>
{{end}}
@@ -596,7 +588,7 @@
<label class="toggle">
<input type="checkbox" id="field-{{.EnvVar}}" name="{{.EnvVar}}" value="true"
{{if $.AlreadyDeployed}}disabled{{end}}>
<span class="toggle-label">Igen</span>
<span class="toggle-label">{{T "deploy.igen"}}</span>
</label>
{{else if eq .Type "path"}}
{{if $.StoragePaths}}
@@ -611,19 +603,18 @@
restoring into a different drive is the mismatch the restore then has
to stop and name. The customer can still pick another. */}}
{{if $.AlreadyDeployed}}{{if eq .Path $.CurrentHDDPath}}selected{{end}}{{else if $.RestorePrefillHDDPath}}{{if and (eq .Path $.RestorePrefillHDDPath) (not .NotAllowed)}}selected{{end}}{{else if and .IsDefault (not .NotAllowed)}}selected{{end}}>
{{.Label}} — {{.FreeHuman}} szabad{{if .NotAllowed}} ({{.NotAllowedNote}}){{else if .IsDefault}} (alapértelmezett){{end}}
{{T "deploy.szabad_2"}}{{if .NotAllowed}} ({{.NotAllowedNote}}){{else if .IsDefault}} {{T "deploy.alapertelmezett"}}{{end}}
</option>
{{end}}
{{if $.CurrentHDDPathMissing}}
<option value="{{$.CurrentHDDPath}}" selected disabled>{{$.CurrentHDDPath}} (nem elérhető)</option>
<option value="{{$.CurrentHDDPath}}" selected disabled>{{T "deploy.nem_elerheto"}}</option>
{{end}}
</select>
<div id="storage-space-warn" class="form-hint" style="color:var(--warn);display:none">
<svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> A kiválasztott tárhely majdnem megtelt.
<svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> {{T "deploy.a_kivalasztott_tarhely_majdnem_megtelt"}}
</div>
<div class="form-hint" style="margin-top:.4rem;opacity:.8">
<svg class="ico ico-sm"><use href="#i-info"/></svg> A kiválasztott meghajtón az alkalmazás <strong>fájljai</strong> (média, dokumentumok) tárolódnak.
Az <strong>adatbázis a gyors belső SSD-n</strong> fut — és az alkalmazással együtt készül róla biztonsági mentés.
<svg class="ico ico-sm"><use href="#i-info"/></svg> {{T "deploy.a_kivalasztott_meghajton_az_alkalmazas"}}
</div>
{{else}}
<input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
@@ -631,7 +622,7 @@
placeholder="{{.Placeholder}}"
{{if .Required}}required{{end}}
{{if $.AlreadyDeployed}}disabled{{end}}>
<span class="form-hint" style="color:var(--warn)">Nincs regisztrált adattároló — adja meg kézzel az útvonalat</span>
<span class="form-hint" style="color:var(--warn)">{{T "deploy.nincs_regisztralt_adattarolo_adja_meg"}}</span>
{{end}}
{{else}}
<input type="text" id="field-{{.EnvVar}}" name="{{.EnvVar}}"
@@ -651,26 +642,26 @@
{{if not .AlreadyDeployed}}
<div class="deploy-actions">
<button type="submit" class="btn btn-primary btn-lg"{{if .DockerBelowReserve}} disabled title="Nincs elég szabad tárhely"{{else if and .MemoryInfo (index .MemoryInfo "Blocked")}} disabled title="Nincs elég memória"{{end}}>Telepítés indítása</button>
<a href="/stacks" class="btn btn-outline">Mégsem</a>
<button type="submit" class="btn btn-primary btn-lg"{{if .DockerBelowReserve}} disabled title="{{T "deploy.nincs_eleg_szabad_tarhely"}}"{{else if and .MemoryInfo (index .MemoryInfo "Blocked")}} disabled title="{{T "deploy.nincs_eleg_memoria"}}"{{end}}>{{T "deploy.telepites_inditasa"}}</button>
<a href="/stacks" class="btn btn-outline">{{T "common.megsem"}}</a>
</div>
{{end}}
</form>
<div id="deploy-progress" class="deploy-progress" style="display:none">
<h3>Telepítés folyamatban...</h3>
<h3>{{T "deploy.telepites_folyamatban"}}</h3>
<div class="deploy-steps">
<div class="deploy-step active" id="step-config">
<span class="step-icon"><svg class="ico"><use href="#i-rotate-cw"/></svg></span>
<span class="step-text">Konfiguráció mentése...</span>
<span class="step-text">{{T "deploy.konfiguracio_mentese"}}</span>
</div>
<div class="deploy-step" id="step-containers">
<span class="step-icon"><svg class="ico"><use href="#i-rotate-cw"/></svg></span>
<span class="step-text">Konténer(ek) indítása...</span>
<span class="step-text">{{T "deploy.kontener_ek_inditasa"}}</span>
</div>
<div class="deploy-step" id="step-health">
<span class="step-icon"><svg class="ico"><use href="#i-rotate-cw"/></svg></span>
<span class="step-text">Alkalmazás inicializálása...</span>
<span class="step-text">{{T "deploy.alkalmazas_inicializalasa"}}</span>
</div>
</div>
<div id="deploy-warning" class="alert alert-warning" style="display:none"></div>
@@ -701,13 +692,13 @@ function buildPostDeployCard(stackName) {
if (subdomain && postDeployInfo.domain) {
var appURL = 'https://' + subdomain + '.' + postDeployInfo.domain;
html += '<div style="text-align:center;margin:1.5rem 0">' +
'<a href="' + appURL + '" target="_blank" class="btn btn-primary btn-lg">Alkalmazás megnyitása ↗</a>' +
'<a href="' + appURL + '" target="_blank" class="btn btn-primary btn-lg">{{T "deploy.alkalmazas_megnyitasa"}}</a>' +
'</div>';
}
// First steps — with dynamic credential hint
if (postDeployInfo.firstSteps && postDeployInfo.firstSteps.length > 0) {
html += '<div class="app-info-card" style="margin-top:1rem"><h4>Első lépések</h4><ol class="app-info-list">';
html += '<div class="app-info-card" style="margin-top:1rem"><h4>{{T "deploy.elso_lepesek"}}</h4><ol class="app-info-list">';
for (var i = 0; i < postDeployInfo.firstSteps.length; i++) {
var step = postDeployInfo.firstSteps[i].replace(/DOMAIN/g, postDeployInfo.domain);
html += '<li>' + step + '</li>';
@@ -742,8 +733,8 @@ function buildPostDeployCard(stackName) {
credRows += '<tr><td style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">' + f.label + '</td>' +
'<td style="padding:.25rem 0;display:flex;align-items:center;gap:.5rem">' +
'<code id="' + credId + '" style="font-family:var(--font-data);letter-spacing:1px">••••••••••••</code>' +
'<button type="button" class="btn btn-sm btn-outline" onclick="togglePostDeployCred(this,\'' + credId + '\',\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">Megjelenítés</button>' +
'<button type="button" class="btn btn-sm btn-outline" onclick="copyPostDeployCred(this,\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">Másolás</button>' +
'<button type="button" class="btn btn-sm btn-outline" onclick="togglePostDeployCred(this,\'' + credId + '\',\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">{{T "deploy.megjelenites"}}</button>' +
'<button type="button" class="btn btn-sm btn-outline" onclick="copyPostDeployCred(this,\'' + val.replace(/'/g, "\\'") + '\')" style="padding:.15rem .4rem;font-size:.75rem">{{T "deploy.masolas"}}</button>' +
'</td></tr>';
} else {
credRows += '<tr><td style="padding:.25rem .75rem .25rem 0;color:var(--text-3);white-space:nowrap">' + f.label + '</td>' +
@@ -752,23 +743,23 @@ function buildPostDeployCard(stackName) {
}
}
if (credRows) {
html += '<div class="app-info-card" style="margin-top:1rem"><h4>Bejelentkezés</h4>' +
html += '<div class="app-info-card" style="margin-top:1rem"><h4>{{T "deploy.bejelentkezes"}}</h4>' +
'<table style="margin:.5rem 0">' + credRows + '</table></div>';
} else if (postDeployInfo.defaultCreds) {
var creds = postDeployInfo.defaultCreds.replace(/DOMAIN/g, postDeployInfo.domain);
html += '<div class="app-info-card" style="margin-top:1rem"><h4>Bejelentkezés</h4>' +
html += '<div class="app-info-card" style="margin-top:1rem"><h4>{{T "deploy.bejelentkezes"}}</h4>' +
'<p class="app-info-creds">' + creds + '</p></div>';
}
// Docs link
if (postDeployInfo.docsURL) {
html += '<div style="margin-top:1rem"><a href="' + postDeployInfo.docsURL + '" target="_blank" class="btn btn-sm btn-outline">Dokumentáció ↗</a></div>';
html += '<div style="margin-top:1rem"><a href="' + postDeployInfo.docsURL + '" target="_blank" class="btn btn-sm btn-outline">{{T "deploy.dokumentacio"}}</a></div>';
}
// Action buttons
html += '<div style="display:flex;gap:.75rem;margin-top:1.5rem;flex-wrap:wrap">' +
'<a href="/stacks/' + stackName + '/deploy" class="btn btn-outline">Beállítások és jelszavak megtekintése</a>' +
'<a href="/stacks" class="btn btn-sm btn-outline">← Alkalmazások</a>' +
'<a href="/stacks/' + stackName + '/deploy" class="btn btn-outline">{{T "deploy.beallitasok_es_jelszavak_megtekintese"}}</a>' +
'<a href="/stacks" class="btn btn-sm btn-outline">{{T "deploy.alkalmazasok"}}</a>' +
'</div>';
return html;
@@ -796,7 +787,7 @@ function revealAutoField(stackName, envVar, btn) {
el.value = '';
el.type = 'password';
btn.dataset.shown = '';
btn.textContent = 'Megjelenítés';
btn.textContent = '{{T "deploy.megjelenites"}}';
return;
}
btn.disabled = true;
@@ -807,18 +798,18 @@ function revealAutoField(stackName, envVar, btn) {
body: 'env_var=' + encodeURIComponent(envVar)
}).then(function (r) { return r.json(); }).then(function (j) {
btn.disabled = false;
if (!j.ok) { showAlert(j.error || 'A lekérés nem sikerült.'); return; }
if (!j.ok) { showAlert(j.error || '{{T "deploy.a_lekeres_nem_sikerult"}}'); return; }
el.value = j.data.value;
el.type = 'text';
btn.dataset.shown = '1';
btn.textContent = 'Elrejtés';
}).catch(function () { btn.disabled = false; showAlert('A lekérés nem sikerült.'); });
btn.textContent = '{{T "deploy.elrejtes"}}';
}).catch(function () { btn.disabled = false; showAlert('{{T "deploy.a_lekeres_nem_sikerult"}}'); });
}
function toggleAutoField(fieldId, btn) {
var el = document.getElementById(fieldId);
if (!el) return;
el.type = el.type === 'password' ? 'text' : 'password';
btn.textContent = el.type === 'password' ? 'Megjelenítés' : 'Elrejtés';
btn.textContent = el.type === 'password' ? '{{T "deploy.megjelenites"}}' : '{{T "deploy.elrejtes"}}';
}
function generatePassword(fieldId, confirmFieldId) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
@@ -841,7 +832,7 @@ function togglePasswordField(fieldId, confirmFieldId, btn) {
el.type = newType;
var ce = document.getElementById(confirmFieldId);
if (ce) ce.type = newType;
btn.title = newType === 'password' ? 'Megjelenítés' : 'Elrejtés';
btn.title = newType === 'password' ? '{{T "deploy.megjelenites"}}' : '{{T "deploy.elrejtes"}}';
}
function togglePostDeployCred(btn, credId, val) {
@@ -849,17 +840,17 @@ function togglePostDeployCred(btn, credId, val) {
if (!el) return;
if (el.textContent === val) {
el.textContent = '••••••••••••';
btn.textContent = 'Megjelenítés';
btn.textContent = '{{T "deploy.megjelenites"}}';
} else {
el.textContent = val;
btn.textContent = 'Elrejtés';
btn.textContent = '{{T "deploy.elrejtes"}}';
}
}
function copyPostDeployCred(btn, val) {
if (navigator.clipboard) {
navigator.clipboard.writeText(val).then(function() {
var orig = btn.textContent;
btn.textContent = 'Másolva!';
btn.textContent = '{{T "deploy.masolva"}}';
setTimeout(function() { btn.textContent = orig; }, 1500);
});
}
@@ -867,8 +858,8 @@ function copyPostDeployCred(btn, val) {
function deleteStaleData(stackName, stalePath, btn) {
// Two-step inline confirm (the irreversible delete keeps its double acknowledgement).
felhomConfirm(btn, 'Biztosan törölni szeretnéd a korábbi adatokat? Tárhely: ' + stalePath + ' — FIGYELEM: a művelet visszavonhatatlan! Előtte győződj meg róla, hogy az alkalmazás az új tárolóról megfelelően működik.', function () {
felhomConfirm(btn, 'UTOLSÓ FIGYELMEZTETÉS! A törlés visszavonhatatlan. Biztosan folytatod?', function () {
felhomConfirm(btn, '{{T "deploy.biztosan_torolni_szeretned_a_korabbi"}} ' + stalePath + ' {{T "deploy.figyelem_a_muvelet_visszavonhatatlan_elo"}}', function () {
felhomConfirm(btn, '{{T "deploy.utolso_figyelmeztetes_a_torles_visszavon"}}', function () {
deleteStaleDataRun(stackName, stalePath, btn);
});
});
@@ -876,7 +867,7 @@ function deleteStaleData(stackName, stalePath, btn) {
function deleteStaleDataRun(stackName, stalePath, btn) {
btn.disabled = true;
btn.textContent = 'Törlés folyamatban...';
btn.textContent = '{{T "deploy.torles_folyamatban"}}';
fetch('/api/storage/stale-cleanup', {
method: 'POST',
@@ -886,14 +877,14 @@ function deleteStaleDataRun(stackName, stalePath, btn) {
.then(function(r) { return r.json(); })
.then(function(data) {
if (!data.ok) {
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
showAlert('{{T "common.hiba_kettospont"}} ' + (data.error || '{{T "deploy.ismeretlen_hiba"}}'));
btn.disabled = false;
btn.textContent = 'Korábbi adatok törlése';
btn.textContent = '{{T "deploy.korabbi_adatok_torlese"}}';
return;
}
var msg = 'Korábbi adatok törölve!\n\nFelszabadított hely: ' + (data.freed_human || '?');
var msg = '{{T "deploy.korabbi_adatok_torolve_n_nfelszabaditott"}} ' + (data.freed_human || '?');
if (data.errors && data.errors.length > 0) {
msg += '\n\nNéhány hiba történt:\n' + data.errors.join('\n');
msg += '{{T "deploy.n_nnehany_hiba_tortent_n"}}' + data.errors.join('\n');
}
showAlert(msg);
// Remove the stale data card from DOM
@@ -906,9 +897,9 @@ function deleteStaleDataRun(stackName, stalePath, btn) {
}
})
.catch(function(e) {
showAlert('Hálózati hiba: ' + e.message);
showAlert('{{T "deploy.halozati_hiba_2"}} ' + e.message);
btn.disabled = false;
btn.textContent = 'Korábbi adatok törlése';
btn.textContent = '{{T "deploy.korabbi_adatok_torlese"}}';
});
}
@@ -920,7 +911,7 @@ document.getElementById('deploy-form').addEventListener('submit', async function
for (const pf of passwordFields) {
if (!pf.disabled && pf.value.trim() === '') {
const label = pf.closest('.form-group').querySelector('label').textContent.trim();
showAlert('Kötelező mező: ' + label + '\nHasználja a Generálás gombot vagy írjon be egy jelszót.');
showAlert('{{T "deploy.kotelezo_mezo"}} ' + label + '{{T "deploy.nhasznalja_a_generalas_gombot_vagy"}}');
pf.focus();
return;
}
@@ -932,7 +923,7 @@ document.getElementById('deploy-form').addEventListener('submit', async function
const mainEl = document.getElementById(ci.getAttribute('data-confirm-for'));
if (mainEl && !mainEl.disabled && ci.value !== mainEl.value) {
const label = mainEl.closest('.form-group').querySelector('label').textContent.trim();
showAlert('A két jelszó nem egyezik: ' + label);
showAlert('{{T "deploy.a_ket_jelszo_nem_egyezik"}} ' + label);
ci.focus();
return;
}
@@ -943,7 +934,7 @@ document.getElementById('deploy-form').addEventListener('submit', async function
if (subdomainField && !subdomainField.disabled) {
const sd = subdomainField.value.trim().toLowerCase();
if (!sd || !/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(sd)) {
showAlert('Az aldomain csak kisbetűket, számokat és kötőjelet tartalmazhat, és nem kezdődhet/végződhet kötőjellel.');
showAlert('{{T "deploy.az_aldomain_csak_kisbetuket_szamokat"}}');
subdomainField.focus();
return;
}
@@ -954,7 +945,7 @@ document.getElementById('deploy-form').addEventListener('submit', async function
for (const rf of requiredFields) {
if (!rf.disabled && rf.value.trim() === '') {
const label = rf.closest('.form-group').querySelector('label').textContent.trim();
showAlert('Kötelező mező: ' + label);
showAlert('{{T "deploy.kotelezo_mezo"}} ' + label);
rf.focus();
return;
}
@@ -962,7 +953,7 @@ document.getElementById('deploy-form').addEventListener('submit', async function
const btn = e.target.querySelector('[type=submit]');
btn.disabled = true;
btn.textContent = 'Telepítés folyamatban...';
btn.textContent = '{{T "deploy.telepites_folyamatban"}}';
const values = {};
const inputs = e.target.querySelectorAll('input, select');
@@ -1005,8 +996,8 @@ document.getElementById('deploy-form').addEventListener('submit', async function
});
var data = await resp.json();
if (!data.ok) {
showAlert('Hiba: ' + data.error);
btn.textContent = 'Telepítés indítása';
showAlert('{{T "common.hiba_kettospont"}} ' + data.error);
btn.textContent = '{{T "deploy.telepites_inditasa"}}';
btn.disabled = false;
return;
}
@@ -1014,8 +1005,8 @@ document.getElementById('deploy-form').addEventListener('submit', async function
// Deploy API returned success — switch to progress view
formEl.style.display = 'none';
progressEl.style.display = 'block';
setStep(stepConfig, 'done', 'Konfiguráció mentve');
setStep(stepContainers, 'active', 'Konténer(ek) indítása...');
setStep(stepConfig, 'done', '{{T "deploy.konfiguracio_mentve"}}');
setStep(stepContainers, 'active', '{{T "deploy.kontener_ek_inditasa"}}');
if (data.data && data.data.warning) {
warningEl.textContent = data.data.warning;
@@ -1027,13 +1018,13 @@ document.getElementById('deploy-form').addEventListener('submit', async function
var pollTimeout = 120000;
var pollTimer = setInterval(async function() {
var elapsed = Math.round((Date.now() - startTime) / 1000);
elapsedEl.textContent = elapsed + ' másodperce...';
elapsedEl.textContent = elapsed + ' {{T "deploy.masodperce"}}';
if (Date.now() - startTime > pollTimeout) {
clearInterval(pollTimer);
setStep(stepHealth, 'warn', 'Időtúllépés — az alkalmazás még indulhat');
setStep(stepHealth, 'warn', '{{T "deploy.idotullepes_az_alkalmazas_meg_indulhat"}}');
resultEl.innerHTML = '<div class="alert alert-warning" style="margin-top:1rem">' +
'A telepítés időtúllépésbe futott. Az alkalmazás még indulhat.' +
'{{T "deploy.a_telepites_idotullepesbe_futott_az"}}' +
'</div>' + buildPostDeployCard(stackName);
resultEl.style.display = 'block';
return;
@@ -1049,56 +1040,56 @@ document.getElementById('deploy-form').addEventListener('submit', async function
if (deployError) {
// Async compose-up failed
clearInterval(pollTimer);
setStep(stepContainers, 'error', 'Telepítés sikertelen');
setStep(stepContainers, 'error', '{{T "deploy.telepites_sikertelen"}}');
setStep(stepHealth, 'error');
progressEl.querySelector('h3').textContent = 'Telepítés sikertelen';
progressEl.querySelector('h3').textContent = '{{T "deploy.telepites_sikertelen"}}';
resultEl.innerHTML = '<div class="alert alert-error" style="margin-top:1rem">' +
'A telepítés nem sikerült: ' + deployError +
'</div><a href="/stacks/' + stackName + '/logs" class="btn btn-outline" style="margin-top:.75rem">Naplók megtekintése</a>' +
' <a href="/stacks" class="btn btn-primary" style="margin-top:.75rem">Alkalmazások</a>';
'{{T "deploy.a_telepites_nem_sikerult"}} ' + deployError +
'</div><a href="/stacks/' + stackName + '/logs" class="btn btn-outline" style="margin-top:.75rem">{{T "deploy.naplok_megtekintese"}}</a>' +
' <a href="/stacks" class="btn btn-primary" style="margin-top:.75rem">{{T "deploy.alkalmazasok_2"}}</a>';
resultEl.style.display = 'block';
} else if (state === 'deploying') {
// Compose up in progress (pulling images, creating containers)
setStep(stepContainers, 'active', 'Képek letöltése, konténerek indítása...');
setStep(stepContainers, 'active', '{{T "deploy.kepek_letoltese_kontenerek_inditasa"}}');
} else if (state === 'running') {
clearInterval(pollTimer);
setStep(stepContainers, 'done', 'Konténerek elindultak');
setStep(stepHealth, 'done', 'Alkalmazás kész!');
progressEl.querySelector('h3').textContent = 'Telepítés sikeres!';
setStep(stepContainers, 'done', '{{T "deploy.kontenerek_elindultak"}}');
setStep(stepHealth, 'done', '{{T "deploy.alkalmazas_kesz"}}');
progressEl.querySelector('h3').textContent = '{{T "deploy.telepites_sikeres"}}';
resultEl.innerHTML = '<div class="alert alert-info" style="margin-top:1rem">' +
'Az alkalmazás sikeresen telepítve és fut!' +
'{{T "deploy.az_alkalmazas_sikeresen_telepitve_es"}}' +
'</div>' + buildPostDeployCard(stackName);
resultEl.style.display = 'block';
} else if (state === 'starting') {
setStep(stepContainers, 'done', 'Konténerek elindultak');
setStep(stepHealth, 'active', 'Alkalmazás inicializálása...');
setStep(stepContainers, 'done', '{{T "deploy.kontenerek_elindultak"}}');
setStep(stepHealth, 'active', '{{T "deploy.alkalmazas_inicializalasa"}}');
} else if (state === 'unhealthy') {
clearInterval(pollTimer);
setStep(stepContainers, 'done', 'Konténerek elindultak');
setStep(stepHealth, 'warn', 'Állapotjelző: még inicializál');
progressEl.querySelector('h3').textContent = 'Telepítés sikeres!';
setStep(stepContainers, 'done', '{{T "deploy.kontenerek_elindultak"}}');
setStep(stepHealth, 'warn', '{{T "deploy.allapotjelzo_meg_inicializal"}}');
progressEl.querySelector('h3').textContent = '{{T "deploy.telepites_sikeres"}}';
resultEl.innerHTML = '<div class="alert alert-warning" style="margin-top:1rem">' +
'Az alkalmazás elindult, de még inicializálódik. ' +
'Ez normális az első percekben — az alkalmazás már elérhető lehet.' +
'{{T "deploy.az_alkalmazas_elindult_de_meg"}} ' +
'{{T "deploy.ez_normalis_az_elso_percekben"}}' +
'</div>' + buildPostDeployCard(stackName);
resultEl.style.display = 'block';
} else if (state === 'exited' || state === 'stopped') {
clearInterval(pollTimer);
setStep(stepContainers, 'error', 'A konténer leállt');
setStep(stepContainers, 'error', '{{T "deploy.a_kontener_leallt"}}');
setStep(stepHealth, 'error');
progressEl.querySelector('h3').textContent = 'Telepítés sikertelen';
progressEl.querySelector('h3').textContent = '{{T "deploy.telepites_sikertelen"}}';
resultEl.innerHTML = '<div class="alert alert-error" style="margin-top:1rem">' +
'A konténer leállt. Ellenőrizze a naplókat.' +
'</div><a href="/stacks/' + stackName + '/logs" class="btn btn-outline" style="margin-top:.75rem">Naplók megtekintése</a>' +
' <a href="/stacks" class="btn btn-primary" style="margin-top:.75rem">Alkalmazások</a>';
'{{T "deploy.a_kontener_leallt_ellenorizze_a"}}' +
'</div><a href="/stacks/' + stackName + '/logs" class="btn btn-outline" style="margin-top:.75rem">{{T "deploy.naplok_megtekintese"}}</a>' +
' <a href="/stacks" class="btn btn-primary" style="margin-top:.75rem">{{T "deploy.alkalmazasok_2"}}</a>';
resultEl.style.display = 'block';
}
} catch(pollErr) {}
}, 3000);
} catch (err) {
showAlert('Hálózati hiba: ' + err.message);
btn.textContent = 'Telepítés indítása';
showAlert('{{T "deploy.halozati_hiba_2"}} ' + err.message);
btn.textContent = '{{T "deploy.telepites_inditasa"}}';
btn.disabled = false;
}
});
@@ -68,7 +68,7 @@
</div>
<div class="share-field">
{{if .SharePasswordSet}}
<label class="share-label">{{T "launcher.jelszo"}}</label>
<label class="share-label">{{T "common.jelszo"}}</label>
<p class="share-note">{{T "launcher.a_megosztas_jelszoval_vedett"}}</p>
<form method="POST" action="/launcher/share/password">
{{.CSRFField}}
@@ -101,14 +101,14 @@
<form method="POST" action="/launcher/share/enable">
{{.CSRFField}}
<div class="modal-actions">
<button type="button" class="btn btn-outline" onclick="closeShareModal()">{{T "launcher.bezaras"}}</button>
<button type="button" class="btn btn-outline" onclick="closeShareModal()">{{T "common.bezaras"}}</button>
<button type="submit" class="btn btn-primary">{{T "launcher.megosztas_bekapcsolasa"}}</button>
</div>
</form>
{{end}}
{{if .ShareEnabled}}
<div class="modal-actions">
<button type="button" class="btn btn-outline" onclick="closeShareModal()">{{T "launcher.bezaras"}}</button>
<button type="button" class="btn btn-outline" onclick="closeShareModal()">{{T "common.bezaras"}}</button>
</div>
{{end}}
</div>
+18 -18
View File
@@ -265,7 +265,7 @@
});
const data = await resp.json();
if (toast) {
toast.textContent = data.ok ? (data.message || '{{T "layout.sablonok_frissitve"}}') : ('{{T "layout.hiba"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
toast.textContent = data.ok ? (data.message || '{{T "layout.sablonok_frissitve"}}') : ('{{T "common.hiba_kettospont"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
toast.style.display = 'block';
setTimeout(function() { toast.style.display = 'none'; }, 5000);
@@ -298,7 +298,7 @@
});
const data = await resp.json();
if (!data.ok) {
showAlert('{{T "layout.hiba"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
showAlert('{{T "common.hiba_kettospont"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
btn.textContent = origText;
btn.disabled = false;
btn.classList.remove('loading');
@@ -358,13 +358,13 @@
'<div class="alert alert-warning" style="margin-bottom:.75rem">{{T "layout.ez_a_muvelet_nem_visszavonhato"}}</div>' +
hddInfo + checkboxHTML +
'<div class="modal-actions">' +
'<button class="btn btn-outline" onclick="closeDeleteModal()">{{T "layout.megsem"}}</button>' +
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">{{T "layout.torles"}}</button>' +
'<button class="btn btn-outline" onclick="closeDeleteModal()">{{T "common.megsem"}}</button>' +
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">{{T "common.torles"}}</button>' +
'</div>';
} catch (err) {
modal.querySelector('.modal-card').innerHTML =
'<h3>{{T "layout.hiba_2"}}</h3><p style="color:var(--text-2)">{{T "layout.nem_sikerult_lekerni_az_adatokat"}} ' + err.message + '</p>' +
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">{{T "layout.bezaras"}}</button></div>';
'<h3>{{T "common.hiba"}}</h3><p style="color:var(--text-2)">{{T "layout.nem_sikerult_lekerni_az_adatokat"}} ' + err.message + '</p>' +
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">{{T "common.bezaras"}}</button></div>';
}
}
function closeDeleteModal() {
@@ -401,16 +401,16 @@
'<h3>{{T "layout.sikeresen_torolve"}}</h3>' +
'<p style="color:var(--text-2)">{{T "layout.az_alkalmazas"}}' + name + '{{T "layout.torolve_lett"}}</p>' +
removedInfo + preservedInfo +
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">{{T "layout.bezaras"}}</button></div>';
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">{{T "common.bezaras"}}</button></div>';
} else {
showAlert('{{T "layout.hiba"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
showAlert('{{T "common.hiba_kettospont"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
btn.disabled = false;
btn.textContent = '{{T "layout.torles"}}';
btn.textContent = '{{T "common.torles"}}';
}
} catch (err) {
showAlert('{{T "layout.halozati_hiba"}} ' + err.message);
btn.disabled = false;
btn.textContent = '{{T "layout.torles"}}';
btn.textContent = '{{T "common.torles"}}';
}
}
async function removeStack(name) {
@@ -471,13 +471,13 @@
'<div class="alert alert-warning" style="margin-bottom:.75rem">{{T "layout.ez_a_muvelet_nem_visszavonhato"}}</div>' +
sections +
'<div class="modal-actions">' +
'<button class="btn btn-outline" onclick="closeRemoveModal()">{{T "layout.megsem"}}</button>' +
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">{{T "layout.eltavolitas"}}</button>' +
'<button class="btn btn-outline" onclick="closeRemoveModal()">{{T "common.megsem"}}</button>' +
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">{{T "common.eltavolitas"}}</button>' +
'</div>' + hddCheckbox;
} catch (err) {
modal.querySelector('.modal-card').innerHTML =
'<h3>{{T "layout.hiba_2"}}</h3><p style="color:var(--text-2)">{{T "layout.nem_sikerult_lekerni_az_adatokat"}} ' + err.message + '</p>' +
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">{{T "layout.bezaras"}}</button></div>';
'<h3>{{T "common.hiba"}}</h3><p style="color:var(--text-2)">{{T "layout.nem_sikerult_lekerni_az_adatokat"}} ' + err.message + '</p>' +
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">{{T "common.bezaras"}}</button></div>';
}
}
function closeRemoveModal() {
@@ -522,16 +522,16 @@
'<h3>{{T "layout.sikeresen_eltavolitva"}}</h3>' +
'<p style="color:var(--text-2)">{{T "layout.az_alkalmazas"}}' + name + '{{T "layout.eltavolitva_ujratelepitheto_a_telepites"}}</p>' +
removedInfo + preservedInfo +
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">{{T "layout.bezaras"}}</button></div>';
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">{{T "common.bezaras"}}</button></div>';
} else {
showAlert('{{T "layout.hiba"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
showAlert('{{T "common.hiba_kettospont"}} ' + (data.error || '{{T "layout.ismeretlen_hiba"}}'));
btn.disabled = false;
btn.textContent = '{{T "layout.eltavolitas"}}';
btn.textContent = '{{T "common.eltavolitas"}}';
}
} catch (err) {
showAlert('{{T "layout.halozati_hiba"}} ' + err.message);
btn.disabled = false;
btn.textContent = '{{T "layout.eltavolitas"}}';
btn.textContent = '{{T "common.eltavolitas"}}';
}
}
</script>
+9 -9
View File
@@ -1,18 +1,18 @@
{{define "logs"}}
{{template "layout_start" .}}
<div class="page-header">
<a href="/stacks" class="btn btn-sm btn-outline">← Vissza</a>
<h2>{{.Stack.Meta.DisplayName}} — Naplók</h2>
<a href="/stacks" class="btn btn-sm btn-outline">{{T "logs.vissza"}}</a>
<h2>{{T "logs.naplok"}}</h2>
</div>
<div class="logs-container" id="logs-container">
<pre class="logs-output" id="logs-output">{{.Logs}}</pre>
</div>
<div class="logs-actions">
<span class="logs-live-indicator" id="live-indicator">
<span class="logs-live-dot"></span> Élő
<span class="logs-live-dot"></span> {{T "logs.elo"}}
</span>
<button class="btn btn-outline btn-sm" id="logs-toggle" onclick="toggleLive()">Szüneteltetés</button>
<button class="btn btn-outline btn-sm" onclick="fetchLogs()">Frissítés</button>
<button class="btn btn-outline btn-sm" id="logs-toggle" onclick="toggleLive()">{{T "logs.szuneteltetes"}}</button>
<button class="btn btn-outline btn-sm" onclick="fetchLogs()">{{T "logs.frissites"}}</button>
</div>
<script>
(function() {
@@ -53,13 +53,13 @@
if (live) {
startPolling();
indicator.className = 'logs-live-indicator';
indicator.innerHTML = '<span class="logs-live-dot"></span> Élő';
toggleBtn.textContent = 'Szüneteltetés';
indicator.innerHTML = '<span class="logs-live-dot"></span> {{T "logs.elo"}}';
toggleBtn.textContent = '{{T "logs.szuneteltetes"}}';
} else {
stopPolling();
indicator.className = 'logs-live-indicator logs-live-paused';
indicator.innerHTML = 'Szünetelve';
toggleBtn.textContent = 'Folytatás';
indicator.innerHTML = '{{T "logs.szunetelve"}}';
toggleBtn.textContent = '{{T "logs.folytatas"}}';
}
};
@@ -2,57 +2,57 @@
{{template "layout_start" .}}
<div class="page-header">
<h2>Rendszermonitor</h2>
<h2>{{T "monitoring.rendszermonitor"}}</h2>
</div>
<!-- Section 0: Host (Proxmox box) health — slice 9, served by the host agent over the local API.
The de-privileged controller can't read the host itself; this card shows the real box. -->
<div class="monitor-card" id="host-health-card">
<div class="monitor-card-header">
<h3>Szerver állapota (gazdagép)</h3>
<h3>{{T "monitoring.szerver_allapota_gazdagep"}}</h3>
<span class="sysinfo-value" id="host-health-updated" style="font-size:.75rem;color:var(--text-3)"></span>
</div>
<div id="host-health-unavailable" class="monitoring-banner monitoring-banner-yellow" style="display:none">
A gazdagép metrikái jelenleg nem elérhetők.
{{T "monitoring.a_gazdagep_metrikai_jelenleg_nem"}}
</div>
<div id="host-health-body" style="display:none">
<div class="sysinfo-grid">
<div class="sysinfo-row">
<span class="sysinfo-label">CPU használat</span>
<span class="sysinfo-label">{{T "monitoring.cpu_hasznalat"}}</span>
<span class="sysinfo-value" id="host-cpu">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Terhelés (load)</span>
<span class="sysinfo-label">{{T "monitoring.terheles_load"}}</span>
<span class="sysinfo-value" id="host-load">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Memória</span>
<span class="sysinfo-label">{{T "monitoring.memoria"}}</span>
<span class="sysinfo-value" id="host-mem">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">CPU hőmérséklet</span>
<span class="sysinfo-label">{{T "monitoring.cpu_homerseklet"}}</span>
<span class="sysinfo-value" id="host-temp">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Üzemidő</span>
<span class="sysinfo-label">{{T "monitoring.uzemido"}}</span>
<span class="sysinfo-value" id="host-uptime">–</span>
</div>
</div>
<h4 style="margin:1rem 0 .5rem">Tárolók kapacitása</h4>
<h4 style="margin:1rem 0 .5rem">{{T "monitoring.tarolok_kapacitasa"}}</h4>
<div class="storage-bars" id="host-storage-bars"></div>
</div>
</div>
<!-- Section 1: System Overview -->
<div class="monitor-card">
<h3>Rendszer áttekintés</h3>
<h3>{{T "monitoring.rendszer_attekintes"}}</h3>
<div class="sysinfo-grid">
<div class="sysinfo-row">
<span class="sysinfo-label">Gépnév</span>
<span class="sysinfo-label">{{T "monitoring.gepnev"}}</span>
<span class="sysinfo-value" id="sysinfo-hostname">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Operációs rendszer</span>
<span class="sysinfo-label">{{T "monitoring.operacios_rendszer"}}</span>
<span class="sysinfo-value" id="sysinfo-os">–</span>
</div>
<div class="sysinfo-row">
@@ -60,15 +60,15 @@
<span class="sysinfo-value" id="sysinfo-kernel">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Processzor</span>
<span class="sysinfo-label">{{T "monitoring.processzor"}}</span>
<span class="sysinfo-value" id="sysinfo-cpu">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Üzemidő</span>
<span class="sysinfo-label">{{T "monitoring.uzemido"}}</span>
<span class="sysinfo-value" id="sysinfo-uptime">–</span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Indítás</span>
<span class="sysinfo-label">{{T "monitoring.inditas"}}</span>
<span class="sysinfo-value" id="sysinfo-boot">–</span>
</div>
</div>
@@ -76,12 +76,12 @@
<!-- Section 1.5: Storage (moved here for visibility) -->
<div class="monitor-card">
<h3>Tárhely</h3>
<h3>{{T "monitoring.tarhely"}}</h3>
<div class="storage-bars">
{{with .SystemInfo}}
<div class="storage-item meter {{usageColor .DiskPercent}}">
<div class="storage-header">
<span class="storage-label">Rendszer (/)</span>
<span class="storage-label">{{T "common.rendszer_gyoker"}}</span>
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
</div>
<div class="meter-track">
@@ -93,7 +93,7 @@
<div class="storage-item meter meter-empty storage-disconnected">
<div class="storage-header">
<span class="storage-label">{{.Label}}</span>
<span class="storage-value">— <span class="state-text-neutral">Leválasztva</span></span>
<span class="storage-value">— <span class="state-text-neutral">{{T "common.levalasztva"}}</span></span>
</div>
<div class="meter-track"></div>
</div>
@@ -127,15 +127,15 @@
<!-- Section 2: Hub Connection Status -->
<div class="monitor-card">
<h3>Hub kapcsolat</h3>
<h3>{{T "monitoring.hub_kapcsolat"}}</h3>
{{if .HubEnabled}}
{{if .HubConnected}}
<div class="monitoring-banner monitoring-banner-green">
Kapcsolódva — a központi rendszer aktívan figyeli a szervert.
{{T "monitoring.kapcsolodva_a_kozponti_rendszer_aktivan"}}
</div>
{{else}}
<div class="monitoring-banner monitoring-banner-red">
Nem elérhető — a központi rendszer nem kapott friss jelentést.
{{T "monitoring.nem_elerheto_a_kozponti_rendszer"}}
</div>
{{end}}
<div class="sysinfo-grid" style="margin-top: 0.75rem">
@@ -144,25 +144,25 @@
<span class="sysinfo-value"><code>{{.HubURL}}</code></span>
</div>
<div class="sysinfo-row">
<span class="sysinfo-label">Ügyfél azonosító</span>
<span class="sysinfo-label">{{T "common.ugyfel_azonosito"}}</span>
<span class="sysinfo-value"><code>{{.CustomerID}}</code></span>
</div>
{{if not .HubLastSuccess.IsZero}}
<div class="sysinfo-row">
<span class="sysinfo-label">Utolsó sikeres jelentés</span>
<span class="sysinfo-label">{{T "monitoring.utolso_sikeres_jelentes"}}</span>
<span class="sysinfo-value">{{.HubLastSuccess | timeAgo}}</span>
</div>
{{end}}
{{if .HubLastError}}
<div class="sysinfo-row">
<span class="sysinfo-label">Utolsó hiba</span>
<span class="sysinfo-label">{{T "monitoring.utolso_hiba"}}</span>
<span class="sysinfo-value"><span class="text-error">{{.HubLastError}}</span></span>
</div>
{{end}}
</div>
{{else}}
<div class="monitoring-banner monitoring-banner-yellow">
A Hub kapcsolat nincs bekapcsolva — a központi monitoring nem aktív.
{{T "monitoring.a_hub_kapcsolat_nincs_bekapcsolva"}}
</div>
{{end}}
</div>
@@ -170,41 +170,41 @@
<!-- Section 3: System Metrics Charts -->
<div class="monitor-card">
<div class="monitor-card-header">
<h3>Rendszer metrikák</h3>
<h3>{{T "monitoring.rendszer_metrikak"}}</h3>
<div class="time-range-bar" id="system-range-bar">
<button class="filter-btn active" data-range="1h">1 óra</button>
<button class="filter-btn" data-range="6h">6 óra</button>
<button class="filter-btn" data-range="24h">24 óra</button>
<button class="filter-btn" data-range="7d">7 nap</button>
<button class="filter-btn" data-range="30d">30 nap</button>
<button class="filter-btn active" data-range="1h">{{T "monitoring.1_ora"}}</button>
<button class="filter-btn" data-range="6h">{{T "monitoring.6_ora"}}</button>
<button class="filter-btn" data-range="24h">{{T "monitoring.24_ora"}}</button>
<button class="filter-btn" data-range="7d">{{T "monitoring.7_nap"}}</button>
<button class="filter-btn" data-range="30d">{{T "monitoring.30_nap"}}</button>
</div>
</div>
<div class="charts-grid" id="system-charts">
<div class="chart-box">
<div class="chart-title">CPU használat (%)</div>
<div class="chart-title">{{T "monitoring.cpu_hasznalat_2"}}</div>
<div class="chart-wrap"><canvas id="chart-cpu"></canvas></div>
</div>
<div class="chart-box">
<div class="chart-title">Memória használat (GB)</div>
<div class="chart-title">{{T "monitoring.memoria_hasznalat_gb"}}</div>
<div class="chart-wrap"><canvas id="chart-memory"></canvas></div>
</div>
<div class="chart-box">
<div class="chart-title">Hőmérséklet (°C)</div>
<div class="chart-title">{{T "monitoring.homerseklet_c"}}</div>
<div class="chart-wrap"><canvas id="chart-temp"></canvas></div>
</div>
<div class="chart-box">
<div class="chart-title">Terhelés (Load Average)</div>
<div class="chart-title">{{T "monitoring.terheles_load_average"}}</div>
<div class="chart-wrap"><canvas id="chart-load"></canvas></div>
</div>
</div>
<div class="chart-empty" id="system-charts-empty" style="display:none">
Még nincsenek adatok. A metrikák gyűjtése elindult, az első adatok néhány perc múlva megjelennek.
{{T "monitoring.meg_nincsenek_adatok_a_metrikak"}}
</div>
</div>
<!-- Section 3.5: Memory Distribution Bar -->
<div class="monitor-card" id="memory-distribution-card" style="display:none">
<h3>Memória eloszlás</h3>
<h3>{{T "monitoring.memoria_eloszlas"}}</h3>
<div id="mem-dist-header" class="memory-dist-header"></div>
<div class="memory-dist-bar" id="mem-dist-bar"></div>
<div class="memory-bar-legend" id="mem-dist-legend"></div>
@@ -212,19 +212,19 @@
<!-- Section 4: Container Resources -->
<div class="monitor-card">
<h3>Alkalmazás erőforrások</h3>
<h3>{{T "monitoring.alkalmazas_eroforrasok"}}</h3>
<div class="container-charts-row" id="container-charts">
<div class="chart-box chart-box-half">
<div class="chart-title">CPU használat (%)</div>
<div class="chart-title">{{T "monitoring.cpu_hasznalat_2"}}</div>
<div class="chart-wrap chart-wrap-bar"><canvas id="chart-container-cpu"></canvas></div>
</div>
<div class="chart-box chart-box-half">
<div class="chart-title">Memória használat (MB)</div>
<div class="chart-title">{{T "monitoring.memoria_hasznalat_mb"}}</div>
<div class="chart-wrap chart-wrap-bar"><canvas id="chart-container-mem"></canvas></div>
</div>
</div>
<div class="chart-empty" id="container-charts-empty" style="display:none">
Még nincsenek konténer adatok.
{{T "monitoring.meg_nincsenek_kontener_adatok"}}
</div>
</div>
@@ -233,12 +233,12 @@
<div class="monitor-card-header">
<h3 id="container-detail-title">–</h3>
<div class="time-range-bar" id="container-range-bar">
<button class="filter-btn active" data-range="1h">1 óra</button>
<button class="filter-btn" data-range="6h">6 óra</button>
<button class="filter-btn" data-range="24h">24 óra</button>
<button class="filter-btn" data-range="7d">7 nap</button>
<button class="filter-btn active" data-range="1h">{{T "monitoring.1_ora"}}</button>
<button class="filter-btn" data-range="6h">{{T "monitoring.6_ora"}}</button>
<button class="filter-btn" data-range="24h">{{T "monitoring.24_ora"}}</button>
<button class="filter-btn" data-range="7d">{{T "monitoring.7_nap"}}</button>
</div>
<button class="btn btn-outline btn-sm" onclick="closeContainerDetail()">Bezárás</button>
<button class="btn btn-outline btn-sm" onclick="closeContainerDetail()">{{T "common.bezaras"}}</button>
</div>
<div class="charts-grid charts-grid-2">
<div class="chart-box">
@@ -246,7 +246,7 @@
<div class="chart-wrap"><canvas id="chart-detail-cpu"></canvas></div>
</div>
<div class="chart-box">
<div class="chart-title">Memória (MB)</div>
<div class="chart-title">{{T "monitoring.memoria_mb"}}</div>
<div class="chart-wrap"><canvas id="chart-detail-mem"></canvas></div>
</div>
</div>
@@ -588,7 +588,7 @@
function fmtMB(mb) { return mb >= 1024 ? (mb/1024).toFixed(1) + ' GB' : Math.round(mb) + ' MB'; }
header.textContent = 'Használt: ' + fmtMB(usedMB) + ' / ' + fmtMB(totalMB) + ' (' + Math.round(usedMB/totalMB*100) + '%)';
header.textContent = '{{T "monitoring.hasznalt"}} ' + fmtMB(usedMB) + ' / ' + fmtMB(totalMB) + ' (' + Math.round(usedMB/totalMB*100) + '%)';
// Sort containers by memory usage descending
containers.sort(function(a, b) { return (b.mem_usage_mb || 0) - (a.mem_usage_mb || 0); });
@@ -608,12 +608,12 @@
// OS / system overhead
if (osMB > 10) {
var osPct = (osMB / totalMB * 100).toFixed(2);
html += '<div class="memory-bar-segment" style="width:' + osPct + '%;background:var(--text-3);opacity:0.5" title="Rendszer: ' + fmtMB(osMB) + '"></div>';
legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--text-3);opacity:0.5"></span>Rendszer (' + fmtMB(osMB) + ')</div>';
html += '<div class="memory-bar-segment" style="width:' + osPct + '%;background:var(--text-3);opacity:0.5" title="{{T "monitoring.rendszer_title"}} ' + fmtMB(osMB) + '"></div>';
legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--text-3);opacity:0.5"></span>{{T "monitoring.rendszer_2"}}' + fmtMB(osMB) + ')</div>';
}
// Free space legend only
legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--bg-2);border:1px solid var(--line)"></span>Szabad (' + fmtMB(freeMB) + ')</div>';
legendHtml += '<div class="memory-legend-item"><span class="memory-legend-dot" style="background:var(--bg-2);border:1px solid var(--line)"></span>{{T "monitoring.szabad"}}' + fmtMB(freeMB) + ')</div>';
bar.innerHTML = html;
legend.innerHTML = legendHtml;
@@ -641,7 +641,7 @@
window.showContainerDetail = async function(name) {
detailContainer = name;
document.getElementById('container-detail-title').textContent = name + ' — Erőforrás előzmények';
document.getElementById('container-detail-title').textContent = name + ' {{T "monitoring.eroforras_elozmenyek"}}';
document.getElementById('container-detail-panel').style.display = '';
document.getElementById('container-detail-panel').scrollIntoView({behavior: 'smooth'});
await loadContainerDetail();
@@ -745,7 +745,7 @@
var bars = document.getElementById('host-storage-bars');
var targets = Array.isArray(d.storage_targets) ? d.storage_targets : [];
if (!targets.length) {
bars.innerHTML = '<div class="sysinfo-value" style="color:var(--text-3)">Nincs tároló adat.</div>';
bars.innerHTML = '<div class="sysinfo-value" style="color:var(--text-3)">{{T "monitoring.nincs_tarolo_adat"}}</div>';
} else {
var html = '';
targets.forEach(function(t) {
@@ -758,7 +758,7 @@
if (t.state && t.state !== 'attached') {
html += '<div class="storage-item meter meter-empty storage-disconnected">' +
'<div class="storage-header"><span class="storage-label">' + label + '</span>' +
'<span class="storage-value">— <span class="state-text-neutral">Nem elérhető</span></span></div>' +
'<span class="storage-value">— <span class="state-text-neutral">{{T "monitoring.nem_elerheto"}}</span></span></div>' +
'<div class="meter-track"></div>' + purposeHtml + '</div>';
return;
}
@@ -770,7 +770,7 @@
}
if (t.smart) {
if (t.smart.temperature_c != null) extra.push(t.smart.temperature_c + ' °C');
if (t.smart.percentage_used != null) extra.push('kopás: ' + t.smart.percentage_used + '%');
if (t.smart.percentage_used != null) extra.push('{{T "monitoring.kopas"}} ' + t.smart.percentage_used + '%');
}
var extraHtml = extra.length ? ' <span style="color:var(--text-3);font-size:.75rem">· ' + escapeHtml(extra.join(' · ')) + '</span>' : '';
html += '<div class="storage-item meter ' + usageColorClass(pct) + '">' +
@@ -786,7 +786,7 @@
var now = new Date();
document.getElementById('host-health-updated').textContent =
'Frissítve: ' + now.toLocaleTimeString('hu-HU', {timeZone: budaTZ, hour: '2-digit', minute: '2-digit', second: '2-digit'});
'{{T "monitoring.frissitve"}} ' + now.toLocaleTimeString('hu-HU', {timeZone: budaTZ, hour: '2-digit', minute: '2-digit', second: '2-digit'});
}
async function loadHostMetrics() {
@@ -835,9 +835,9 @@
var days = Math.floor(seconds / 86400);
var hours = Math.floor((seconds % 86400) / 3600);
var minutes = Math.floor((seconds % 3600) / 60);
if (days > 0) return days + ' nap, ' + hours + ' óra';
if (hours > 0) return hours + ' óra, ' + minutes + ' perc';
return minutes + ' perc';
if (days > 0) return days + ' {{T "monitoring.nap"}} ' + hours + ' {{T "monitoring.ora"}}';
if (hours > 0) return hours + ' {{T "monitoring.ora_2"}} ' + minutes + ' {{T "monitoring.perc"}}';
return minutes + ' {{T "monitoring.perc"}}';
}
// =============================================
@@ -2,110 +2,110 @@
{{template "layout_start" .}}
<div class="page-header">
<h2>Beállítások — Értesítések</h2>
<h2>{{T "settings_notifications.beallitasok_ertesitesek"}}</h2>
</div>
<!-- Section C: Notification Preferences -->
<div class="settings-card">
<h3>Értesítések</h3>
<h3>{{T "settings_notifications.ertesitesek"}}</h3>
{{if .HubEnabled}}
{{if .NotificationSuccess}}<div class="alert alert-info">{{.NotificationSuccess}}</div>{{end}}
{{if .NotificationError}}<div class="alert alert-error">{{.NotificationError}}</div>{{end}}
<form method="POST" action="/settings/notifications">
{{.CSRFField}}
<div class="form-group">
<label for="notification_email">E-mail cím</label>
<label for="notification_email">{{T "settings_notifications.e_mail_cim"}}</label>
<input type="email" id="notification_email" name="notification_email"
value="{{with .NotificationPrefs}}{{.Email}}{{end}}"
placeholder="pelda@email.hu" class="form-control">
placeholder="{{T "settings_notifications.pelda_email"}}" class="form-control">
</div>
<div class="form-group">
<label>Hibák és figyelmeztetések:</label>
<label>{{T "settings_notifications.hibak_es_figyelmeztetesek"}}</label>
<div class="checkbox-group">
<label class="toggle">
<input type="checkbox" name="event_backup_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "backup_failed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Biztonsági mentés sikertelen</span>
<span class="toggle-label">{{T "settings_notifications.biztonsagi_mentes_sikertelen"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_db_dump_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "db_dump_failed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Adatbázis mentés sikertelen</span>
<span class="toggle-label">{{T "settings_notifications.adatbazis_mentes_sikertelen"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_backup_integrity_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "backup_integrity_failed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Mentés sérülés észlelve</span>
<span class="toggle-label">{{T "settings_notifications.mentes_serules_eszlelve"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_crossdrive_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "crossdrive_failed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Másodlagos mentés sikertelen</span>
<span class="toggle-label">{{T "settings_notifications.masodlagos_mentes_sikertelen"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_offbox_enlarge_blocked" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "offbox_enlarge_blocked"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Távoli mentés — tárhelykeret-figyelmeztetés</span>
<span class="toggle-label">{{T "settings_notifications.tavoli_mentes_tarhelykeret_figyelmeztete"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_disk_warning" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "disk_warning"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Lemez betelőben (90% felett)</span>
<span class="toggle-label">{{T "settings_notifications.lemez_beteloben_90_felett"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_disk_critical" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "disk_critical"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Lemez megtelt vagy meghibásodott</span>
<span class="toggle-label">{{T "settings_notifications.lemez_megtelt_vagy_meghibasodott"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_storage_disconnected" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "storage_disconnected"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Meghajtó leválasztva</span>
<span class="toggle-label">{{T "settings_notifications.meghajto_levalasztva"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_node_down" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "node_down"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Szerver nem elérhető</span>
<span class="toggle-label">{{T "settings_notifications.szerver_nem_elerheto"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_health_critical" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "health_critical"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Rendszer állapot kritikus</span>
<span class="toggle-label">{{T "settings_notifications.rendszer_allapot_kritikus"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_expected_backup_missed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "expected_backup_missed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Elvárt fájlmentés elmaradt</span>
<span class="toggle-label">{{T "settings_notifications.elvart_fajlmentes_elmaradt"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_expected_dbdump_missed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "expected_dbdump_missed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Elvárt adatbázismentés elmaradt</span>
<span class="toggle-label">{{T "settings_notifications.elvart_adatbazismentes_elmaradt"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_app_start_failed" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "app_start_failed"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Alkalmazás nem fut</span>
<span class="toggle-label">{{T "settings_notifications.alkalmazas_nem_fut"}}</span>
</label>
</div>
</div>
<div class="form-group">
<label>Tájékoztató:</label>
<label>{{T "settings_notifications.tajekoztato"}}</label>
<div class="checkbox-group">
<label class="toggle">
<input type="checkbox" name="event_storage_reconnected" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "storage_reconnected"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Meghajtó újra csatlakoztatva</span>
<span class="toggle-label">{{T "settings_notifications.meghajto_ujra_csatlakoztatva"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_health_recovered" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "health_recovered"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">Rendszer állapot helyreállt</span>
<span class="toggle-label">{{T "settings_notifications.rendszer_allapot_helyreallt"}}</span>
</label>
</div>
</div>
<div class="form-group">
<label for="cooldown_hours">Értesítési szünet</label>
<label for="cooldown_hours">{{T "settings_notifications.ertesitesi_szunet"}}</label>
<div class="form-inline">
<input type="number" id="cooldown_hours" name="cooldown_hours" min="1" max="168"
value="{{with .NotificationPrefs}}{{.CooldownHours}}{{end}}"
class="form-control form-control-narrow">
<span class="form-hint">óra (azonos probléma esetén ennyi ideig nem küld újat)</span>
<span class="form-hint">{{T "settings_notifications.ora_azonos_problema_eseten_ennyi"}}</span>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Mentés</button>
<button type="submit" formaction="/settings/notifications/test" class="btn btn-outline">Teszt email küldése</button>
<button type="submit" class="btn btn-primary">{{T "settings_notifications.mentes"}}</button>
<button type="submit" formaction="/settings/notifications/test" class="btn btn-outline">{{T "settings_notifications.teszt_email_kuldese"}}</button>
</div>
</form>
{{else}}
<div class="alert alert-info">
Az értesítések a központi rendszeren keresztül működnek, ami jelenleg nincs bekapcsolva.
{{T "settings_notifications.az_ertesitesek_a_kozponti_rendszeren"}}
</div>
{{end}}
</div>
@@ -113,11 +113,9 @@
<!-- Section: App email (SMTP relay) -->
{{if .AppEmailAvailable}}
<div class="settings-card">
<h3>Alkalmazás-email</h3>
<h3>{{T "settings_notifications.alkalmazas_email"}}</h3>
<p class="settings-card-desc">
Az alkalmazások a Felhom-on keresztül küldhetnek emailt (pl. jelszó-visszaállítás, meghívók),
külön email-szolgáltató beállítása nélkül. A feladó címe minden alkalmazásnál a saját
<em>&lt;alkalmazás&gt;@felhom.eu</em> címe lesz.
{{T "settings_notifications.az_alkalmazasok_a_felhom_on"}}
</p>
{{if .AppEmailSuccess}}<div class="alert alert-info">{{.AppEmailSuccess}}</div>{{end}}
{{if .AppEmailError}}<div class="alert alert-error">{{.AppEmailError}}</div>{{end}}
@@ -126,21 +124,21 @@
<div class="form-group">
<label style="display:flex;align-items:center;gap:.5rem">
<input type="checkbox" name="app_email_enabled" value="on" {{if .AppEmailEnabled}}checked{{end}}>
Alkalmazás-email engedélyezése
{{T "settings_notifications.alkalmazas_email_engedelyezese"}}
</label>
</div>
<div class="form-group">
<label for="app_email_from_name">Feladó neve (opcionális)</label>
<label for="app_email_from_name">{{T "settings_notifications.felado_neve_opcionalis"}}</label>
<input type="text" id="app_email_from_name" name="app_email_from_name"
value="{{.AppEmailFromName}}" placeholder="pl. a háztartás neve" class="form-control">
<span class="form-hint">Ez jelenik meg a kimenő emailek feladójaként az alkalmazás neve mellett.</span>
value="{{.AppEmailFromName}}" placeholder="{{T "settings_notifications.pl_a_haztartas_neve"}}" class="form-control">
<span class="form-hint">{{T "settings_notifications.ez_jelenik_meg_a_kimeno"}}</span>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Mentés</button>
<button type="submit" class="btn btn-primary">{{T "settings_notifications.mentes"}}</button>
</div>
</form>
<p class="settings-card-desc" style="margin-top:.75rem">
Bekapcsolás után az egyes alkalmazásoknál is engedélyezni kell az email-küldést (az alkalmazás oldalán).
{{T "settings_notifications.bekapcsolas_utan_az_egyes_alkalmazasokna"}}
</p>
</div>
{{end}}
@@ -2,52 +2,51 @@
{{template "layout_start" .}}
<div class="page-header">
<h2>Beállítások — Biztonság és hozzáférés</h2>
<h2>{{T "settings_security.beallitasok_biztonsag_es_hozzaferes"}}</h2>
</div>
<!-- Section B: Password Change -->
<div class="settings-card">
<h3>Jelszó módosítás</h3>
<h3>{{T "settings_security.jelszo_modositas"}}</h3>
{{if .AuthEnabled}}
{{if .PasswordError}}<div class="alert alert-error">{{.PasswordError}}</div>{{end}}
<form method="POST" action="/settings/password">
{{.CSRFField}}
<div class="form-group">
<label for="current_password">Jelenlegi jelszó</label>
<label for="current_password">{{T "settings_security.jelenlegi_jelszo"}}</label>
<input type="password" id="current_password" name="current_password" required
placeholder="Adja meg a jelenlegi jelszavát" class="form-control">
placeholder="{{T "settings_security.adja_meg_a_jelenlegi_jelszavat"}}" class="form-control">
</div>
<div class="form-group">
<label for="new_password">Új jelszó</label>
<label for="new_password">{{T "settings_security.uj_jelszo"}}</label>
<input type="password" id="new_password" name="new_password" required minlength="8"
placeholder="Legalább 8 karakter" class="form-control">
placeholder="{{T "settings_security.legalabb_8_karakter"}}" class="form-control">
</div>
<div class="form-group">
<label for="confirm_password">Új jelszó megerősítése</label>
<label for="confirm_password">{{T "settings_security.uj_jelszo_megerositese"}}</label>
<input type="password" id="confirm_password" name="confirm_password" required minlength="8"
placeholder="Jelszó mégegyszer" class="form-control">
placeholder="{{T "settings_security.jelszo_megegyszer"}}" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Jelszó módosítása</button>
<button type="submit" class="btn btn-primary">{{T "settings_security.jelszo_modositasa"}}</button>
</form>
{{else}}
<div class="alert alert-info">
A jelszavas védelem nincs beállítva. Kérd az üzemeltetőt a beállításhoz.
{{T "settings_security.a_jelszavas_vedelem_nincs_beallitva"}}
</div>
{{end}}
</div>
<!-- Section: Geo-Restriction -->
<div class="settings-card">
<h3>Földrajzi korlátozás</h3>
<h3>{{T "settings_security.foldrajzi_korlatozas"}}</h3>
<p class="settings-card-desc">
Ország alapján korlátozható a webes alkalmazások elérése a Cloudflare WAF segítségével.
<br><span class="form-hint">A helyi hálózati hozzáférés mindig engedélyezett (nem halad át a Cloudflare-en).</span>
{{T "settings_security.orszag_alapjan_korlatozhato_a_webes"}}
<br><span class="form-hint">{{T "settings_security.a_helyi_halozati_hozzaferes_mindig"}}</span>
</p>
{{if not .CFConfigured}}
<div class="alert alert-info">
A Cloudflare API token nincs konfigurálva. Kérd az üzemeltetőt a beállításhoz.<br>
<small>A tokennek <strong>Zone WAF:Edit</strong> jogosultsággal kell rendelkeznie.</small>
{{T "settings_security.a_cloudflare_api_token_nincs"}}
</div>
{{else}}
<div id="geo-status-msg"></div>
@@ -55,16 +54,16 @@
<label class="toggle" style="margin-bottom:1rem">
<input type="checkbox" id="geo-enabled" {{if .GeoEnabled}}checked{{end}}
onchange="toggleGeo(this.checked)">
<span class="toggle-label">Geo-korlátozás aktív</span>
<span class="toggle-label">{{T "settings_security.geo_korlatozas_aktiv"}}</span>
</label>
<div id="geo-details" {{if not .GeoEnabled}}style="display:none"{{end}}>
<!-- Global allowed countries -->
<div class="form-group">
<label>Engedélyezett országok (globális)</label>
<label>{{T "settings_security.engedelyezett_orszagok_globalis"}}</label>
<div class="geo-country-selector" id="geo-countries">
<input type="text" id="geo-search" class="form-control"
placeholder="Ország keresése..."
placeholder="{{T "settings_security.orszag_keresese"}}"
autocomplete="off"
oninput="filterCountries(this.value)"
onfocus="showCountryList()"
@@ -72,22 +71,22 @@
<div class="geo-country-list" id="geo-country-list"></div>
</div>
<div class="geo-selected-tags" id="geo-selected-tags"></div>
<span class="form-hint">Csak a kiválasztott országokból érhető el a rendszer.</span>
<span class="form-hint">{{T "settings_security.csak_a_kivalasztott_orszagokbol_erheto"}}</span>
</div>
<!-- Per-app overrides -->
<div class="form-group" style="margin-top:1.5rem">
<label>Alkalmazás-specifikus felülírások</label>
<label>{{T "settings_security.alkalmazas_specifikus_felulirasok"}}</label>
<div id="geo-app-overrides"></div>
{{if .DeployedApps}}
<div style="margin-top:.5rem;display:flex;align-items:center;gap:.5rem">
<select id="geo-add-app-select" class="form-control" style="max-width:250px">
<option value="">— Alkalmazás kiválasztása —</option>
<option value="">{{T "settings_security.alkalmazas_kivalasztasa"}}</option>
{{range .DeployedApps}}
<option value="{{.Name}}">{{.Display}}</option>
{{end}}
</select>
<button class="btn btn-sm btn-outline" onclick="addAppOverride()">+ Hozzáadás</button>
<button class="btn btn-sm btn-outline" onclick="addAppOverride()">{{T "settings_security.hozzaadas"}}</button>
</div>
{{end}}
</div>
@@ -96,11 +95,11 @@
<div class="form-group" style="margin-top:1.5rem">
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
<button class="btn btn-primary" id="btn-geo-save" onclick="saveGeoSettings()">
Mentés és szinkronizálás
{{T "settings_security.mentes_es_szinkronizalas"}}
</button>
<button class="btn btn-sm btn-outline" onclick="triggerGeoSync()">Kézi szinkronizálás</button>
<button class="btn btn-sm btn-outline" onclick="triggerGeoSync()">{{T "settings_security.kezi_szinkronizalas"}}</button>
<span id="geo-sync-status" class="form-hint">
{{if .GeoLastSync}}Utolsó szinkronizálás: {{.GeoLastSync}}{{end}}
{{if .GeoLastSync}}{{T "settings_security.utolso_szinkronizalas"}}{{end}}
{{if .GeoLastError}} <span class="state-text-crit">{{.GeoLastError}}</span>{{end}}
</span>
</div>
@@ -118,10 +117,10 @@ function openDialog(opts){
var root=document.getElementById('dialog-root');
if(!root) return;
root.innerHTML='<div class="confirm-overlay" onclick="if(event.target===this)document.getElementById(\'dialog-cancel\').click()"><div class="confirm-box">'
+'<h3>'+escText(opts.title||'Megerősítés')+'</h3>'
+'<h3>'+escText(opts.title||'{{T "settings_security.megerosites"}}')+'</h3>'
+'<p style="white-space:pre-line">'+escText(opts.message||'')+'</p>'
+'<div class="form-actions"><button id="dialog-go" class="btn btn-primary">'+escText(opts.confirmLabel||'Megerősítés')+'</button>'
+'<button type="button" class="btn btn-outline" id="dialog-cancel">Mégsem</button></div>'
+'<div class="form-actions"><button id="dialog-go" class="btn btn-primary">'+escText(opts.confirmLabel||'{{T "settings_security.megerosites"}}')+'</button>'
+'<button type="button" class="btn btn-outline" id="dialog-cancel">{{T "common.megsem"}}</button></div>'
+'</div></div>';
document.getElementById('dialog-go').onclick=function(){ closeDialog(); if(opts.onConfirm) opts.onConfirm(); };
document.getElementById('dialog-cancel').onclick=function(){ closeDialog(); if(opts.onCancel) opts.onCancel(); };
@@ -171,7 +170,7 @@ function openDialog(opts){
+ escHtml(c.name) + ' <small>(' + c.code + ')</small></div>';
count++;
}
list.innerHTML = html || '<div class="geo-country-option" style="opacity:.5">Nincs találat</div>';
list.innerHTML = html || '<div class="geo-country-option" style="opacity:.5">{{T "settings_security.nincs_talalat"}}</div>';
// Reveal with 'block', NOT '' — the .geo-country-list CSS default is display:none,
// and clearing the inline style ('') would fall back to that and keep the (populated)
// list hidden. This was the country-autocomplete "no list" bug.
@@ -188,8 +187,8 @@ function openDialog(opts){
window.removeCountry = function(code) {
if (code === 'HU') {
openDialog({title:'Figyelem', confirmLabel:'Eltávolítás',
message:'Figyelem: Magyarország eltávolítása azt jelenti, hogy magyar IP-ről sem lesz elérhető a rendszer távolról. Biztosan folytatja?',
openDialog({title:'{{T "settings_security.figyelem"}}', confirmLabel:'{{T "common.eltavolitas"}}',
message:'{{T "settings_security.figyelem_magyarorszag_eltavolitasa_azt_j"}}',
onConfirm:function(){ doRemoveCountry(code); }});
return;
}
@@ -247,8 +246,8 @@ function openDialog(opts){
var idx = ov.allowed_countries.indexOf(code);
if (idx >= 0) {
if (code === 'HU') {
openDialog({title:'Figyelem', confirmLabel:'Eltávolítás',
message:'Magyarország eltávolítása nem ajánlott. Folytatja?',
openDialog({title:'{{T "settings_security.figyelem"}}', confirmLabel:'{{T "common.eltavolitas"}}',
message:'{{T "settings_security.magyarorszag_eltavolitasa_nem_ajanlott_f"}}',
onConfirm:function(){ ov.allowed_countries.splice(ov.allowed_countries.indexOf(code), 1); },
onCancel:function(){ el.checked = true; }});
return;
@@ -262,7 +261,7 @@ function openDialog(opts){
function renderAppOverrides() {
var el = document.getElementById('geo-app-overrides');
if (!appOverrides || Object.keys(appOverrides).length === 0) {
el.innerHTML = '<p class="form-hint">Nincs alkalmazás-specifikus beállítás. Minden alkalmazás a globális beállítást követi.</p>';
el.innerHTML = '<p class="form-hint">{{T "settings_security.nincs_alkalmazas_specifikus_beallitas_mi"}}</p>';
return;
}
var html = '';
@@ -284,8 +283,8 @@ function openDialog(opts){
html += '<span class="geo-tag geo-tag-sm">' + code + '</span>';
}
html += '</div>';
html += '<button class="btn btn-sm btn-outline" onclick="editAppOverride(\'' + appName + '\')">Szerkesztés</button>';
html += '<button class="btn btn-sm btn-danger-outline" onclick="removeAppOverride(\'' + appName + '\')">Törlés</button>';
html += '<button class="btn btn-sm btn-outline" onclick="editAppOverride(\'' + appName + '\')">{{T "settings_security.szerkesztes"}}</button>';
html += '<button class="btn btn-sm btn-danger-outline" onclick="removeAppOverride(\'' + appName + '\')">{{T "common.torles"}}</button>';
html += '</div>';
}
el.innerHTML = html;
@@ -298,7 +297,7 @@ function openDialog(opts){
var checked = {};
for (var i = 0; i < ov.allowed_countries.length; i++) checked[ov.allowed_countries[i]] = true;
var html = '<div class="geo-edit-overlay" id="geo-edit-' + appName + '">';
html += '<h4>Engedélyezett országok: ' + escHtml(appName) + '</h4>';
html += '<h4>{{T "settings_security.engedelyezett_orszagok"}} ' + escHtml(appName) + '</h4>';
html += '<div class="geo-edit-grid">';
for (var i = 0; i < allCountries.length; i++) {
var c = allCountries[i];
@@ -307,7 +306,7 @@ function openDialog(opts){
+ ' ' + escHtml(c.name) + ' (' + c.code + ')</label>';
}
html += '</div>';
html += '<button class="btn btn-sm btn-primary" style="margin-top:.5rem" onclick="closeAppEdit(\'' + appName + '\')">Kész</button>';
html += '<button class="btn btn-sm btn-primary" style="margin-top:.5rem" onclick="closeAppEdit(\'' + appName + '\')">{{T "settings_security.kesz"}}</button>';
html += '</div>';
document.getElementById('geo-app-overrides').innerHTML += html;
});
@@ -324,7 +323,7 @@ function openDialog(opts){
var btn = document.getElementById('btn-geo-save');
var status = document.getElementById('geo-status-msg');
btn.disabled = true;
btn.textContent = 'Mentés...';
btn.textContent = '{{T "settings_security.mentes"}}';
var payload = {
enabled: document.getElementById('geo-enabled').checked,
@@ -339,21 +338,21 @@ function openDialog(opts){
.then(function(r){return r.json()})
.then(function(d){
if (d.ok) {
status.innerHTML = '<div class="alert alert-info">' + (d.message || 'Mentve') + '</div>';
status.innerHTML = '<div class="alert alert-info">' + (d.message || '{{T "settings_security.mentve"}}') + '</div>';
// Save per-app overrides
if (appOverrides && Object.keys(appOverrides).length > 0) {
saveAllAppOverrides();
}
} else {
status.innerHTML = '<div class="alert alert-error">' + (d.error || 'Hiba') + '</div>';
status.innerHTML = '<div class="alert alert-error">' + (d.error || '{{T "common.hiba"}}') + '</div>';
}
})
.catch(function(err){
status.innerHTML = '<div class="alert alert-error">Hálózati hiba</div>';
status.innerHTML = '<div class="alert alert-error">{{T "settings_security.halozati_hiba"}}</div>';
})
.finally(function(){
btn.disabled = false;
btn.textContent = 'Mentés és szinkronizálás';
btn.textContent = '{{T "settings_security.mentes_es_szinkronizalas"}}';
setTimeout(function(){ status.innerHTML = ''; }, 8000);
});
};
@@ -375,7 +374,7 @@ function openDialog(opts){
.then(function(r){return r.json()})
.then(function(d){
var status = document.getElementById('geo-sync-status');
status.textContent = d.ok ? 'Szinkronizálás elindítva...' : (d.error || 'Hiba');
status.textContent = d.ok ? '{{T "settings_security.szinkronizalas_elinditva"}}' : (d.error || '{{T "common.hiba"}}');
setTimeout(function(){
fetch('/api/geo/status', {headers: csrfHeaders()})
.then(function(r){return r.json()})
@@ -383,7 +382,7 @@ function openDialog(opts){
if (d.ok && d.data) {
var sync = d.data.last_sync || '';
var err = d.data.last_sync_error || '';
status.innerHTML = sync ? ('Utolsó: ' + sync.substring(0,19).replace('T',' ')) : '';
status.innerHTML = sync ? ('{{T "settings_security.utolso"}} ' + sync.substring(0,19).replace('T',' ')) : '';
if (err) status.innerHTML += ' <span class="state-text-crit">' + escHtml(err) + '</span>';
}
});
@@ -407,13 +406,13 @@ function openDialog(opts){
<!-- Section: Recovery Info -->
{{if .HasRetrievalPassword}}
<div class="settings-card">
<h3>Vészhelyzeti információk</h3>
<h3>{{T "settings_security.veszhelyzeti_informaciok"}}</h3>
<p class="settings-card-desc">
Ezeket az adatokat mentse el biztos helyre. Újratelepítés esetén szükség lesz rájuk a rendszer visszaállításához.
{{T "settings_security.ezeket_az_adatokat_mentse_el"}}
</p>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Ügyfél azonosító</span>
<span class="settings-label">{{T "common.ugyfel_azonosito"}}</span>
<span class="settings-value mono">{{.CustomerID}}</span>
</div>
<div class="settings-row">
@@ -421,22 +420,20 @@ function openDialog(opts){
<span class="settings-value mono">{{.HubURL}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Visszaállítási jelszó</span>
<span class="settings-label">{{T "settings_security.visszaallitasi_jelszo"}}</span>
<!-- R-249: the value is NOT in this page. The old markup rendered it into a
display:none span, which hid it from the eye and from nothing else — a fetch of the
page returned the plaintext. „Megjelenít" now asks the server for it. -->
<span class="settings-value">
<span id="retrieval-pw-slot" class="mono">••••••••••••••••</span>
<button type="button" id="retrieval-pw-btn" class="btn btn-xs btn-outline" onclick="revealRetrievalPw()">Megjelenít</button>
<button type="button" id="retrieval-pw-btn" class="btn btn-xs btn-outline" onclick="revealRetrievalPw()">{{T "settings_security.megjelenit"}}</button>
<span id="retrieval-pw-err" class="form-hint" style="display:none;color:var(--red)"></span>
</span>
</div>
<div class="settings-row">
<span class="settings-label">Támogatás</span>
<span class="settings-label">{{T "settings_security.tamogatas"}}</span>
<span class="settings-value">
<a href="mailto:{{.SupportEmail}}" style="color: var(--blue);">{{.SupportEmail}}</a>
&nbsp;|&nbsp;
<a href="{{.SupportURL}}" target="_blank" style="color: var(--blue);">felhom.eu/kapcsolat</a>
{{T "settings_security.felhom_eu_kapcsolat"}}
</span>
</div>
</div>
@@ -451,7 +448,7 @@ function revealRetrievalPw() {
err.style.display = 'none';
if (btn.dataset.shown === '1') { // „Elrejt" — drop the value out of the DOM again
slot.textContent = '••••••••••••••••';
btn.textContent = 'Megjelenít';
btn.textContent = '{{T "settings_security.megjelenit"}}';
btn.dataset.shown = '';
return;
}
@@ -463,16 +460,16 @@ function revealRetrievalPw() {
}).then(function (r) { return r.json(); }).then(function (j) {
btn.disabled = false;
if (!j.ok) {
err.textContent = j.error || 'A visszaállítási jelszó lekérése nem sikerült.';
err.textContent = j.error || '{{T "settings_security.a_visszaallitasi_jelszo_lekerese_nem"}}';
err.style.display = 'inline';
return;
}
slot.textContent = j.data.password;
btn.textContent = 'Elrejt';
btn.textContent = '{{T "settings_security.elrejt"}}';
btn.dataset.shown = '1';
}).catch(function () {
btn.disabled = false;
err.textContent = 'A visszaállítási jelszó lekérése nem sikerült.';
err.textContent = '{{T "settings_security.a_visszaallitasi_jelszo_lekerese_nem"}}';
err.style.display = 'inline';
});
}
@@ -2,20 +2,20 @@
{{template "layout_start" .}}
<div class="page-header">
<h2>Beállítások — Rendszer</h2>
<h2>{{T "settings_system.beallitasok_rendszer"}}</h2>
</div>
<!-- Section A: System Configuration (read-only) -->
<div class="settings-card">
<h3>Rendszer konfiguráció</h3>
<p class="settings-card-desc">Az üzemeltető által beállított értékek. Módosításhoz kérd az üzemeltetőt.</p>
<h3>{{T "settings_system.rendszer_konfiguracio"}}</h3>
<p class="settings-card-desc">{{T "settings_system.az_uzemelteto_altal_beallitott_ertekek"}}</p>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Ügyfél azonosító</span>
<span class="settings-label">{{T "common.ugyfel_azonosito"}}</span>
<span class="settings-value mono">{{.CustomerID}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Ügyfél neve</span>
<span class="settings-label">{{T "settings_system.ugyfel_neve"}}</span>
<span class="settings-value">{{.CustomerName}}</span>
</div>
<div class="settings-row">
@@ -24,27 +24,27 @@
</div>
{{if .GitRepoURL}}
<div class="settings-row">
<span class="settings-label">Alkalmazás sablon forrás</span>
<span class="settings-label">{{T "settings_system.alkalmazas_sablon_forras"}}</span>
<span class="settings-value mono settings-value-truncate">{{.GitRepoURL}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">Sablon szinkronizálás</span>
<span class="settings-label">{{T "settings_system.sablon_szinkronizalas"}}</span>
<span class="settings-value mono">{{.GitSyncInterval}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Biztonsági mentés</span>
<span class="settings-value">{{if .BackupEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span>{{else}}<span class="state-text-neutral">Inaktív</span>{{end}}</span>
<span class="settings-label">{{T "settings_system.biztonsagi_mentes"}}</span>
<span class="settings-value">{{if .BackupEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span>{{else}}<span class="state-text-neutral">{{T "settings_system.inaktiv"}}</span>{{end}}</span>
</div>
{{if .BackupEnabled}}
<div class="settings-row">
<span class="settings-label">Mentés ütemezés</span>
<span class="settings-label">{{T "settings_system.mentes_utemezes"}}</span>
<span class="settings-value mono">{{.DBDumpSchedule}} / {{.ResticSchedule}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">Monitoring</span>
<span class="settings-value">{{if .MonitoringEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span>{{else}}<span class="state-text-neutral">Inaktív</span>{{end}}</span>
<span class="settings-value">{{if .MonitoringEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span>{{else}}<span class="state-text-neutral">{{T "settings_system.inaktiv"}}</span>{{end}}</span>
</div>
{{if .MonitoringEnabled}}
<div class="settings-row">
@@ -53,78 +53,78 @@
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">Hub jelentés</span>
<span class="settings-value">{{if .HubEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span>{{else}}–{{end}}</span>
<span class="settings-label">{{T "settings_system.hub_jelentes"}}</span>
<span class="settings-value">{{if .HubEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span>{{else}}–{{end}}</span>
</div>
</div>
</div>
<!-- Section: Version & Update -->
<div class="settings-card">
<h3>Verzió és frissítés</h3>
<h3>{{T "settings_system.verzio_es_frissites"}}</h3>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Jelenlegi verzió</span>
<span class="settings-label">{{T "settings_system.jelenlegi_verzio"}}</span>
<span class="settings-value mono">{{.Version}}</span>
</div>
{{if .SelfUpdateEnabled}}
{{if .LatestVersion}}
<div class="settings-row">
<span class="settings-label">Legújabb verzió</span>
<span class="settings-label">{{T "settings_system.legujabb_verzio"}}</span>
<span class="settings-value mono">
{{.LatestVersion}}
{{if .UpdateAvailable}}
<span class="state-text-run" style="margin-left:0.5em;">● Frissítés elérhető</span>
<span class="state-text-run" style="margin-left:0.5em;">{{T "settings_system.frissites_elerheto"}}</span>
{{else}}
<span style="margin-left:0.5em; color:var(--text-3);">— naprakész</span>
<span style="margin-left:0.5em; color:var(--text-3);">{{T "settings_system.naprakesz"}}</span>
{{end}}
</span>
</div>
{{end}}
{{if .LastCheckTime}}
<div class="settings-row">
<span class="settings-label">Utolsó ellenőrzés</span>
<span class="settings-label">{{T "settings_system.utolso_ellenorzes"}}</span>
<span class="settings-value mono">{{.LastCheckTime}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">Registry</span>
<span class="settings-value">{{if .RegistryAnonymous}}nyilvános (hitelesítés nélkül){{else}}hitelesített{{end}}</span>
<span class="settings-value">{{if .RegistryAnonymous}}{{T "settings_system.nyilvanos_hitelesites_nelkul"}}{{else}}{{T "settings_system.hitelesitett"}}{{end}}</span>
</div>
{{if .LastCheckError}}
<div class="settings-row">
<span class="settings-label">Hiba</span>
<span class="settings-label">{{T "common.hiba"}}</span>
<span class="settings-value state-text-crit">{{.LastCheckError}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">Automatikus frissítés</span>
<span class="settings-label">{{T "settings_system.automatikus_frissites"}}</span>
<span class="settings-value">
{{if .AutoUpdateEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Aktív</span> <span class="mono">({{.AutoUpdateTime}})</span>{{else}}–{{end}}
{{if .AutoUpdateEnabled}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.aktiv"}}</span> <span class="mono">({{.AutoUpdateTime}})</span>{{else}}–{{end}}
</span>
</div>
{{if .ControllerFloor}}
<div class="settings-row">
<span class="settings-label">Minimális verzió (üzemeltető)</span>
<span class="settings-label">{{T "settings_system.minimalis_verzio_uzemelteto"}}</span>
<span class="settings-value mono">
{{.ControllerFloor}}
<span style="margin-left:0.5em; color:#888;">— a rendszer automatikusan erre a verzióra frissít, ha régebbi</span>
<span style="margin-left:0.5em; color:#888;">{{T "settings_system.a_rendszer_automatikusan_erre_a"}}</span>
</span>
</div>
{{end}}
{{if .UpdateRunning}}
<div class="settings-row">
<span class="settings-label">Állapot</span>
<span class="settings-value state-text-progress" id="auto-update-status"><svg class="ico ico-sm ico-spin"><use href="#i-rotate-cw"/></svg> Frissítés folyamatban — a vezérlő hamarosan újraindul…</span>
<span class="settings-label">{{T "settings_system.allapot"}}</span>
<span class="settings-value state-text-progress" id="auto-update-status"><svg class="ico ico-sm ico-spin"><use href="#i-rotate-cw"/></svg> {{T "settings_system.frissites_folyamatban_a_vezerlo_hamarosa"}}</span>
</div>
{{end}}
{{with .LastUpdateState}}
<div class="settings-row">
<span class="settings-label">Utolsó frissítés</span>
<span class="settings-label">{{T "settings_system.utolso_frissites"}}</span>
<span class="settings-value">
{{if eq .Status "success"}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> Sikeres</span> ({{.PreviousVersion}} → {{.TargetVersion}})
{{else if eq .Status "failed"}}<span class="state-text-crit"><svg class="ico ico-sm"><use href="#i-x"/></svg> Sikertelen</span> — {{.Error}}
{{else if eq .Status "pending"}}<span class="state-text-progress"><svg class="ico ico-sm ico-spin"><use href="#i-rotate-cw"/></svg> Folyamatban</span>
{{if eq .Status "success"}}<span class="state-text-run"><svg class="ico ico-sm"><use href="#i-check"/></svg> {{T "settings_system.sikeres"}}</span> ({{.PreviousVersion}} → {{.TargetVersion}})
{{else if eq .Status "failed"}}<span class="state-text-crit"><svg class="ico ico-sm"><use href="#i-x"/></svg> {{T "settings_system.sikertelen"}}</span> — {{.Error}}
{{else if eq .Status "pending"}}<span class="state-text-progress"><svg class="ico ico-sm ico-spin"><use href="#i-rotate-cw"/></svg> {{T "settings_system.folyamatban"}}</span>
{{end}}
</span>
</div>
@@ -132,9 +132,9 @@
<div class="settings-row" style="padding-top: 0.5em;">
<span class="settings-label"></span>
<span class="settings-value">
<button class="btn btn-secondary btn-sm" id="btn-check-update" onclick="checkUpdate()">Frissítés keresése</button>
<button class="btn btn-secondary btn-sm" id="btn-check-update" onclick="checkUpdate()">{{T "settings_system.frissites_keresese"}}</button>
{{if .UpdateAvailable}}
<button class="btn btn-primary btn-sm" id="btn-trigger-update" onclick="triggerUpdate()" style="margin-left:0.5em;">Frissítés telepítése</button>
<button class="btn btn-primary btn-sm" id="btn-trigger-update" onclick="triggerUpdate()" style="margin-left:0.5em;">{{T "settings_system.frissites_telepitese"}}</button>
{{end}}
<span id="update-status-msg" style="margin-left:0.5em; display:none;"></span>
</span>
@@ -147,50 +147,50 @@
stored NOWHERE (S-5: a DHCP lease persisted anywhere eventually misdirects people); a missing
value renders „—" because an address-less row beats a wrong address. -->
<div class="settings-card">
<h3>Hálózat</h3>
<h3>{{T "settings_system.halozat"}}</h3>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Helyi cím (LAN)</span>
<span class="settings-value mono">{{if .NetLANAddress}}{{.NetLANAddress}}{{else}}— <span style="color:var(--text-3);">nem állapítható meg</span>{{end}}</span>
<span class="settings-label">{{T "settings_system.helyi_cim_lan"}}</span>
<span class="settings-value mono">{{if .NetLANAddress}}{{.NetLANAddress}}{{else}}— <span style="color:var(--text-3);">{{T "settings_system.nem_allapithato_meg"}}</span>{{end}}</span>
</div>
{{if .NetSMBName}}
<div class="settings-row">
<span class="settings-label">Hálózati név</span>
<span class="settings-label">{{T "settings_system.halozati_nev"}}</span>
<span class="settings-value mono">\\{{.NetSMBName}}</span>
</div>
{{end}}
<div class="settings-row">
<span class="settings-label">Átjáró</span>
<span class="settings-value mono">{{if .NetGateway}}{{.NetGateway}}{{else}}— <span style="color:var(--text-3);">nem állapítható meg</span>{{end}}</span>
<span class="settings-label">{{T "settings_system.atjaro"}}</span>
<span class="settings-value mono">{{if .NetGateway}}{{.NetGateway}}{{else}}— <span style="color:var(--text-3);">{{T "settings_system.nem_allapithato_meg"}}</span>{{end}}</span>
</div>
</div>
<p class="settings-card-desc" style="margin-top:0.5em;">Ezek az értékek a helyi hálózattól függenek és változhatnak. Távoli hibaelhárításnál ezt az oldalt kérjük felolvasni.</p>
<p class="settings-card-desc" style="margin-top:0.5em;">{{T "settings_system.ezek_az_ertekek_a_helyi"}}</p>
</div>
<div class="settings-card">
<h3>Szerver memória (RAM)</h3>
<h3>{{T "settings_system.szerver_memoria_ram"}}</h3>
{{if .MemorySupported}}
{{if .MemoryReachable}}
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Jelenlegi memória</span>
<span class="settings-label">{{T "settings_system.jelenlegi_memoria"}}</span>
<span class="settings-value mono">{{.MemoryAllocatedMB}} MB</span>
</div>
<div class="settings-row">
<span class="settings-label">Felhasznált</span>
<span class="settings-label">{{T "settings_system.felhasznalt"}}</span>
<span class="settings-value mono">{{.MemoryUsageMB}} MB</span>
</div>
<div class="settings-row">
<span class="settings-label">Megengedett tartomány</span>
<span class="settings-label">{{T "settings_system.megengedett_tartomany"}}</span>
<span class="settings-value mono">{{.MemoryMinMB}} MB – {{.MemoryMaxMB}} MB</span>
</div>
<div class="settings-row" style="padding-top: 0.5em;">
<span class="settings-label">Új méret (MB)</span>
<span class="settings-label">{{T "settings_system.uj_meret_mb"}}</span>
<span class="settings-value">
<input type="number" id="mem-input" class="input mono" style="width:8em;"
value="{{.MemoryAllocatedMB}}" min="{{.MemoryMinMB}}" max="{{.MemoryMaxMB}}" step="256"
data-current="{{.MemoryAllocatedMB}}">
<button class="btn btn-primary btn-sm" id="btn-mem-resize" onclick="resizeMemory()" style="margin-left:0.5em;">Átméretezés</button>
<button class="btn btn-primary btn-sm" id="btn-mem-resize" onclick="resizeMemory()" style="margin-left:0.5em;">{{T "settings_system.atmeretezes"}}</button>
<span id="mem-status-msg" style="margin-left:0.5em; display:none;"></span>
</span>
</div>
@@ -198,14 +198,14 @@
{{else}}
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Jelenlegi memória</span>
<span class="settings-label">{{T "settings_system.jelenlegi_memoria"}}</span>
<span class="settings-value mono">{{if .MemoryProcMB}}{{.MemoryProcMB}} MB{{else}}n/a{{end}}</span>
</div>
</div>
<p class="settings-card-desc">A kiszolgáló ügynök jelenleg nem elérhető — próbáld meg később.</p>
<p class="settings-card-desc">{{T "settings_system.a_kiszolgalo_ugynok_jelenleg_nem"}}</p>
{{end}}
{{else}}
<p class="settings-card-desc">A memória átméretezéséhez a kiszolgáló rendszerfrissítése szükséges.</p>
<p class="settings-card-desc">{{T "settings_system.a_memoria_atmeretezesehez_a_kiszolgalo"}}</p>
{{end}}
</div>
@@ -218,10 +218,10 @@ function openDialog(opts){
var root=document.getElementById('dialog-root');
if(!root) return;
root.innerHTML='<div class="confirm-overlay" onclick="if(event.target===this)document.getElementById(\'dialog-cancel\').click()"><div class="confirm-box">'
+'<h3>'+escText(opts.title||'Megerősítés')+'</h3>'
+'<h3>'+escText(opts.title||'{{T "settings_system.megerosites"}}')+'</h3>'
+'<p style="white-space:pre-line">'+escText(opts.message||'')+'</p>'
+'<div class="form-actions"><button id="dialog-go" class="btn btn-primary">'+escText(opts.confirmLabel||'Megerősítés')+'</button>'
+'<button type="button" class="btn btn-outline" id="dialog-cancel">Mégsem</button></div>'
+'<div class="form-actions"><button id="dialog-go" class="btn btn-primary">'+escText(opts.confirmLabel||'{{T "settings_system.megerosites"}}')+'</button>'
+'<button type="button" class="btn btn-outline" id="dialog-cancel">{{T "common.megsem"}}</button></div>'
+'</div></div>';
document.getElementById('dialog-go').onclick=function(){ closeDialog(); if(opts.onConfirm) opts.onConfirm(); };
document.getElementById('dialog-cancel').onclick=function(){ closeDialog(); if(opts.onCancel) opts.onCancel(); };
@@ -231,7 +231,7 @@ function checkUpdate() {
var btn = document.getElementById('btn-check-update');
var msg = document.getElementById('update-status-msg');
btn.disabled = true;
btn.textContent = 'Ellenőrzés...';
btn.textContent = '{{T "settings_system.ellenorzes"}}';
msg.style.display = 'none';
fetch('/api/selfupdate/check', {method:'POST', headers: csrfHeaders()})
.then(function(r) { return r.json(); })
@@ -239,23 +239,23 @@ function checkUpdate() {
if (data.ok) {
location.reload();
} else {
msg.textContent = data.error || 'Hiba történt';
msg.textContent = data.error || '{{T "common.hiba_tortent"}}';
msg.style.display = 'inline';
btn.disabled = false;
btn.textContent = 'Frissítés keresése';
btn.textContent = '{{T "settings_system.frissites_keresese"}}';
}
})
.catch(function() {
msg.textContent = 'Kapcsolódási hiba';
msg.textContent = '{{T "settings_system.kapcsolodasi_hiba"}}';
msg.style.display = 'inline';
btn.disabled = false;
btn.textContent = 'Frissítés keresése';
btn.textContent = '{{T "settings_system.frissites_keresese"}}';
});
}
function triggerUpdate() {
openDialog({title:'Vezérlő frissítése', confirmLabel:'Frissítés',
message:'Biztosan frissíti a controllert?\n\nA folyamat alatt a vezérlőpult rövid időre elérhetetlenné válik.',
openDialog({title:'{{T "settings_system.vezerlo_frissitese"}}', confirmLabel:'{{T "settings_system.frissites"}}',
message:'{{T "settings_system.biztosan_frissiti_a_controllert_n"}}',
onConfirm:doTriggerUpdate});
}
function doTriggerUpdate() {
@@ -263,25 +263,25 @@ function doTriggerUpdate() {
var checkBtn = document.getElementById('btn-check-update');
var msg = document.getElementById('update-status-msg');
btn.disabled = true;
btn.textContent = 'Frissítés...';
btn.textContent = '{{T "settings_system.frissites_2"}}';
if (checkBtn) checkBtn.disabled = true;
msg.textContent = 'Frissítés folyamatban...';
msg.textContent = '{{T "settings_system.frissites_folyamatban"}}';
msg.style.display = 'inline';
fetch('/api/selfupdate/update', {method:'POST', headers: csrfHeaders()})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.ok) {
msg.textContent = 'Újraindulás...';
msg.textContent = '{{T "settings_system.ujraindulas"}}';
pollUntilBack();
} else {
msg.textContent = data.error || 'Hiba történt';
msg.textContent = data.error || '{{T "common.hiba_tortent"}}';
btn.disabled = false;
btn.textContent = 'Frissítés telepítése';
btn.textContent = '{{T "settings_system.frissites_telepitese"}}';
if (checkBtn) checkBtn.disabled = false;
}
})
.catch(function() {
msg.textContent = 'Kapcsolódási hiba';
msg.textContent = '{{T "settings_system.kapcsolodasi_hiba"}}';
pollUntilBack();
});
}
@@ -291,12 +291,12 @@ function resizeMemory() {
var target = parseInt(input.value, 10);
var current = parseInt(input.getAttribute('data-current'), 10);
if (isNaN(target) || target <= 0) {
showMemMsg('Érvénytelen memória érték', true);
showMemMsg('{{T "settings_system.ervenytelen_memoria_ertek"}}', true);
return;
}
if (target < current) {
openDialog({title:'Memória csökkentése', confirmLabel:'Csökkentés',
message:'A memória csökkentése hatással lehet a futó alkalmazások teljesítményére. Biztosan folytatod?',
openDialog({title:'{{T "settings_system.memoria_csokkentese"}}', confirmLabel:'{{T "settings_system.csokkentes"}}',
message:'{{T "settings_system.a_memoria_csokkentese_hatassal_lehet"}}',
onConfirm:function(){ doResizeMemory(target); }});
return;
}
@@ -305,7 +305,7 @@ function resizeMemory() {
function doResizeMemory(target) {
var btn = document.getElementById('btn-mem-resize');
btn.disabled = true;
btn.textContent = 'Átméretezés...';
btn.textContent = '{{T "settings_system.atmeretezes_2"}}';
fetch('/api/system/memory/resize', {
method:'POST', headers: Object.assign({'Content-Type':'application/json'}, csrfHeaders()),
body: JSON.stringify({memory_mb: target})
@@ -313,18 +313,18 @@ function doResizeMemory(target) {
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.ok) {
showMemMsg(data.error || data.message || 'Kész.', false);
showMemMsg(data.error || data.message || '{{T "settings_system.kesz"}}', false);
setTimeout(function(){ location.reload(); }, 1200);
} else {
showMemMsg(data.error || 'A művelet nem sikerült', true);
showMemMsg(data.error || '{{T "settings_system.a_muvelet_nem_sikerult"}}', true);
btn.disabled = false;
btn.textContent = 'Átméretezés';
btn.textContent = '{{T "settings_system.atmeretezes"}}';
}
})
.catch(function() {
showMemMsg('Kapcsolódási hiba', true);
showMemMsg('{{T "settings_system.kapcsolodasi_hiba"}}', true);
btn.disabled = false;
btn.textContent = 'Átméretezés';
btn.textContent = '{{T "settings_system.atmeretezes"}}';
});
}
function showMemMsg(text, isErr) {
@@ -357,50 +357,49 @@ pollUntilBack();
<!-- Section: Controller restart (self-serve) — always available (not gated on RetrievalPassword) -->
<div class="settings-card">
<h3>Vezérlő újraindítása</h3>
<h3>{{T "settings_system.vezerlo_ujrainditasa"}}</h3>
<p class="settings-card-desc">
Ha a vezérlő hibásan működik, itt biztonságosan újraindíthatja — nem kell az egész szervert újraindítani.
Az alkalmazásai futnak tovább; csak a vezérlő indul újra (néhány másodperc).
{{T "settings_system.ha_a_vezerlo_hibasan_mukodik"}}
</p>
<div id="restart-status"></div>
<button type="button" class="btn btn-outline" id="btn-restart-controller" onclick="restartController()">Vezérlő újraindítása</button>
<button type="button" class="btn btn-outline" id="btn-restart-controller" onclick="restartController()">{{T "settings_system.vezerlo_ujrainditasa"}}</button>
</div>
<!-- Section: Full server (guest) restart — a deliberate maintenance affordance, sibling to the controller restart -->
<div class="settings-card">
<h3>Kiszolgáló újraindítása</h3>
<h3>{{T "settings_system.kiszolgalo_ujrainditasa"}}</h3>
<p class="settings-card-desc">
Az egész kiszolgáló (szerver) újraindítása. Minden alkalmazás rövid időre leáll, és a vezérlőpult kb. 30 másodpercig nem elérhető. Akkor használja, ha a teljes rendszer újraindítására van szükség — egyébként a fenti „Vezérlő újraindítása” elegendő.
{{T "settings_system.az_egesz_kiszolgalo_szerver_ujrainditasa"}}
</p>
<div id="server-restart-status"></div>
<button type="button" class="btn btn-outline" id="btn-restart-server" onclick="restartServer()">Kiszolgáló újraindítása</button>
<button type="button" class="btn btn-outline" id="btn-restart-server" onclick="restartServer()">{{T "settings_system.kiszolgalo_ujrainditasa"}}</button>
</div>
<script>
function restartController() {
openDialog({title:'Vezérlő újraindítása', confirmLabel:'Újraindítás',
message:'Biztosan újraindítja a vezérlőt? A művelet néhány másodpercig tart, és a felület rövid időre elérhetetlen lesz.',
openDialog({title:'{{T "settings_system.vezerlo_ujrainditasa"}}', confirmLabel:'{{T "common.ujrainditas"}}',
message:'{{T "settings_system.biztosan_ujrainditja_a_vezerlot_a"}}',
onConfirm:doRestartController});
}
function doRestartController() {
var btn = document.getElementById('btn-restart-controller');
var status = document.getElementById('restart-status');
btn.disabled = true;
status.innerHTML = '<div class="alert alert-info">Újraindítás folyamatban… újracsatlakozás…</div>';
status.innerHTML = '<div class="alert alert-info">{{T "settings_system.ujrainditas_folyamatban_ujracsatlakozas"}}</div>';
fetch('/api/selfrestart', { method: 'POST', headers: csrfHeaders() })
.then(function(){ pollRestart(0); })
.catch(function(){ pollRestart(0); }); // connection may drop as the process exits — poll regardless
}
function restartServer() {
openDialog({title:'Kiszolgáló újraindítása', confirmLabel:'Újraindítás',
message:'Biztosan újraindítja a kiszolgálót? Az alkalmazások és a vezérlőpult kb. 30 másodpercre elérhetetlenné válnak.',
openDialog({title:'{{T "settings_system.kiszolgalo_ujrainditasa"}}', confirmLabel:'{{T "common.ujrainditas"}}',
message:'{{T "settings_system.biztosan_ujrainditja_a_kiszolgalot_az"}}',
onConfirm:doRestartServer});
}
function doRestartServer() {
var btn = document.getElementById('btn-restart-server');
var status = document.getElementById('server-restart-status');
if (btn) btn.disabled = true;
if (status) status.innerHTML = '<div class="alert alert-info">Újraindítás folyamatban… a vezérlőpult néhány másodperc múlva újratölt.</div>';
if (status) status.innerHTML = '<div class="alert alert-info">{{T "settings_system.ujrainditas_folyamatban_a_vezerlopult_ne"}}</div>';
fetch('/api/server/reboot', { method: 'POST', headers: csrfHeaders() })
.then(function(){ pollRestart(0); })
.catch(function(){ pollRestart(0); }); // the guest reboot drops the connection — poll regardless
@@ -408,7 +407,7 @@ function doRestartServer() {
function pollRestart(attempt) {
if (attempt > 60) { // ~2 min cap — never leave the user on a dead page silently
document.getElementById('restart-status').innerHTML =
'<div class="alert alert-error">Az újraindítás a vártnál tovább tart. Töltse újra az oldalt kézzel.</div>';
'<div class="alert alert-error">{{T "settings_system.az_ujrainditas_a_vartnal_tovabb"}}</div>';
return;
}
setTimeout(function(){
+31 -31
View File
@@ -2,18 +2,18 @@
{{template "layout_start" .}}
<div class="page-header">
<h2>Alkalmazások</h2>
<h2>{{T "stacks.alkalmazasok"}}</h2>
<span class="domain-badge">{{.Domain}}</span>
<a href="/import" class="btn btn-sm btn-outline" title="Alkalmazás visszaállítása exportált csomagból">Importálás</a>
<button class="btn btn-sm btn-outline" id="sync-btn" onclick="syncTemplates()" title="Sablonok frissítése a központi katalógusból">↻ Sablonok frissítése</button>
<a href="/import" class="btn btn-sm btn-outline" title="{{T "stacks.alkalmazas_visszaallitasa_exportalt_csom"}}">{{T "stacks.importalas"}}</a>
<button class="btn btn-sm btn-outline" id="sync-btn" onclick="syncTemplates()" title="{{T "stacks.sablonok_frissitese_a_kozponti_katalogus"}}">{{T "stacks.sablonok_frissitese"}}</button>
</div>
<div id="sync-toast" class="sync-toast" style="display:none"></div>
<div class="filter-bar" id="filter-bar">
<button class="filter-btn active" data-filter="all">Mind <span class="filter-count" id="count-all"></span></button>
<button class="filter-btn" data-filter="running">Futó <span class="filter-count" id="count-running"></span></button>
<button class="filter-btn" data-filter="stopped">Leállítva <span class="filter-count" id="count-stopped"></span></button>
<button class="filter-btn" data-filter="available">Telepíthető <span class="filter-count" id="count-available"></span></button>
<button class="filter-btn active" data-filter="all">{{T "stacks.mind"}} <span class="filter-count" id="count-all"></span></button>
<button class="filter-btn" data-filter="running">{{T "stacks.futo"}} <span class="filter-count" id="count-running"></span></button>
<button class="filter-btn" data-filter="stopped">{{T "stacks.leallitva"}} <span class="filter-count" id="count-stopped"></span></button>
<button class="filter-btn" data-filter="available">{{T "stacks.telepitheto"}} <span class="filter-count" id="count-available"></span></button>
</div>
<div class="stack-grid">
@@ -32,18 +32,18 @@
{{$subdomain}}.{{$.Domain}} ↗
</a>
{{if and .Deployed (routeUnpublished .State)}}
<span class="route-unpublished" title="A proxy (Traefik) csak egészséges konténerhez publikál nyilvános útvonalat. Amíg az alkalmazás nem egészséges, az URL nem érhető el (404), pedig a konténer fut."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> URL nem elérhető – útvonal nincs publikálva</span>
<span class="route-unpublished" title="{{T "stacks.a_proxy_traefik_csak_egeszseges"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> {{T "stacks.url_nem_elerheto_utvonal_nincs"}}</span>
{{end}}
{{end}}
</div>
</div>
<span class="tag tag-{{stateColor .State}}"><span class="dot"></span>{{stateLabel .State}}</span>
{{if .Orphaned}}<span class="tag tag-warn">Elavult</span>{{end}}
{{if .Orphaned}}<span class="tag tag-warn">{{T "common.elavult"}}</span>{{end}}
{{template "meta_badge" (lifecycleBadge .Meta)}}
{{template "meta_badge" (updateBadge .)}}
{{$ms := index $.MissingStorage .Name}}{{if $ms}}<span class="tag tag-warn" title="Az alkalmazás adattárolója nem elérhető. Csatlakoztasd újra a meghajtót, vagy helyezd át az adatokat."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Hiányzó tárhely: {{$ms}}</span>{{end}}
{{$ns := index $.NetworkStubs .Name}}{{if $ns}}<span class="tag tag-warn" title="Az alkalmazás környezetében a hálózati tárhely helyén üres helyi könyvtár van — az adatok nem a NAS-ra kerülnek. Jelezze az üzemeltetőnek."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Hálózati tárhely hibás — az alkalmazás nem a NAS-t látja</span>{{end}}
{{$nw := index $.NetworkWarnings .Name}}{{if $nw}}<span class="tag tag-warn" title="A hálózati tárhely (NAS) jelenleg nem érhető el. Az alkalmazás fut; az adatok elérése a NAS visszatértével helyreáll."><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>Hálózati tárhely nem elérhető: {{$nw}}</span>{{end}}
{{$ms := index $.MissingStorage .Name}}{{if $ms}}<span class="tag tag-warn" title="{{T "stacks.az_alkalmazas_adattaroloja_nem_elerheto"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "stacks.hianyzo_tarhely"}}</span>{{end}}
{{$ns := index $.NetworkStubs .Name}}{{if $ns}}<span class="tag tag-warn" title="{{T "stacks.az_alkalmazas_kornyezeteben_a_halozati"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "stacks.halozati_tarhely_hibas_az_alkalmazas"}}</span>{{end}}
{{$nw := index $.NetworkWarnings .Name}}{{if $nw}}<span class="tag tag-warn" title="{{T "stacks.a_halozati_tarhely_nas_jelenleg"}}"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg>{{T "stacks.halozati_tarhely_nem_elerheto"}}</span>{{end}}
</div>
{{if $im}}
@@ -54,10 +54,10 @@
<div class="metarows">
{{if .Meta.Resources.MemRequest}}<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~{{.Meta.Resources.MemRequest}}</span></span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Pi kompatibilis</span>{{end}}
{{if .Meta.Resources.NeedsHDD}}<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>HDD szükséges</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Magyar felület</span>{{end}}
{{if and .Deployed (index $.StorageLabels .Name)}}<span class="metarow" title="Adattároló: {{index $.StorageLabels .Name}}"><svg class="ico"><use href="#i-hard-drive"/></svg>{{index $.StorageLabels .Name}}</span>{{end}}
{{if .Meta.Resources.PiCompatible}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>{{T "common.pi_kompatibilis"}}</span>{{end}}
{{if .Meta.Resources.NeedsHDD}}<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>{{T "common.hdd_szukseges"}}</span>{{end}}
{{if .Meta.Resources.HungarianUI}}<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>{{T "common.magyar_felulet"}}</span>{{end}}
{{if and .Deployed (index $.StorageLabels .Name)}}<span class="metarow" title="{{T "stacks.adattarolo"}}"><svg class="ico"><use href="#i-hard-drive"/></svg>{{index $.StorageLabels .Name}}</span>{{end}}
</div>
{{if .Containers}}
@@ -73,18 +73,18 @@
<div class="stack-detail-actions">
{{if .Protected}}
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>Védett rendszerkomponens</span>
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>{{T "stacks.vedett_rendszerkomponens"}}</span>
{{if isOperational .State}}
<button class="btn btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')">Újraindítás</button>
<button class="btn btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')">{{T "common.ujrainditas"}}</button>
{{end}}
{{if .Meta.Slug}}
<a href="/apps/{{.Meta.Slug}}" class="btn btn-outline">Részletek</a>
<a href="/apps/{{.Meta.Slug}}" class="btn btn-outline">{{T "stacks.reszletek"}}</a>
{{end}}
{{else if not .Deployed}}
{{/* The endpoint refuses a non-installable template server-side; hiding the button
here keeps the two consistent rather than offering an action that will fail. */}}
{{if canInstall .Meta}}<a href="/stacks/{{.Name}}/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, '{{.Name}}')">Telepítés</a>{{end}}
<a href="{{appPageURL .Meta.Slug}}" class="btn btn-outline">Részletek</a>
{{if canInstall .Meta}}<a href="/stacks/{{.Name}}/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, '{{.Name}}')">{{T "stacks.telepites"}}</a>{{end}}
<a href="{{appPageURL .Meta.Slug}}" class="btn btn-outline">{{T "stacks.reszletek"}}</a>
{{else}}
{{/* Slice 4 (v0.238.0): the UPDATING and HELD checks come BEFORE isOperational. That
predicate counts `restarting` as operational, which is how the 2026-09-01 spike saw
@@ -93,21 +93,21 @@
{{if .Updating}}
<span class="tag tag-progress" data-update-phase="{{.UpdatePhase}}"><span class="dot"></span>{{.UpdatePhaseLabel}}</span>
{{else if .HoldReason}}
<div class="alert alert-error" data-held="true">{{.HoldReason}} <a href="/backups/apps" class="btn btn-sm btn-outline">Mentések</a></div>
{{if not .Orphaned}}<button class="btn btn-danger" onclick="removeStack('{{.Name}}')">Eltávolítás</button>{{end}}
<div class="alert alert-error" data-held="true">{{.HoldReason}} <a href="/backups/apps" class="btn btn-sm btn-outline">{{T "stacks.mentesek"}}</a></div>
{{if not .Orphaned}}<button class="btn btn-danger" onclick="removeStack('{{.Name}}')">{{T "common.eltavolitas"}}</button>{{end}}
{{else if isOperational .State}}
{{if .UpdateError}}<div class="alert alert-warning" data-update-error="true">{{.UpdateError}}</div>{{end}}
{{if not .Orphaned}}<button class="btn btn-success" onclick="stackAction(event, '{{.Name}}', 'update')">Frissítés</button>{{end}}
<button class="btn btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')">Újraindítás</button>
<button class="btn btn-danger" onclick="stackAction(event, '{{.Name}}', 'stop')">Leállítás</button>
{{if not .Orphaned}}<button class="btn btn-success" onclick="stackAction(event, '{{.Name}}', 'update')">{{T "stacks.frissites"}}</button>{{end}}
<button class="btn btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')">{{T "common.ujrainditas"}}</button>
<button class="btn btn-danger" onclick="stackAction(event, '{{.Name}}', 'stop')">{{T "stacks.leallitas"}}</button>
{{else}}
{{if .UpdateError}}<div class="alert alert-warning" data-update-error="true">{{.UpdateError}}</div>{{end}}
<button class="btn btn-success" onclick="stackAction(event, '{{.Name}}', 'start')">Indítás</button>
{{if not .Orphaned}}<button class="btn btn-danger" onclick="removeStack('{{.Name}}')">Eltávolítás</button>{{end}}
<button class="btn btn-success" onclick="stackAction(event, '{{.Name}}', 'start')">{{T "stacks.inditas"}}</button>
{{if not .Orphaned}}<button class="btn btn-danger" onclick="removeStack('{{.Name}}')">{{T "common.eltavolitas"}}</button>{{end}}
{{end}}
<a href="/stacks/{{.Name}}/logs" class="btn btn-outline">Naplók</a>
{{if not .Orphaned}}<a href="{{appPageURL .Meta.Slug}}" class="btn btn-outline">Részletek</a>{{end}}
{{if .Orphaned}}<button class="btn btn-danger" onclick="deleteOrphanStack('{{.Name}}')">Törlés</button>{{end}}
<a href="/stacks/{{.Name}}/logs" class="btn btn-outline">{{T "stacks.naplok"}}</a>
{{if not .Orphaned}}<a href="{{appPageURL .Meta.Slug}}" class="btn btn-outline">{{T "stacks.reszletek"}}</a>{{end}}
{{if .Orphaned}}<button class="btn btn-danger" onclick="deleteOrphanStack('{{.Name}}')">{{T "common.torles"}}</button>{{end}}
{{end}}
</div>
</div>