package main import ( "encoding/json" "errors" "net/http" "github.com/gorilla/mux" "github.com/mattermost/mattermost/server/public/plugin" ) type cleanupRequest struct { ChannelID string `json:"channel_id"` } type cleanupResponse struct { Deleted int `json:"deleted"` } type autoCleanupRequest struct { ChannelID string `json:"channel_id"` OffsetDays int `json:"offset_days"` } type autoCleanupResponse struct { ChannelID string `json:"channel_id"` OffsetDays int `json:"offset_days"` Enabled bool `json:"enabled"` } func (p *Plugin) initRouter() *mux.Router { router := mux.NewRouter() router.Use(p.requireLoggedInUser) apiRouter := router.PathPrefix("/api/v1").Subrouter() apiRouter.HandleFunc("/cleanup", p.handleCleanup).Methods(http.MethodPost) apiRouter.HandleFunc("/auto-cleanup/run", p.handleRunAutoCleanup).Methods(http.MethodPost) apiRouter.HandleFunc("/auto-cleanup", p.handleSetAutoCleanup).Methods(http.MethodPut) apiRouter.HandleFunc("/auto-cleanup/{channel_id}", p.handleGetAutoCleanup).Methods(http.MethodGet) apiRouter.HandleFunc("/auto-cleanup/{channel_id}", p.handleDeleteAutoCleanup).Methods(http.MethodDelete) return router } func (p *Plugin) ServeHTTP(_ *plugin.Context, w http.ResponseWriter, r *http.Request) { p.router.ServeHTTP(w, r) } func (p *Plugin) requireLoggedInUser(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Mattermost-User-ID") == "" { http.Error(w, "Not authorized", http.StatusUnauthorized) return } next.ServeHTTP(w, r) }) } func (p *Plugin) handleCleanup(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("Mattermost-User-ID") var req cleanupRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.ChannelID == "" { http.Error(w, "channel_id is required", http.StatusBadRequest) return } deleted, err := p.cleanupChannel(userID, req.ChannelID) if err != nil { if errors.Is(err, errPermissionDenied) { http.Error(w, err.Error(), http.StatusForbidden) return } p.client.Log.Error("Failed to cleanup channel", "channel_id", req.ChannelID, "error", err) http.Error(w, "Failed to cleanup channel", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(cleanupResponse{Deleted: deleted}); err != nil { p.client.Log.Error("Failed to encode cleanup response", "error", err) } } type autoCleanupRunRequest struct { ChannelID string `json:"channel_id"` OffsetDays int `json:"offset_days,omitempty"` } func (p *Plugin) handleRunAutoCleanup(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("Mattermost-User-ID") if !p.isSystemAdmin(userID) { http.Error(w, errPermissionDenied.Error(), http.StatusForbidden) return } var req autoCleanupRunRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.ChannelID == "" { http.Error(w, "channel_id is required", http.StatusBadRequest) return } offsetDays := req.OffsetDays if offsetDays <= 0 { config, err := p.autoCleanupStore.Get(req.ChannelID) if err != nil { p.client.Log.Error("Failed to get auto cleanup config", "channel_id", req.ChannelID, "error", err) http.Error(w, "Failed to get auto cleanup config", http.StatusInternalServerError) return } if config == nil { http.Error(w, "automatic cleanup is not configured for this channel", http.StatusBadRequest) return } offsetDays = config.OffsetDays } deleted, err := p.autoCleanupChannel(req.ChannelID, offsetDays) if err != nil { p.client.Log.Error("Failed to run auto cleanup", "channel_id", req.ChannelID, "error", err) http.Error(w, "Failed to run auto cleanup", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(cleanupResponse{Deleted: deleted}); err != nil { p.client.Log.Error("Failed to encode auto cleanup run response", "error", err) } } func (p *Plugin) handleGetAutoCleanup(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("Mattermost-User-ID") if !p.isSystemAdmin(userID) { http.Error(w, errPermissionDenied.Error(), http.StatusForbidden) return } channelID := mux.Vars(r)["channel_id"] if channelID == "" { http.Error(w, "channel_id is required", http.StatusBadRequest) return } config, err := p.autoCleanupStore.Get(channelID) if err != nil { p.client.Log.Error("Failed to get auto cleanup config", "channel_id", channelID, "error", err) http.Error(w, "Failed to get auto cleanup config", http.StatusInternalServerError) return } response := autoCleanupResponse{ ChannelID: channelID, Enabled: config != nil, } if config != nil { response.OffsetDays = config.OffsetDays } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { p.client.Log.Error("Failed to encode auto cleanup response", "error", err) } } func (p *Plugin) handleSetAutoCleanup(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("Mattermost-User-ID") if !p.isSystemAdmin(userID) { http.Error(w, errPermissionDenied.Error(), http.StatusForbidden) return } var req autoCleanupRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.ChannelID == "" { http.Error(w, "channel_id is required", http.StatusBadRequest) return } if req.OffsetDays <= 0 { http.Error(w, "offset_days must be greater than zero", http.StatusBadRequest) return } if _, err := p.client.Channel.Get(req.ChannelID); err != nil { http.Error(w, "channel not found", http.StatusNotFound) return } if err := p.autoCleanupStore.Set(req.ChannelID, req.OffsetDays); err != nil { p.client.Log.Error("Failed to set auto cleanup config", "channel_id", req.ChannelID, "error", err) http.Error(w, "Failed to set auto cleanup config", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(autoCleanupResponse{ ChannelID: req.ChannelID, OffsetDays: req.OffsetDays, Enabled: true, }); err != nil { p.client.Log.Error("Failed to encode auto cleanup response", "error", err) } } func (p *Plugin) handleDeleteAutoCleanup(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("Mattermost-User-ID") if !p.isSystemAdmin(userID) { http.Error(w, errPermissionDenied.Error(), http.StatusForbidden) return } channelID := mux.Vars(r)["channel_id"] if channelID == "" { http.Error(w, "channel_id is required", http.StatusBadRequest) return } if err := p.autoCleanupStore.Delete(channelID); err != nil { p.client.Log.Error("Failed to delete auto cleanup config", "channel_id", channelID, "error", err) http.Error(w, "Failed to delete auto cleanup config", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(autoCleanupResponse{ ChannelID: channelID, Enabled: false, }); err != nil { p.client.Log.Error("Failed to encode auto cleanup response", "error", err) } }