feat(admin): add scan settings + system information to settings page

- Scanning section: auto-scan toggle (on/off) and poll interval input
  with HTMX form that updates system_settings table
- System Information section: surfaces all hardcoded constants
  (session duration, password policy, rate limits, worker pool,
  sync queue, tombstone TTL, OPDS page size, CORS, etc.)
- New ScanSettingsData type, ScanSettingsSection partial template
- New HTMX endpoint: PUT /admin/settings/scan
- registerAdminSettingsRoutes for scan settings HTMX CRUD
- Fix bookhoord→bookhoard import typo in admin_library.go
This commit is contained in:
2026-08-07 23:37:14 -04:00
parent a42232e67a
commit 64ad10b3f2
6 changed files with 267 additions and 12 deletions
+37
View File
@@ -10,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/google/uuid"
@@ -378,3 +379,39 @@ func getAdminStats(ctx context.Context, cfg *Config) templates.AdminStats {
return stats
}
func registerAdminSettingsRoutes(cfg *Config, frontendProtected *echo.Group) {
g := frontendProtected.Group("", handlers.AdminMiddleware)
g.PUT("/admin/settings/scan", func(c *echo.Context) error {
ctx := c.Request().Context()
autoScan := c.FormValue("auto_scan_enabled") == "true"
intervalStr := c.FormValue("scan_poll_interval_seconds")
interval, err := strconv.Atoi(intervalStr)
if err != nil || interval < 1 || interval > 3600 {
return c.HTML(http.StatusBadRequest, `<div class="text-sm" style="color: var(--status-danger);">Interval must be between 1 and 3600 seconds</div>`)
}
autoScanStr := "false"
if autoScan {
autoScanStr = "true"
}
_ = cfg.Queries.UpdateSystemSetting(ctx, database.UpdateSystemSettingParams{
SettingKey: "auto_scan_enabled",
SettingValue: autoScanStr,
})
_ = cfg.Queries.UpdateSystemSetting(ctx, database.UpdateSystemSettingParams{
SettingKey: "scan_poll_interval_seconds",
SettingValue: strconv.Itoa(interval),
})
scanSettings := templates.ScanSettingsData{
AutoScanEnabled: autoScan,
ScanPollIntervalSeconds: interval,
}
var buf bytes.Buffer
_ = templates.ScanSettingsSection(scanSettings).Render(ctx, &buf)
return c.HTML(http.StatusOK, buf.String())
})
}
+21 -3
View File
@@ -995,21 +995,37 @@ func registerFrontendRoutes(cfg *Config) {
return renderErrorPage(c, "Error loading user", "user_load_error")
}
ctx := c.Request().Context()
// Fetch current system configuration - just base_url
baseURL := cfg.getBaseURL(c.Request().Context())
baseURL := cfg.getBaseURL(ctx)
systemConfig := map[string]string{
"base_url": baseURL,
"default_timezone": "UTC",
}
defaultTimezone, err := cfg.Queries.GetSystemTimezone(c.Request().Context())
defaultTimezone, err := cfg.Queries.GetSystemTimezone(ctx)
if err == nil && defaultTimezone != "" {
systemConfig["default_timezone"] = defaultTimezone
}
// Fetch scan settings
scanSettings := templates.ScanSettingsData{
AutoScanEnabled: true,
ScanPollIntervalSeconds: 60,
}
if val, err := cfg.Queries.GetSystemSetting(ctx, "auto_scan_enabled"); err == nil {
scanSettings.AutoScanEnabled = val == "true"
}
if val, err := cfg.Queries.GetSystemSetting(ctx, "scan_poll_interval_seconds"); err == nil {
if n, err := strconv.Atoi(val); err == nil {
scanSettings.ScanPollIntervalSeconds = n
}
}
var buf bytes.Buffer
err = templates.AdminSettings(user, systemConfig, "").Render(c.Request().Context(), &buf)
err = templates.AdminSettings(user, systemConfig, scanSettings, "").Render(ctx, &buf)
if err != nil {
return err
}
@@ -1053,6 +1069,8 @@ func registerFrontendRoutes(cfg *Config) {
// Admin library HTMX endpoints
registerAdminLibraryRoutes(cfg, frontendProtected)
// Admin settings HTMX endpoints
registerAdminSettingsRoutes(cfg, frontendProtected)
// ============================================================================
// LEGACY API ROUTES (for backward compatibility)