fix: address code review findings for Phase 7-8

Bugs fixed:
- Renderer.Error set WriteHeader before Content-Type, causing
  the header to be silently dropped. Now sets Content-Type first.
- truncate template function operated on bytes, not runes — could
  split multi-byte UTF-8 characters (Norwegian æøå). Now uses
  []rune for correct Unicode handling.

Performance:
- Skip session DB lookup (2 queries) on /static/ and /uploads/
  requests — these never use user context.

UX consistency:
- Replace all http.NotFound and http.Error("Forbidden") in
  handler layer with styled error pages via Renderer.Error.
- Add notFound/forbidden helper methods on Handler.

Deployment fixes:
- Remove false libc6/glibc deps from nfpm.yaml (binary is
  statically linked with CGO_ENABLED=0).
- Add CGO_ENABLED=0 to Makefile build target for consistency.
- Add .dockerignore to exclude .git, dist/, data/ from build
  context.
- Remove phantom 'lint' from Makefile .PHONY.
- Document ProtectSystem=strict constraint in systemd service.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-29 16:39:10 +02:00
commit aa5ab6b415
9 changed files with 73 additions and 32 deletions

View file

@ -125,14 +125,14 @@ func (h *Handler) handleFaveCreate(w http.ResponseWriter, r *http.Request) {
func (h *Handler) handleFaveDetail(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
http.NotFound(w, r)
h.notFound(w, r)
return
}
fave, err := h.deps.Faves.GetByID(id)
if err != nil {
if errors.Is(err, store.ErrFaveNotFound) {
http.NotFound(w, r)
h.notFound(w, r)
return
}
slog.Error("get fave error", "error", err)
@ -143,7 +143,7 @@ func (h *Handler) handleFaveDetail(w http.ResponseWriter, r *http.Request) {
// Check access: private faves are only visible to their owner.
user := middleware.UserFromContext(r.Context())
if fave.Privacy == "private" && (user == nil || user.ID != fave.UserID) {
http.NotFound(w, r)
h.notFound(w, r)
return
}
@ -169,14 +169,14 @@ func (h *Handler) handleFaveEdit(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
http.NotFound(w, r)
h.notFound(w, r)
return
}
fave, err := h.deps.Faves.GetByID(id)
if err != nil {
if errors.Is(err, store.ErrFaveNotFound) {
http.NotFound(w, r)
h.notFound(w, r)
return
}
slog.Error("get fave error", "error", err)
@ -186,7 +186,7 @@ func (h *Handler) handleFaveEdit(w http.ResponseWriter, r *http.Request) {
// Only the owner can edit.
if user.ID != fave.UserID {
http.Error(w, "Forbidden", http.StatusForbidden)
h.forbidden(w, r)
return
}
@ -217,14 +217,14 @@ func (h *Handler) handleFaveUpdate(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
http.NotFound(w, r)
h.notFound(w, r)
return
}
fave, err := h.deps.Faves.GetByID(id)
if err != nil {
if errors.Is(err, store.ErrFaveNotFound) {
http.NotFound(w, r)
h.notFound(w, r)
return
}
slog.Error("get fave error", "error", err)
@ -233,7 +233,7 @@ func (h *Handler) handleFaveUpdate(w http.ResponseWriter, r *http.Request) {
}
if user.ID != fave.UserID {
http.Error(w, "Forbidden", http.StatusForbidden)
h.forbidden(w, r)
return
}
@ -304,14 +304,14 @@ func (h *Handler) handleFaveDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
http.NotFound(w, r)
h.notFound(w, r)
return
}
fave, err := h.deps.Faves.GetByID(id)
if err != nil {
if errors.Is(err, store.ErrFaveNotFound) {
http.NotFound(w, r)
h.notFound(w, r)
return
}
slog.Error("get fave error", "error", err)
@ -320,7 +320,7 @@ func (h *Handler) handleFaveDelete(w http.ResponseWriter, r *http.Request) {
}
if user.ID != fave.UserID {
http.Error(w, "Forbidden", http.StatusForbidden)
h.forbidden(w, r)
return
}

View file

@ -144,7 +144,12 @@ func (h *Handler) Routes() *http.ServeMux {
return mux
}
// handleNotFound renders a styled 404 page.
func (h *Handler) handleNotFound(w http.ResponseWriter, r *http.Request) {
// notFound renders a styled 404 error page.
func (h *Handler) notFound(w http.ResponseWriter, r *http.Request) {
h.deps.Renderer.Error(w, r, http.StatusNotFound, "Ikke funnet")
}
// forbidden renders a styled 403 error page.
func (h *Handler) forbidden(w http.ResponseWriter, r *http.Request) {
h.deps.Renderer.Error(w, r, http.StatusForbidden, "Ingen tilgang")
}

View file

@ -22,7 +22,7 @@ func (h *Handler) handlePublicProfile(w http.ResponseWriter, r *http.Request) {
profileUser, err := h.deps.Users.GetByUsername(username)
if err != nil {
if errors.Is(err, store.ErrUserNotFound) {
http.NotFound(w, r)
h.notFound(w, r)
return
}
slog.Error("get user error", "error", err)
@ -31,7 +31,7 @@ func (h *Handler) handlePublicProfile(w http.ResponseWriter, r *http.Request) {
}
if profileUser.Disabled {
http.NotFound(w, r)
h.notFound(w, r)
return
}