package policy import ( "encoding/json" "net/http" "github.com/go-chi/chi/v5" "github.com/google/uuid" "nexavpn/backend/internal/apiutil" "nexavpn/backend/internal/audit" "nexavpn/backend/internal/requestctx" ) type Handler struct { service *Service audit *audit.Service } func NewHandler(service *Service, auditService *audit.Service) *Handler { return &Handler{service: service, audit: auditService} } func (h *Handler) List(w http.ResponseWriter, r *http.Request) { items, err := h.service.List(r.Context()) if err != nil { apiutil.Error(w, http.StatusInternalServerError, "policies_list_failed", "unable to list policies") return } apiutil.JSON(w, http.StatusOK, items) } func (h *Handler) Create(w http.ResponseWriter, r *http.Request) { var input CreateRequest if err := json.NewDecoder(r.Body).Decode(&input); err != nil { apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body") return } claims, ok := requestctx.ClaimsFromContext(r.Context()) if !ok { apiutil.Error(w, http.StatusUnauthorized, "unauthorized", "missing auth claims") return } item, err := h.service.Create(r.Context(), claims.UserID, input) if err != nil { apiutil.Error(w, http.StatusInternalServerError, "policy_create_failed", "unable to create policy") return } _ = h.audit.Record(r.Context(), audit.Entry{ ActorUserID: &claims.UserID, EntityType: "policy", EntityID: &item.ID, EventType: "admin.policy.created", Status: "success", Message: "admin created policy", Metadata: map[string]any{ "name": item.Name, }, }) apiutil.JSON(w, http.StatusCreated, item) } func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { policyID, err := uuid.Parse(chi.URLParam(r, "id")) if err != nil { apiutil.Error(w, http.StatusBadRequest, "invalid_policy_id", "invalid policy id") return } var input UpdateRequest if err := json.NewDecoder(r.Body).Decode(&input); err != nil { apiutil.Error(w, http.StatusBadRequest, "invalid_json", "invalid request body") return } item, err := h.service.Update(r.Context(), policyID, input) if err != nil { apiutil.Error(w, http.StatusInternalServerError, "policy_update_failed", "unable to update policy") return } if claims, ok := requestctx.ClaimsFromContext(r.Context()); ok { _ = h.audit.Record(r.Context(), audit.Entry{ ActorUserID: &claims.UserID, EntityType: "policy", EntityID: &policyID, EventType: "admin.policy.updated", Status: "success", Message: "admin updated policy", }) } apiutil.JSON(w, http.StatusOK, item) } func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { policyID, err := uuid.Parse(chi.URLParam(r, "id")) if err != nil { apiutil.Error(w, http.StatusBadRequest, "invalid_policy_id", "invalid policy id") return } if err := h.service.Delete(r.Context(), policyID); err != nil { apiutil.Error(w, http.StatusInternalServerError, "policy_delete_failed", "unable to delete policy") return } if claims, ok := requestctx.ClaimsFromContext(r.Context()); ok { _ = h.audit.Record(r.Context(), audit.Entry{ ActorUserID: &claims.UserID, EntityType: "policy", EntityID: &policyID, EventType: "admin.policy.deleted", Status: "success", Message: "admin deleted policy", }) } apiutil.JSON(w, http.StatusOK, map[string]any{"ok": true}) }