D1 Part 2: settings.html split into four pages + sidebar restructure
- settings.html (1451 lines) deleted; sections moved verbatim into
settings_system.html (Rendszer konfiguráció, Verzió és frissítés,
Vezérlő/Kiszolgáló újraindítása + update/restart JS),
settings_notifications.html (Értesítések, Alkalmazás-email),
settings_security.html (Jelszó módosítás, Földrajzi korlátozás + geo
JS, Vészhelyzeti információk — heading + section copy accents fixed),
storage.html (Adattárolók, NAS, migrate progress, agent view + all
storage JS; wizard entry links now /storage/init|attach with sprite
icons instead of emoji). The NAS + migrate sections were nested inside
{{if .StoragePaths}} in the monolith and vanished with zero drives —
now unconditional on /storage.
- layout.html: Tárhely main-nav item (hard-drive icon) + the
'Beállítások' sidebar group with Rendszer / Értesítések / Biztonság és
hozzáférés sub-links (active-state per page key); orphaned
.sidebar-settings-link CSS deleted (grep-zero), .nav-group-label /
.nav-links-sub added.
- Handlers wired to their own builders + templates; the legacy
settingsData() merge deleted.
- scripts/template_id_gate.py: the §10 JS element-ID integrity gate
(getElementById/querySelector('#…') must resolve in the SAME template;
JS-created + template-parameterized IDs handled; layout modal IDs
allowlisted). Red-proven: a storage function planted in the
notifications template failed the gate with 'static #migrate-progress
not defined'.
- Tests: per-page section markers + cross-leak assertions, h3 section
inventory (all 11 old headings accounted for; typo rename asserted).
This commit is contained in:
@@ -1048,43 +1048,28 @@ func (s *Server) securityPageData() map[string]interface{} {
|
||||
return data
|
||||
}
|
||||
|
||||
// settingsData merges every subpage builder — legacy glue kept ONLY while the monolithic
|
||||
// settings.html exists (deleted with the D1 Part 2 template split).
|
||||
func (s *Server) settingsData() map[string]interface{} {
|
||||
data := s.systemPageData()
|
||||
for _, m := range []map[string]interface{}{s.storagePageData(), s.notificationsPageData(), s.securityPageData()} {
|
||||
for k, v := range m {
|
||||
if k == "Page" || k == "Title" {
|
||||
continue
|
||||
}
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (s *Server) settingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
s.executeTemplate(w, r, "settings", s.settingsData())
|
||||
s.executeTemplate(w, r, "settings_system", s.systemPageData())
|
||||
}
|
||||
|
||||
// storagePageHandler serves the Tárhely main-nav page (D1). Storage action flashes land here.
|
||||
func (s *Server) storagePageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := s.settingsData()
|
||||
data := s.storagePageData()
|
||||
if msg := r.URL.Query().Get("storage_msg"); msg == "success" {
|
||||
data["StorageSuccess"] = r.URL.Query().Get("storage_detail")
|
||||
}
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
}
|
||||
|
||||
// settingsNotificationsPageHandler serves GET /settings/notifications (the POST on the same
|
||||
// path is the save handler — dispatch is split in the router).
|
||||
func (s *Server) settingsNotificationsPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
s.executeTemplate(w, r, "settings", s.settingsData())
|
||||
s.executeTemplate(w, r, "settings_notifications", s.notificationsPageData())
|
||||
}
|
||||
|
||||
// settingsSecurityPageHandler serves GET /settings/security.
|
||||
func (s *Server) settingsSecurityPageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
s.executeTemplate(w, r, "settings", s.settingsData())
|
||||
s.executeTemplate(w, r, "settings_security", s.securityPageData())
|
||||
}
|
||||
|
||||
func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -1097,7 +1082,7 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request)
|
||||
s.logger.Printf("[DEBUG] [web] settingsPasswordHandler: password change attempt from %s", r.RemoteAddr)
|
||||
}
|
||||
|
||||
data := s.settingsData()
|
||||
data := s.securityPageData()
|
||||
|
||||
// Validate current password
|
||||
effectiveHash := s.effectivePasswordHash()
|
||||
@@ -1106,21 +1091,21 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request)
|
||||
s.logger.Printf("[DEBUG] [web] settingsPasswordHandler: current password mismatch from %s", r.RemoteAddr)
|
||||
}
|
||||
data["PasswordError"] = "Hibás jelenlegi jelszó"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_security", data)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate new password length
|
||||
if len(newPassword) < 8 {
|
||||
data["PasswordError"] = "A jelszónak legalább 8 karakter hosszúnak kell lennie"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_security", data)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate passwords match
|
||||
if newPassword != confirmPassword {
|
||||
data["PasswordError"] = "A két jelszó nem egyezik"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_security", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1129,7 +1114,7 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to hash new password: %v", err)
|
||||
data["PasswordError"] = "Belső hiba a jelszó mentésekor"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_security", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1137,7 +1122,7 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request)
|
||||
if err := s.settings.SetPasswordHash(string(hash)); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to save password to settings.json: %v", err)
|
||||
data["PasswordError"] = "Belső hiba a jelszó mentésekor"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_security", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1202,16 +1187,16 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
|
||||
|
||||
if err := s.settings.SetNotificationPrefs(prefs); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to save notification prefs: %v", err)
|
||||
data := s.settingsData()
|
||||
data := s.notificationsPageData()
|
||||
data["NotificationError"] = "Hiba a beállítások mentésekor"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Printf("[INFO] [web] Notification preferences updated: email=%s, events=%v", email, enabledEvents)
|
||||
|
||||
// Sync preferences to hub
|
||||
data := s.settingsData()
|
||||
data := s.notificationsPageData()
|
||||
if s.notifier != nil && s.notifier.IsEnabled() {
|
||||
if err := s.notifier.SyncPreferences(email, enabledEvents, cooldownHours); err != nil {
|
||||
s.logger.Printf("[WARN] [web] Failed to sync preferences to hub: %v", err)
|
||||
@@ -1222,7 +1207,7 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
|
||||
} else {
|
||||
data["NotificationSuccess"] = "Értesítési beállítások mentve."
|
||||
}
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
}
|
||||
|
||||
// settingsAppEmailHandler saves the global app-email toggle and starts/stops the on-box
|
||||
@@ -1232,39 +1217,39 @@ func (s *Server) settingsAppEmailHandler(w http.ResponseWriter, r *http.Request)
|
||||
enabled := r.FormValue("app_email_enabled") == "on" || r.FormValue("app_email_enabled") == "true"
|
||||
fromName := strings.TrimSpace(r.FormValue("app_email_from_name"))
|
||||
|
||||
data := s.settingsData()
|
||||
data := s.notificationsPageData()
|
||||
if err := s.settings.SetAppEmail(enabled, fromName); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to save app-email toggle: %v", err)
|
||||
data["AppEmailError"] = "Hiba az alkalmazás-email beállítás mentésekor"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
return
|
||||
}
|
||||
// Reconcile the shim's running state with the new toggle.
|
||||
if s.mailShim != nil {
|
||||
if err := s.mailShim.Apply(enabled); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] app-email shim could not be %s: %v", map[bool]string{true: "started", false: "stopped"}[enabled], err)
|
||||
data = s.settingsData()
|
||||
data = s.notificationsPageData()
|
||||
data["AppEmailError"] = "A beállítás elmentve, de az email-szolgáltatás indítása nem sikerült."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
return
|
||||
}
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] App-email globally %s (from_name=%q)", map[bool]string{true: "enabled", false: "disabled"}[enabled], fromName)
|
||||
data = s.settingsData()
|
||||
data = s.notificationsPageData()
|
||||
if enabled {
|
||||
data["AppEmailSuccess"] = "Alkalmazás-email bekapcsolva. Kapcsold be az egyes alkalmazásoknál is, ahol email-küldést szeretnél."
|
||||
} else {
|
||||
data["AppEmailSuccess"] = "Alkalmazás-email kikapcsolva."
|
||||
}
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
}
|
||||
|
||||
func (s *Server) settingsNotificationsTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := s.settingsData()
|
||||
data := s.notificationsPageData()
|
||||
|
||||
if s.notifier == nil {
|
||||
data["NotificationError"] = "Az értesítések nincsenek bekapcsolva"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1272,12 +1257,12 @@ func (s *Server) settingsNotificationsTestHandler(w http.ResponseWriter, r *http
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Test notification failed: %v", err)
|
||||
data["NotificationError"] = fmt.Sprintf("Teszt email küldése sikertelen: %v", err)
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
return
|
||||
}
|
||||
|
||||
data["NotificationSuccess"] = "Teszt email elküldve."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "settings_notifications", data)
|
||||
}
|
||||
|
||||
// --- Storage path management handlers ---
|
||||
@@ -1496,27 +1481,27 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques
|
||||
label = settings.InferStorageLabel(path)
|
||||
}
|
||||
|
||||
data := s.settingsData()
|
||||
data := s.storagePageData()
|
||||
|
||||
// 1. Exists and is directory
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil || !fi.IsDir() {
|
||||
data["StorageError"] = "Az útvonal nem létezik vagy nem mappa."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Is mount point
|
||||
if !system.IsMountPoint(path) {
|
||||
data["StorageError"] = "Ez az útvonal nem külön csatlakoztatott meghajtó. Adatok az SSD-re kerülnének!"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
// 3. Writable
|
||||
if !system.IsWritable(path) {
|
||||
data["StorageError"] = "Az útvonal nem írható."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1524,7 +1509,7 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques
|
||||
for _, existing := range s.settings.GetStoragePaths() {
|
||||
if system.PathsOverlap(path, existing.Path) {
|
||||
data["StorageError"] = fmt.Sprintf("Az útvonal átfedi a már regisztrált %s útvonalat.", existing.Path)
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1545,7 +1530,7 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques
|
||||
if err := s.settings.AddStoragePath(sp); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to add storage path: %v", err)
|
||||
data["StorageError"] = "Hiba a mentés során."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1562,13 +1547,13 @@ func (s *Server) settingsStorageRemoveHandler(w http.ResponseWriter, r *http.Req
|
||||
s.logger.Printf("[DEBUG] [web] settingsStorageRemoveHandler: path=%s from %s", path, r.RemoteAddr)
|
||||
}
|
||||
|
||||
data := s.settingsData()
|
||||
data := s.storagePageData()
|
||||
|
||||
// Check: apps using this path
|
||||
apps := s.appsUsingPath(path)
|
||||
if len(apps) > 0 {
|
||||
data["StorageError"] = fmt.Sprintf("Nem törölhető: az alábbi alkalmazások használják: %s", strings.Join(apps, ", "))
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1576,7 +1561,7 @@ func (s *Server) settingsStorageRemoveHandler(w http.ResponseWriter, r *http.Req
|
||||
for _, sp := range s.settings.GetStoragePaths() {
|
||||
if sp.Path == path && sp.IsDefault {
|
||||
data["StorageError"] = "Az alapértelmezett adattároló nem törölhető."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1584,13 +1569,13 @@ func (s *Server) settingsStorageRemoveHandler(w http.ResponseWriter, r *http.Req
|
||||
// Check: last path
|
||||
if len(s.settings.GetStoragePaths()) <= 1 {
|
||||
data["StorageError"] = "Az utolsó adattároló nem törölhető."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.settings.RemoveStoragePath(path); err != nil {
|
||||
data["StorageError"] = "Hiba a törlés során."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1645,17 +1630,17 @@ func (s *Server) settingsStorageLabelHandler(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
if label == "" || len(label) > 50 {
|
||||
data := s.settingsData()
|
||||
data := s.storagePageData()
|
||||
data["StorageError"] = "A megnevezés nem lehet üres és legfeljebb 50 karakter."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.settings.SetStorageLabel(path, label); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to set storage label: %v", err)
|
||||
data := s.settingsData()
|
||||
data := s.storagePageData()
|
||||
data["StorageError"] = "Hiba a megnevezés mentésekor."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
s.executeTemplate(w, r, "storage", data)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user