diff --git a/internal/router/frontend.go b/internal/router/frontend.go index 2f19b97..adb3acd 100644 --- a/internal/router/frontend.go +++ b/internal/router/frontend.go @@ -7,6 +7,7 @@ import ( "net/http" "time" + "bookhoard/internal/config" "bookhoard/internal/database" "bookhoard/internal/handlers" "bookhoard/internal/services" @@ -530,8 +531,14 @@ func registerFrontendRoutes(cfg *Config) { pendingList = convertPending(pendingMaps) } + // Get base URL from database config with fallback to config/env var + baseURL := config.GetBaseURL(c.Request().Context(), cfg.Queries) + if baseURL == "" { + baseURL = cfg.Cfg.BaseURL + } + var buf bytes.Buffer - err = templates.Devices(user, devices, pendingList, errorMsg, cfg.Cfg.BaseURL).Render(c.Request().Context(), &buf) + err = templates.Devices(user, devices, pendingList, errorMsg, baseURL).Render(c.Request().Context(), &buf) if err != nil { return err } @@ -723,6 +730,30 @@ func registerFrontendRoutes(cfg *Config) { })) // Admin: Get profile modal for editing user + frontendProtected.GET("/admin/settings", handlers.AdminMiddleware(func(c *echo.Context) error { + user, err := getTemplateUserWithTheme(c, cfg) + if err != nil { + return renderErrorPage(c, "Error loading user", "user_load_error") + } + + // Fetch current system configuration - just base_url + baseURL := config.GetBaseURL(c.Request().Context(), cfg.Queries) + if baseURL == "" { + baseURL = cfg.Cfg.BaseURL + } + + systemConfig := map[string]string{ + "base_url": baseURL, + } + + var buf bytes.Buffer + err = templates.AdminSettings(user, systemConfig, "").Render(c.Request().Context(), &buf) + if err != nil { + return err + } + return c.HTML(http.StatusOK, buf.String()) + })) + frontendProtected.GET("/admin/users/:id/profile-modal", handlers.AdminMiddleware(func(c *echo.Context) error { // Get target user ID from URL targetUserID := c.Param("id") @@ -790,8 +821,14 @@ func registerFrontendRoutes(cfg *Config) { pendingList = convertPending(pendingMaps) } + // Get base URL from database config with fallback to config/env var + baseURL := config.GetBaseURL(c.Request().Context(), cfg.Queries) + if baseURL == "" { + baseURL = cfg.Cfg.BaseURL + } + var buf bytes.Buffer - err = templates.Devices(user, devices, pendingList, errorMsg, cfg.Cfg.BaseURL).Render(c.Request().Context(), &buf) + err = templates.Devices(user, devices, pendingList, errorMsg, baseURL).Render(c.Request().Context(), &buf) if err != nil { return err } diff --git a/templates/admin_settings.templ b/templates/admin_settings.templ new file mode 100644 index 0000000..aaa0c7f --- /dev/null +++ b/templates/admin_settings.templ @@ -0,0 +1,74 @@ +package templates + +templ AdminSettings(user User, systemConfig map[string]string, errorMessage string) { + + + + + System Settings - Bookhoard + + + + + + @Header(user, "/admin/settings") +
+ @AdminSidebar(user, "/admin/settings") +
+
+
+ +

System Settings

+

Configure your Bookhoard instance

+
+ + if errorMessage != "" { +
+ { errorMessage } +
+ } + +
+
+

Base URL

+ +
+ + +

The public URL of your Bookhoard instance (e.g., https://books.example.com). Used for device sync, OPDS, and API endpoints.

+
+ +
+ +
+
+
+ +
+

URL Paths

+
+

OPDS: { systemConfig["base_url"] }/opds

+

API: { systemConfig["base_url"] }/api

+

Device Sync: { systemConfig["base_url"] }/api/sync

+
+
+
+
+
+ + +} diff --git a/templates/admin_settings_templ.go b/templates/admin_settings_templ.go new file mode 100644 index 0000000..0b53ab0 --- /dev/null +++ b/templates/admin_settings_templ.go @@ -0,0 +1,131 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.1001 +package templates + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +func AdminSettings(user User, systemConfig map[string]string, errorMessage string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "System Settings - Bookhoard") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = Header(user, "/admin/settings").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = AdminSidebar(user, "/admin/settings").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

System Settings

Configure your Bookhoard instance

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if errorMessage != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 string + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_settings.templ`, Line: 31, Col: 22} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

Base URL

The public URL of your Bookhoard instance (e.g., https://books.example.com). Used for device sync, OPDS, and API endpoints.

URL Paths

OPDS: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(systemConfig["base_url"]) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_settings.templ`, Line: 64, Col: 60} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "/opds

API: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(systemConfig["base_url"]) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_settings.templ`, Line: 65, Col: 59} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "/api

Device Sync: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(systemConfig["base_url"]) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `admin_settings.templ`, Line: 66, Col: 67} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "/api/sync

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/templates/admin_sidebar.templ b/templates/admin_sidebar.templ index ce92cbe..fff21e9 100644 --- a/templates/admin_sidebar.templ +++ b/templates/admin_sidebar.templ @@ -14,6 +14,9 @@ templ AdminSidebar(user User, currentPath string) { 📚 Library Management + + ⚙️ System Settings +