From 65534ecfd5bf284f993f10b047a68f091aa971d3 Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Fri, 16 Oct 2026 01:06:20 +0300 Subject: [PATCH] Mutlifix --- Dockerfile | 10 +++++----- docker-compose.yml | 5 +++-- internal/web/auth.go | 2 -- internal/web/service.go | 20 ++++++++++++-------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index f877b4e..50a003e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,16 +44,16 @@ RUN chmod +x /app/server COPY --from=builder /build/static /app/static COPY --from=builder /build/templates /app/templates -# Ensure the app user owns the working dir (DB file created here at runtime) +# Ensure the app user owns the working dir RUN chown -R app:app /app -# Create a dedicated subdirectory for persistable data (mounted as a volume) -RUN mkdir -p /app/data +# Create data directory owned by app user (for named volume mount) +RUN mkdir -p /data && chown app:app /data USER app # Expose the HTTP port EXPOSE 8080 -# Default command -CMD ["/app/server", "-addr", ":8080", "-db", "/app/data/state.db"] +# Default command (runs as 'app' user, DB stored in named volume) +CMD ["/app/server", "-addr", ":8080", "-db", "/data/state.db"] diff --git a/docker-compose.yml b/docker-compose.yml index 955c998..fe5b5b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,8 +8,7 @@ services: ports: - "8080:8080" volumes: - # Bind mount only the data subdirectory, so /app/server and assets are NOT overwritten - - ./data:/app/data + - uportal_data:/data environment: LOG_LEVEL: info # Optional configuration (uncomment as needed): @@ -20,3 +19,5 @@ services: # TRUSTED_DOMAINS: example.com restart: unless-stopped +volumes: + uportal_data: diff --git a/internal/web/auth.go b/internal/web/auth.go index 92f60c1..9f1f34e 100644 --- a/internal/web/auth.go +++ b/internal/web/auth.go @@ -274,8 +274,6 @@ func (s *WebService) HandleExternalRedirect(w http.ResponseWriter, r *http.Reque if !ok { // Not authenticated is fine — just show the warning without nav data = &ViewData{Title: "External Redirect"} - token, _ := auth.SetCsrfCookie(w, r, CsrfCookieName) - data.FormToken = token } data.Redirect = target diff --git a/internal/web/service.go b/internal/web/service.go index 4de15e0..6642e91 100644 --- a/internal/web/service.go +++ b/internal/web/service.go @@ -185,17 +185,21 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) { lrw.ResponseWriter.WriteHeader(code) } -// render mints a CSRF token, fills ViewData defaults, and writes the response. +// render fills ViewData defaults and writes the response. +// If data.FormToken is empty (e.g. for call sites that don't use +// basicViewData), a CSRF token is minted as a fallback. func (s *WebService) render(w http.ResponseWriter, r *http.Request, page string, data *ViewData) { - token, err := auth.SetCsrfCookie(w, r, CsrfCookieName) - if err != nil { - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } if data == nil { data = &ViewData{} } - data.FormToken = token + if data.FormToken == "" { + token, err := auth.SetCsrfCookie(w, r, CsrfCookieName) + if err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + data.FormToken = token + } if data.Title == "" { data.Title = page } @@ -228,7 +232,7 @@ func (s *WebService) currentUser(r *http.Request) (*user.User, error) { return s.getUserInfo(r) } -// basicViewData builds a ViewData with the current user and a CSRF token. +// basicViewData builds a ViewData with the current user and CSRF token. func (s *WebService) basicViewData(w http.ResponseWriter, r *http.Request, title string) (*ViewData, bool) { user, err := s.getUserInfo(r) if err != nil {