mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
- 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
66 lines
1.8 KiB
Go
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
|
|
}
|