Files
OwnCord/Server/admin/helpers.go
T
jevb 28f33644de fix: address remaining code review findings (C-3, H-5, H-6, M-1 through M-16)
- C-3: inject setupLimiter into NewAdminAPI instead of package-level global
- H-5: generateRandomKey returns error instead of panicking
- H-6: replace init() bcrypt with sync.Once lazy initialization
- M-2: remove unsafe-inline from admin CSP script-src and style-src
- M-3: sanitize upload filenames (strip control chars, truncate to 255)
- M-5: truncate User-Agent to 512 bytes before storing as device
- M-10: MaxBodySizeUnless uses prefix matching instead of exact path
- M-12: wrap seedExistingDatabase in a single transaction
- M-13: use errors.Is for EOF check in upload handler
- M-14: log writeJSON encoding errors instead of discarding
- M-16: standardize error codes to INTERNAL_ERROR across all handlers
2026-03-31 19:00:17 +02:00

66 lines
1.8 KiB
Go

package admin
import (
"encoding/json"
"log/slog"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/owncord/server/db"
)
// ─── Helpers ──────────────────────────────────────────────────────────────────
type errorResponse struct {
Error string `json:"error"`
Message string `json:"message"`
}
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(v); err != nil {
slog.Error("writeJSON: failed to encode response", "error", err)
}
}
func writeErr(w http.ResponseWriter, status int, code, msg string) {
writeJSON(w, status, errorResponse{Error: code, Message: msg})
}
func pathInt64(r *http.Request, param string) (int64, error) {
raw := chi.URLParam(r, param)
return strconv.ParseInt(raw, 10, 64)
}
// queryInt parses an integer query parameter with a minimum and maximum bound.
// Use minVal=1 for limit parameters, minVal=0 for offset parameters.
func queryInt(r *http.Request, key string, defaultVal, minVal int) int {
raw := r.URL.Query().Get(key)
if raw == "" {
return defaultVal
}
n, err := strconv.Atoi(raw)
if err != nil || n < minVal {
return defaultVal
}
// Cap to prevent unbounded result sets exhausting memory.
const maxLimit = 500
if n > maxLimit {
return maxLimit
}
return n
}
// actorFromContext returns the authenticated user's ID stored in the request
// context by adminAuthMiddleware. Returns 0 if called outside that middleware
// (should not happen in production).
func actorFromContext(r *http.Request) int64 {
user, ok := r.Context().Value(adminUserKey).(*db.User)
if !ok || user == nil {
return 0
}
return user.ID
}