package server import ( "errors" "fmt" "net/http" "time" "git.wntrmute.dev/mc/mcias/internal/db" "git.wntrmute.dev/mc/mcias/internal/middleware" "git.wntrmute.dev/mc/mcias/internal/model" ) type ssoClientResponse struct { ClientID string `json:"client_id"` RedirectURI string `json:"redirect_uri"` Tags []string `json:"tags"` Enabled bool `json:"enabled"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } func ssoClientToResponse(c *model.SSOClient) ssoClientResponse { return ssoClientResponse{ ClientID: c.ClientID, RedirectURI: c.RedirectURI, Tags: c.Tags, Enabled: c.Enabled, CreatedAt: c.CreatedAt.Format(time.RFC3339), UpdatedAt: c.UpdatedAt.Format(time.RFC3339), } } func (s *Server) handleListSSOClients(w http.ResponseWriter, r *http.Request) { clients, err := s.db.ListSSOClients() if err != nil { middleware.WriteError(w, http.StatusInternalServerError, "internal error", "internal_error") return } resp := make([]ssoClientResponse, 0, len(clients)) for _, c := range clients { resp = append(resp, ssoClientToResponse(c)) } writeJSON(w, http.StatusOK, resp) } type createSSOClientRequest struct { ClientID string `json:"client_id"` RedirectURI string `json:"redirect_uri"` Tags []string `json:"tags"` } func (s *Server) handleCreateSSOClient(w http.ResponseWriter, r *http.Request) { var req createSSOClientRequest if !decodeJSON(w, r, &req) { return } if req.ClientID == "" { middleware.WriteError(w, http.StatusBadRequest, "client_id is required", "bad_request") return } if req.RedirectURI == "" { middleware.WriteError(w, http.StatusBadRequest, "redirect_uri is required", "bad_request") return } claims := middleware.ClaimsFromContext(r.Context()) var createdBy *int64 if claims != nil { if actor, err := s.db.GetAccountByUUID(claims.Subject); err == nil { createdBy = &actor.ID } } c, err := s.db.CreateSSOClient(req.ClientID, req.RedirectURI, req.Tags, createdBy) if err != nil { s.logger.Error("create SSO client", "error", err) middleware.WriteError(w, http.StatusBadRequest, err.Error(), "bad_request") return } s.writeAudit(r, model.EventSSOClientCreated, createdBy, nil, fmt.Sprintf(`{"client_id":%q}`, c.ClientID)) writeJSON(w, http.StatusCreated, ssoClientToResponse(c)) } func (s *Server) handleGetSSOClient(w http.ResponseWriter, r *http.Request) { clientID := r.PathValue("clientId") c, err := s.db.GetSSOClient(clientID) if errors.Is(err, db.ErrNotFound) { middleware.WriteError(w, http.StatusNotFound, "SSO client not found", "not_found") return } if err != nil { middleware.WriteError(w, http.StatusInternalServerError, "internal error", "internal_error") return } writeJSON(w, http.StatusOK, ssoClientToResponse(c)) } type updateSSOClientRequest struct { RedirectURI *string `json:"redirect_uri,omitempty"` Tags *[]string `json:"tags,omitempty"` Enabled *bool `json:"enabled,omitempty"` } func (s *Server) handleUpdateSSOClient(w http.ResponseWriter, r *http.Request) { clientID := r.PathValue("clientId") var req updateSSOClientRequest if !decodeJSON(w, r, &req) { return } err := s.db.UpdateSSOClient(clientID, req.RedirectURI, req.Tags, req.Enabled) if errors.Is(err, db.ErrNotFound) { middleware.WriteError(w, http.StatusNotFound, "SSO client not found", "not_found") return } if err != nil { s.logger.Error("update SSO client", "error", err) middleware.WriteError(w, http.StatusBadRequest, err.Error(), "bad_request") return } claims := middleware.ClaimsFromContext(r.Context()) var actorID *int64 if claims != nil { if actor, err := s.db.GetAccountByUUID(claims.Subject); err == nil { actorID = &actor.ID } } s.writeAudit(r, model.EventSSOClientUpdated, actorID, nil, fmt.Sprintf(`{"client_id":%q}`, clientID)) c, err := s.db.GetSSOClient(clientID) if err != nil { middleware.WriteError(w, http.StatusInternalServerError, "internal error", "internal_error") return } writeJSON(w, http.StatusOK, ssoClientToResponse(c)) } func (s *Server) handleDeleteSSOClient(w http.ResponseWriter, r *http.Request) { clientID := r.PathValue("clientId") err := s.db.DeleteSSOClient(clientID) if errors.Is(err, db.ErrNotFound) { middleware.WriteError(w, http.StatusNotFound, "SSO client not found", "not_found") return } if err != nil { middleware.WriteError(w, http.StatusInternalServerError, "internal error", "internal_error") return } claims := middleware.ClaimsFromContext(r.Context()) var actorID *int64 if claims != nil { if actor, err := s.db.GetAccountByUUID(claims.Subject); err == nil { actorID = &actor.ID } } s.writeAudit(r, model.EventSSOClientDeleted, actorID, nil, fmt.Sprintf(`{"client_id":%q}`, clientID)) w.WriteHeader(http.StatusNoContent) }