package channel import ( "encoding/json" "net/http" "git.dustin.coffee/hobokenchicken/dumpsterChat/internal/middleware" "git.dustin.coffee/hobokenchicken/dumpsterChat/internal/permissions" "github.com/go-chi/chi/v5" ) type overrideRequest struct { Allow int64 `json:"allow"` Deny int64 `json:"deny"` } type overrideResponse struct { ID string `json:"id"` ChannelID string `json:"channel_id"` TargetType string `json:"target_type"` TargetID string `json:"target_id"` Allow int64 `json:"allow_bitflags"` Deny int64 `json:"deny_bitflags"` } func (h *Handler) registerOverrideRoutes(r chi.Router) { r.Put("/{channelID}/permissions/{targetType}/{targetID}", h.SetOverride) r.Get("/{channelID}/permissions", h.ListOverrides) r.Delete("/{channelID}/permissions/{targetType}/{targetID}", h.DeleteOverride) } func (h *Handler) requireChannelManager(w http.ResponseWriter, r *http.Request, channelID string) (userID, serverID string, ok bool) { userID, authOK := middleware.UserIDFromContext(r.Context()) if !authOK { http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized) return "", "", false } serverID, err := h.serverIDForChannel(r.Context(), channelID) if err != nil { http.Error(w, `{"error":"channel not found"}`, http.StatusNotFound) return "", "", false } allowed, err := h.checker.CheckPermission(r.Context(), serverID, userID, permissions.MANAGE_CHANNELS) if err != nil || !allowed { http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden) return "", "", false } return userID, serverID, true } func (h *Handler) SetOverride(w http.ResponseWriter, r *http.Request) { channelID := chi.URLParam(r, "channelID") _, _, ok := h.requireChannelManager(w, r, channelID) if !ok { return } targetType := chi.URLParam(r, "targetType") targetID := chi.URLParam(r, "targetID") if targetType != "role" && targetType != "user" { http.Error(w, `{"error":"invalid target_type"}`, http.StatusBadRequest) return } var req overrideRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) return } var res overrideResponse err := h.db.QueryRowContext(r.Context(), ` INSERT INTO channel_overrides (channel_id, target_type, target_id, allow_bitflags, deny_bitflags) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (channel_id, target_type, target_id) DO UPDATE SET allow_bitflags = EXCLUDED.allow_bitflags, deny_bitflags = EXCLUDED.deny_bitflags, updated_at = NOW() RETURNING id, channel_id, target_type, target_id, allow_bitflags, deny_bitflags `, channelID, targetType, targetID, req.Allow, req.Deny).Scan(&res.ID, &res.ChannelID, &res.TargetType, &res.TargetID, &res.Allow, &res.Deny) if err != nil { http.Error(w, `{"error":"failed to set override"}`, http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(res) } func (h *Handler) ListOverrides(w http.ResponseWriter, r *http.Request) { channelID := chi.URLParam(r, "channelID") _, _, ok := h.requireChannelManager(w, r, channelID) if !ok { return } rows, err := h.db.QueryContext(r.Context(), ` SELECT id, channel_id, target_type, target_id, allow_bitflags, deny_bitflags FROM channel_overrides WHERE channel_id = $1 ORDER BY target_type, target_id `, channelID) if err != nil { http.Error(w, `{"error":"server error"}`, http.StatusInternalServerError) return } defer rows.Close() overrides := make([]overrideResponse, 0) for rows.Next() { var o overrideResponse if err := rows.Scan(&o.ID, &o.ChannelID, &o.TargetType, &o.TargetID, &o.Allow, &o.Deny); err != nil { continue } overrides = append(overrides, o) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(overrides) } func (h *Handler) DeleteOverride(w http.ResponseWriter, r *http.Request) { channelID := chi.URLParam(r, "channelID") _, _, ok := h.requireChannelManager(w, r, channelID) if !ok { return } targetType := chi.URLParam(r, "targetType") targetID := chi.URLParam(r, "targetID") _, err := h.db.ExecContext(r.Context(), ` DELETE FROM channel_overrides WHERE channel_id = $1 AND target_type = $2 AND target_id = $3 `, channelID, targetType, targetID) if err != nil { http.Error(w, `{"error":"failed to delete override"}`, http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }