merge: 合入本地 Ops 扩展与 origin/main(ECR-009–016)
保留远程用户侧 ECR-009–016 与本地 Ops 目录/RBAC/CMS/危机等能力;文档标注分叉期间 ECR 编号冲突。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -30,22 +30,49 @@ func (h *AdminHandler) Register(api *gin.RouterGroup) {
|
||||
authed.Use(middleware.AdminAuth(h.Svc))
|
||||
authed.POST("/auth/logout", h.Logout)
|
||||
authed.GET("/me", h.Me)
|
||||
authed.GET("/stats", h.Stats)
|
||||
authed.GET("/users", h.ListUsers)
|
||||
authed.GET("/users/:id", h.GetUser)
|
||||
authed.POST("/users/:id/membership/grant", h.GrantMembership)
|
||||
authed.POST("/users/:id/ask-quota/grant", h.GrantAskQuota)
|
||||
authed.GET("/orders", h.ListOrders)
|
||||
authed.GET("/stats", middleware.RequireAdminPermission(h.Svc, admin.PermUsersRead), h.Stats)
|
||||
authed.GET("/users", middleware.RequireAdminPermission(h.Svc, admin.PermUsersRead), h.ListUsers)
|
||||
authed.GET("/users/:id", middleware.RequireAdminPermission(h.Svc, admin.PermUsersRead), h.GetUser)
|
||||
authed.POST("/users/:id/membership/grant", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipGrant), h.GrantMembership)
|
||||
authed.POST("/users/:id/ask-quota/grant", middleware.RequireAdminPermission(h.Svc, admin.PermAskQuotaGrant), h.GrantAskQuota)
|
||||
authed.GET("/orders", middleware.RequireAdminPermission(h.Svc, admin.PermOrdersRead), h.ListOrders)
|
||||
authed.GET("/membership/plan-prices", h.ListPlanPrices)
|
||||
authed.PUT("/membership/plan-prices", h.PutPlanPrices)
|
||||
authed.GET("/audit-logs", h.ListAudit)
|
||||
authed.GET("/analytics/overview", h.AnalyticsOverview)
|
||||
authed.GET("/analytics/pages", h.AnalyticsPages)
|
||||
authed.GET("/analytics/exits", h.AnalyticsExits)
|
||||
authed.GET("/analytics/clicks", h.AnalyticsClicks)
|
||||
authed.GET("/analytics/funnel", h.AnalyticsFunnel)
|
||||
authed.GET("/audit-logs", middleware.RequireAdminPermission(h.Svc, admin.PermAuditRead), h.ListAudit)
|
||||
authed.GET("/analytics/overview", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.AnalyticsOverview)
|
||||
authed.GET("/analytics/pages", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.AnalyticsPages)
|
||||
authed.GET("/analytics/exits", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.AnalyticsExits)
|
||||
authed.GET("/analytics/clicks", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.AnalyticsClicks)
|
||||
authed.GET("/analytics/funnel", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.AnalyticsFunnel)
|
||||
h.registerContent(authed)
|
||||
h.registerSystem(authed)
|
||||
h.registerRBAC(authed)
|
||||
h.registerLifecycle(authed)
|
||||
h.registerMembershipPlans(authed)
|
||||
h.registerRedemption(authed)
|
||||
h.registerInsight(authed)
|
||||
h.registerAskOps(authed)
|
||||
h.registerQualityFeedback(authed)
|
||||
h.registerEntitlement(authed)
|
||||
h.registerContentSafety(authed)
|
||||
h.registerAIConfig(authed)
|
||||
h.registerCrisis(authed)
|
||||
h.registerCMS(authed)
|
||||
h.registerCMSPublications(authed)
|
||||
h.registerKnowledgeChunks(authed)
|
||||
h.registerToolDefinitions(authed)
|
||||
h.registerBlockPolicies(authed)
|
||||
h.registerModerationCases(authed)
|
||||
h.registerCrisisEvents(authed)
|
||||
h.registerInterventionOutcomes(authed)
|
||||
h.registerHandoffCases(authed)
|
||||
h.registerPrivacyRequests(authed)
|
||||
h.registerStarConfigs(authed)
|
||||
h.registerRhythmConfigs(authed)
|
||||
h.registerImageCardDecks(authed)
|
||||
h.registerReportTemplates(authed)
|
||||
h.registerFunnelDefinitions(authed)
|
||||
h.registerExploreScales(authed)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) Login(c *gin.Context) {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerAIConfig(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/ai")
|
||||
g.GET("/system-prompts", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.ListSystemPrompts)
|
||||
g.GET("/system-prompts/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.GetSystemPrompt)
|
||||
g.GET("/knowledge-sources", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.ListKnowledgeSources)
|
||||
g.GET("/knowledge-sources/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.GetKnowledgeSource)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListSystemPrompts(c *gin.Context) {
|
||||
items, err := h.Svc.ListSystemPrompts(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50027, "list system prompts failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetSystemPrompt(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetSystemPrompt(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrSystemPromptNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40404, "system prompt not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50028, "get system prompt failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListKnowledgeSources(c *gin.Context) {
|
||||
items, err := h.Svc.ListKnowledgeSources(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50029, "list knowledge sources failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetKnowledgeSource(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetKnowledgeSource(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrKnowledgeSourceNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40405, "knowledge source not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50030, "get knowledge source failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerAskOps(authed *gin.RouterGroup) {
|
||||
authed.GET("/ask/threads", middleware.RequireAdminPermission(h.Svc, admin.PermAskRead), h.ListAskThreads)
|
||||
authed.GET("/ask/threads/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAskRead), h.GetAskThread)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListAskThreads(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||||
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
||||
var userID *uuid.UUID
|
||||
if q := c.Query("user_id"); q != "" {
|
||||
id, err := uuid.Parse(q)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid user_id")
|
||||
return
|
||||
}
|
||||
userID = &id
|
||||
}
|
||||
items, err := h.Svc.ListAskSessions(c.Request.Context(), userID, limit, offset)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50019, "list ask threads failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetAskThread(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid thread id")
|
||||
return
|
||||
}
|
||||
detail, err := h.Svc.GetAskSessionDetail(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrAskThreadNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40402, "ask thread not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50020, "get ask thread failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, detail)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerBlockPolicies(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/content-safety")
|
||||
g.GET("/block-policies", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.ListBlockPolicies)
|
||||
g.GET("/block-policies/:id", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.GetBlockPolicy)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListBlockPolicies(c *gin.Context) {
|
||||
items, err := h.Svc.ListBlockPolicies(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list block-policy failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetBlockPolicy(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetBlockPolicy(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrBlockPolicyNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "block-policy not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get block-policy failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerCMS(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/cms")
|
||||
g.GET("/banners", middleware.RequireAdminPermission(h.Svc, admin.PermCMSRead), h.ListBanners)
|
||||
g.GET("/banners/:id", middleware.RequireAdminPermission(h.Svc, admin.PermCMSRead), h.GetBanner)
|
||||
g.GET("/feed-slots", middleware.RequireAdminPermission(h.Svc, admin.PermCMSRead), h.ListFeedSlots)
|
||||
g.GET("/feed-slots/:id", middleware.RequireAdminPermission(h.Svc, admin.PermCMSRead), h.GetFeedSlot)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListBanners(c *gin.Context) {
|
||||
items, err := h.Svc.ListBanners(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50040, "list banners failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetBanner(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetBanner(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrBannerNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40410, "banner not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50041, "get banner failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListFeedSlots(c *gin.Context) {
|
||||
items, err := h.Svc.ListFeedSlots(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50042, "list feed slots failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetFeedSlot(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetFeedSlot(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrFeedSlotNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40411, "feed slot not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50043, "get feed slot failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerContent(authed *gin.RouterGroup) {
|
||||
authed.GET("/home/tools", h.ListHomeTools)
|
||||
authed.PUT("/home/tools", h.ReplaceHomeTools)
|
||||
authed.GET("/scales", h.ListScales)
|
||||
authed.PATCH("/scales/:id", h.PatchScale)
|
||||
authed.GET("/home/tools", middleware.RequireAdminPermission(h.Svc, admin.PermContentWrite), h.ListHomeTools)
|
||||
authed.PUT("/home/tools", middleware.RequireAdminPermission(h.Svc, admin.PermContentWrite), h.ReplaceHomeTools)
|
||||
authed.GET("/scales", middleware.RequireAdminPermission(h.Svc, admin.PermContentWrite), h.ListScales)
|
||||
authed.PATCH("/scales/:id", middleware.RequireAdminPermission(h.Svc, admin.PermContentWrite), h.PatchScale)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListHomeTools(c *gin.Context) {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerContentSafety(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/content-safety")
|
||||
g.GET("/filter-rules", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.ListFilterRules)
|
||||
g.GET("/filter-rules/:id", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.GetFilterRule)
|
||||
g.POST("/evaluate", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.EvaluateContent)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListFilterRules(c *gin.Context) {
|
||||
items, err := h.Svc.ListFilterRules(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50022, "list filter rules failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetFilterRule(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetFilterRule(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrFilterRuleNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40403, "filter rule not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50023, "get filter rule failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) EvaluateContent(c *gin.Context) {
|
||||
var body struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid body")
|
||||
return
|
||||
}
|
||||
matches, err := h.Svc.EvaluateContent(c.Request.Context(), body.Text)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50024, "evaluate failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"matches": matches})
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerCrisis(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/crisis")
|
||||
g.GET("/policies", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.ListCrisisPolicies)
|
||||
g.GET("/policies/:id", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.GetCrisisPolicy)
|
||||
g.POST("/evaluate", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.EvaluateCrisis)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListCrisisPolicies(c *gin.Context) {
|
||||
items, err := h.Svc.ListCrisisPolicies(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50029, "list crisis policies failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetCrisisPolicy(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetCrisisPolicy(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrCrisisPolicyNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40405, "crisis policy not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50030, "get crisis policy failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) EvaluateCrisis(c *gin.Context) {
|
||||
var body struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid body")
|
||||
return
|
||||
}
|
||||
matches, err := h.Svc.EvaluateCrisis(c.Request.Context(), body.Text)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50031, "evaluate failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"matches": matches})
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerCrisisEvents(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/crisis")
|
||||
g.GET("/events", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.ListCrisisEvents)
|
||||
g.GET("/events/:id", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.GetCrisisEvent)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListCrisisEvents(c *gin.Context) {
|
||||
items, err := h.Svc.ListCrisisEvents(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list crisis-event failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetCrisisEvent(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetCrisisEvent(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrCrisisEventNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "crisis-event not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get crisis-event failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerEntitlement(authed *gin.RouterGroup) {
|
||||
authed.GET("/users/:id/entitlements", middleware.RequireAdminPermission(h.Svc, admin.PermUsersRead), h.GetUserEntitlements)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetUserEntitlements(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid user id")
|
||||
return
|
||||
}
|
||||
ent, err := h.Svc.GetUserEntitlement(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrUserNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40401, "user not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50021, "get entitlements failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, ent)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerExploreScales(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/explore")
|
||||
g.GET("/scales", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.ListExploreScales)
|
||||
g.GET("/scales/:id", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.GetExploreScale)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListExploreScales(c *gin.Context) {
|
||||
items, err := h.Svc.ListScalesAdmin(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50060, "list explore scales failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetExploreScale(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetScaleAdmin(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrScaleNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40430, "scale not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50061, "get explore scale failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerFunnelDefinitions(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/analytics")
|
||||
g.GET("/funnel-definitions", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.ListFunnelDefinitions)
|
||||
g.GET("/funnel-definitions/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAnalyticsRead), h.GetFunnelDefinition)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListFunnelDefinitions(c *gin.Context) {
|
||||
items, err := h.Svc.ListFunnelDefinitions(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list funnel-definition failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetFunnelDefinition(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetFunnelDefinition(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrFunnelDefinitionNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "funnel-definition not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get funnel-definition failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerHandoffCases(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/ask")
|
||||
g.GET("/handoffs", middleware.RequireAdminPermission(h.Svc, admin.PermAskRead), h.ListHandoffCases)
|
||||
g.GET("/handoffs/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAskRead), h.GetHandoffCase)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListHandoffCases(c *gin.Context) {
|
||||
items, err := h.Svc.ListHandoffCases(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list handoff-case failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetHandoffCase(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetHandoffCase(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrHandoffCaseNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "handoff-case not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get handoff-case failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerImageCardDecks(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/explore")
|
||||
g.GET("/image-card-decks", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.ListImageCardDecks)
|
||||
g.GET("/image-card-decks/:id", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.GetImageCardDeck)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListImageCardDecks(c *gin.Context) {
|
||||
items, err := h.Svc.ListImageCardDecks(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list image-card-deck failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetImageCardDeck(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetImageCardDeck(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrImageCardDeckNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "image-card-deck not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get image-card-deck failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerInsight(authed *gin.RouterGroup) {
|
||||
authed.GET("/users/:id/insight", middleware.RequireAdminPermission(h.Svc, admin.PermUsersRead), h.GetUserInsight)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetUserInsight(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid user id")
|
||||
return
|
||||
}
|
||||
insight, err := h.Svc.GetUserInsight(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrUserNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40401, "user not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50018, "get insight failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, insight)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerInterventionOutcomes(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/crisis")
|
||||
g.GET("/interventions", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.ListInterventionOutcomes)
|
||||
g.GET("/interventions/:id", middleware.RequireAdminPermission(h.Svc, admin.PermCrisisRead), h.GetInterventionOutcome)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListInterventionOutcomes(c *gin.Context) {
|
||||
items, err := h.Svc.ListInterventionOutcomes(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list intervention-outcome failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetInterventionOutcome(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetInterventionOutcome(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrInterventionOutcomeNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "intervention-outcome not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get intervention-outcome failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerKnowledgeChunks(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/ai")
|
||||
g.GET("/knowledge-chunks", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.ListKnowledgeChunks)
|
||||
g.GET("/knowledge-chunks/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.GetKnowledgeChunk)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListKnowledgeChunks(c *gin.Context) {
|
||||
items, err := h.Svc.ListKnowledgeChunks(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list knowledge-chunk failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetKnowledgeChunk(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetKnowledgeChunk(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrKnowledgeChunkNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "knowledge-chunk not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get knowledge-chunk failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerLifecycle(authed *gin.RouterGroup) {
|
||||
authed.POST("/users/:id/status", middleware.RequireAdminPermission(h.Svc, admin.PermUsersStatusWrite), h.PostUserStatus)
|
||||
authed.GET("/users/:id/status-transitions", middleware.RequireAdminPermission(h.Svc, admin.PermUsersRead), h.ListUserStatusTransitions)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) PostUserStatus(c *gin.Context) {
|
||||
adminID, ok := middleware.AdminIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
return
|
||||
}
|
||||
userID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid id")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Status string `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.Status == "" {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "status required")
|
||||
return
|
||||
}
|
||||
err = h.Svc.TransitionUserStatus(c.Request.Context(), adminID, userID, body.Status, body.Reason)
|
||||
if errors.Is(err, admin.ErrUserNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "user not found")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, admin.ErrReasonRequired) || errors.Is(err, admin.ErrInvalidStatusEdge) {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
detail, err := h.Svc.GetUser(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, detail)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListUserStatusTransitions(c *gin.Context) {
|
||||
userID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid id")
|
||||
return
|
||||
}
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
||||
items, err := h.Svc.ListStatusTransitions(c.Request.Context(), userID, limit)
|
||||
if errors.Is(err, admin.ErrUserNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "user not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerMembershipPlans(authed *gin.RouterGroup) {
|
||||
authed.GET("/membership-plans", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipPlansRead), h.ListMembershipPlans)
|
||||
authed.GET("/membership-plans/:code", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipPlansRead), h.GetMembershipPlan)
|
||||
authed.PUT("/membership-plans/:code", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipPlansWrite), h.PutMembershipPlan)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListMembershipPlans(c *gin.Context) {
|
||||
items, err := h.Svc.ListMembershipPlans(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetMembershipPlan(c *gin.Context) {
|
||||
plan, err := h.Svc.GetMembershipPlan(c.Request.Context(), c.Param("code"))
|
||||
if errors.Is(err, admin.ErrPlanNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "plan not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, plan)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) PutMembershipPlan(c *gin.Context) {
|
||||
adminID, ok := middleware.AdminIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Title string `json:"title"`
|
||||
DurationDays int `json:"duration_days"`
|
||||
AmountCents int `json:"amount_cents"`
|
||||
Active *bool `json:"active"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid body")
|
||||
return
|
||||
}
|
||||
active := true
|
||||
if body.Active != nil {
|
||||
active = *body.Active
|
||||
}
|
||||
plan, err := h.Svc.UpdateMembershipPlan(
|
||||
c.Request.Context(), adminID, c.Param("code"), body.Title, body.DurationDays, body.AmountCents, active,
|
||||
)
|
||||
if errors.Is(err, admin.ErrPlanNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "plan not found")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, admin.ErrInvalidPlanU) {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, plan)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerModerationCases(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/content-safety")
|
||||
g.GET("/cases", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.ListModerationCases)
|
||||
g.GET("/cases/:id", middleware.RequireAdminPermission(h.Svc, admin.PermContentSafetyRead), h.GetModerationCase)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListModerationCases(c *gin.Context) {
|
||||
items, err := h.Svc.ListModerationCases(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list moderation-case failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetModerationCase(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetModerationCase(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrModerationCaseNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "moderation-case not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get moderation-case failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerPrivacyRequests(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/privacy")
|
||||
g.GET("/requests", middleware.RequireAdminPermission(h.Svc, admin.PermPrivacyRead), h.ListPrivacyRequests)
|
||||
g.GET("/requests/:id", middleware.RequireAdminPermission(h.Svc, admin.PermPrivacyRead), h.GetPrivacyRequest)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListPrivacyRequests(c *gin.Context) {
|
||||
items, err := h.Svc.ListPrivacyRequests(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list privacy-request failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetPrivacyRequest(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetPrivacyRequest(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrPrivacyRequestNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "privacy-request not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get privacy-request failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerQualityFeedback(authed *gin.RouterGroup) {
|
||||
authed.GET("/ask/feedback", middleware.RequireAdminPermission(h.Svc, admin.PermAskRead), h.ListAskFeedback)
|
||||
authed.POST("/ask/threads/:id/feedback", middleware.RequireAdminPermission(h.Svc, admin.PermAskFeedbackWrite), h.CreateAskFeedback)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListAskFeedback(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||||
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
||||
items, err := h.Svc.ListQualityFeedback(c.Request.Context(), limit, offset)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50025, "list feedback failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) CreateAskFeedback(c *gin.Context) {
|
||||
adminID, ok := middleware.AdminIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
return
|
||||
}
|
||||
threadID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid thread id")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Rating int `json:"rating"`
|
||||
Tag string `json:"tag"`
|
||||
Note string `json:"note"`
|
||||
MessageID *string `json:"message_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid body")
|
||||
return
|
||||
}
|
||||
var msgID *uuid.UUID
|
||||
if body.MessageID != nil && *body.MessageID != "" {
|
||||
id, err := uuid.Parse(*body.MessageID)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid message_id")
|
||||
return
|
||||
}
|
||||
msgID = &id
|
||||
}
|
||||
row, err := h.Svc.CreateQualityFeedback(c.Request.Context(), adminID, threadID, msgID, body.Rating, body.Tag, body.Note)
|
||||
if errors.Is(err, admin.ErrBadFeedbackRating) || errors.Is(err, admin.ErrBadFeedbackTag) || errors.Is(err, admin.ErrFeedbackNoteLong) {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
if errors.Is(err, admin.ErrAskThreadNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40402, "ask thread not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50026, "create feedback failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerRBAC(authed *gin.RouterGroup) {
|
||||
authed.GET("/roles", middleware.RequireAdminPermission(h.Svc, admin.PermRolesRead), h.ListRoles)
|
||||
authed.GET("/roles/:id", middleware.RequireAdminPermission(h.Svc, admin.PermRolesRead), h.GetRole)
|
||||
authed.PUT("/roles/:id/permissions", middleware.RequireAdminPermission(h.Svc, admin.PermRolesWrite), h.PutRolePermissions)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListRoles(c *gin.Context) {
|
||||
items, err := h.Svc.ListRoles(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetRole(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid id")
|
||||
return
|
||||
}
|
||||
role, err := h.Svc.GetRole(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrRoleNotFound) || role == nil {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "role not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, role)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) PutRolePermissions(c *gin.Context) {
|
||||
adminID, ok := middleware.AdminIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
return
|
||||
}
|
||||
roleID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid id")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid body")
|
||||
return
|
||||
}
|
||||
if body.Permissions == nil {
|
||||
body.Permissions = []string{}
|
||||
}
|
||||
err = h.Svc.ReplaceRolePermissions(c.Request.Context(), adminID, roleID, body.Permissions)
|
||||
if errors.Is(err, admin.ErrRoleNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "role not found")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, admin.ErrInvalidPerm) {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
role, err := h.Svc.GetRole(c.Request.Context(), roleID)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, role)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerRedemption(authed *gin.RouterGroup) {
|
||||
authed.POST("/redemption-batches", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipCodesWrite), h.CreateRedemptionBatch)
|
||||
authed.GET("/redemption-batches", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipCodesRead), h.ListRedemptionBatches)
|
||||
authed.GET("/redemption-batches/:id/codes", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipCodesRead), h.ListRedemptionCodes)
|
||||
authed.POST("/redemption-codes/:id/disable", middleware.RequireAdminPermission(h.Svc, admin.PermMembershipCodesWrite), h.DisableRedemptionCode)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) CreateRedemptionBatch(c *gin.Context) {
|
||||
adminID, ok := middleware.AdminIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Label string `json:"label"`
|
||||
PlanCode string `json:"plan_code"`
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid body")
|
||||
return
|
||||
}
|
||||
batch, codes, err := h.Svc.CreateRedemptionBatch(c.Request.Context(), adminID, body.Label, body.PlanCode, body.Quantity)
|
||||
if errors.Is(err, admin.ErrBadBatchQty) || errors.Is(err, admin.ErrPlanNotFound) || errors.Is(err, admin.ErrInvalidPlanU) {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"batch": batch, "codes": codes})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListRedemptionBatches(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
||||
items, err := h.Svc.ListRedemptionBatches(c.Request.Context(), limit)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListRedemptionCodes(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid id")
|
||||
return
|
||||
}
|
||||
items, err := h.Svc.ListRedemptionCodes(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrBatchNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40400, "batch not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) DisableRedemptionCode(c *gin.Context) {
|
||||
adminID, ok := middleware.AdminIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "invalid id")
|
||||
return
|
||||
}
|
||||
err = h.Svc.DisableRedemptionCode(c.Request.Context(), adminID, id)
|
||||
if errors.Is(err, admin.ErrCodeDisable) {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"ok": true})
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerReportTemplates(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/growth")
|
||||
g.GET("/report-templates", middleware.RequireAdminPermission(h.Svc, admin.PermGrowthRead), h.ListReportTemplates)
|
||||
g.GET("/report-templates/:id", middleware.RequireAdminPermission(h.Svc, admin.PermGrowthRead), h.GetReportTemplate)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListReportTemplates(c *gin.Context) {
|
||||
items, err := h.Svc.ListReportTemplates(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list report-template failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetReportTemplate(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetReportTemplate(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrReportTemplateNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "report-template not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get report-template failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerRhythmConfigs(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/explore")
|
||||
g.GET("/rhythm-configs", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.ListRhythmConfigs)
|
||||
g.GET("/rhythm-configs/:id", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.GetRhythmConfig)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListRhythmConfigs(c *gin.Context) {
|
||||
items, err := h.Svc.ListRhythmConfigs(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list rhythm-config failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetRhythmConfig(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetRhythmConfig(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrRhythmConfigNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "rhythm-config not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get rhythm-config failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerCMSPublications(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/cms")
|
||||
g.GET("/publications", middleware.RequireAdminPermission(h.Svc, admin.PermCMSRead), h.ListScheduledPublications)
|
||||
g.GET("/publications/:id", middleware.RequireAdminPermission(h.Svc, admin.PermCMSRead), h.GetScheduledPublication)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListScheduledPublications(c *gin.Context) {
|
||||
items, err := h.Svc.ListScheduledPublications(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list scheduled-publication failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetScheduledPublication(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetScheduledPublication(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrScheduledPublicationNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "scheduled-publication not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get scheduled-publication failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerStarConfigs(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/explore")
|
||||
g.GET("/star-configs", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.ListStarConfigs)
|
||||
g.GET("/star-configs/:id", middleware.RequireAdminPermission(h.Svc, admin.PermExploreRead), h.GetStarConfig)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListStarConfigs(c *gin.Context) {
|
||||
items, err := h.Svc.ListStarConfigs(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list star-config failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetStarConfig(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetStarConfig(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrStarConfigNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "star-config not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get star-config failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/service/admin"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) registerToolDefinitions(authed *gin.RouterGroup) {
|
||||
g := authed.Group("/ai")
|
||||
g.GET("/tools", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.ListToolDefinitions)
|
||||
g.GET("/tools/:id", middleware.RequireAdminPermission(h.Svc, admin.PermAIConfigRead), h.GetToolDefinition)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) ListToolDefinitions(c *gin.Context) {
|
||||
items, err := h.Svc.ListToolDefinitions(c.Request.Context())
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50050, "list tool-definition failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetToolDefinition(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40002, "invalid id")
|
||||
return
|
||||
}
|
||||
row, err := h.Svc.GetToolDefinition(c.Request.Context(), id)
|
||||
if errors.Is(err, admin.ErrToolDefinitionNotFound) {
|
||||
response.Fail(c, http.StatusNotFound, 40420, "tool-definition not found")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50051, "get tool-definition failed")
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -28,6 +28,7 @@ func (h *AskHandler) Register(rg *gin.RouterGroup) {
|
||||
rg.DELETE("/ask/threads/:id", h.ClearThread)
|
||||
rg.GET("/ask/threads/:id/messages", h.ListMessages)
|
||||
rg.POST("/ask/threads/:id/messages", h.SendMessage)
|
||||
h.registerFeedback(rg)
|
||||
}
|
||||
|
||||
// GetQuota handles GET /ask/quota.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
|
||||
asksvc "github.com/yuxingu/digital-psychology/apps/api/internal/service/ask"
|
||||
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
|
||||
)
|
||||
|
||||
func (h *AskHandler) registerFeedback(rg *gin.RouterGroup) {
|
||||
rg.POST("/ask/threads/:id/feedback", h.SubmitFeedback)
|
||||
}
|
||||
|
||||
// SubmitFeedback handles POST /ask/threads/:id/feedback.
|
||||
func (h *AskHandler) SubmitFeedback(c *gin.Context) {
|
||||
userID, ok := middleware.UserIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40100, "unauthorized")
|
||||
return
|
||||
}
|
||||
threadID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 10000, "invalid thread id")
|
||||
return
|
||||
}
|
||||
var req struct {
|
||||
Rating int `json:"rating"`
|
||||
Tag string `json:"tag"`
|
||||
Note string `json:"note"`
|
||||
MessageID *string `json:"message_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 10000, "invalid request")
|
||||
return
|
||||
}
|
||||
var msgID *uuid.UUID
|
||||
if req.MessageID != nil && *req.MessageID != "" {
|
||||
id, err := uuid.Parse(*req.MessageID)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 10000, "invalid message_id")
|
||||
return
|
||||
}
|
||||
msgID = &id
|
||||
}
|
||||
row, err := h.Svc.SubmitFeedback(c.Request.Context(), userID, threadID, asksvc.SubmitFeedbackInput{
|
||||
MessageID: msgID, Rating: req.Rating, Tag: req.Tag, Note: req.Note,
|
||||
})
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
if strings.Contains(msg, "rating") || strings.Contains(msg, "tag") || strings.Contains(msg, "note") {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, msg)
|
||||
return
|
||||
}
|
||||
if strings.Contains(msg, "thread not found") {
|
||||
response.Fail(c, http.StatusNotFound, 40410, msg)
|
||||
return
|
||||
}
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, msg)
|
||||
return
|
||||
}
|
||||
response.OK(c, row)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -51,6 +52,10 @@ func (h *AuthHandler) RegisterAccount(c *gin.Context) {
|
||||
if failTextCompliance(c, err) {
|
||||
return
|
||||
}
|
||||
if errors.Is(err, auth.ErrAccountRestricted) {
|
||||
response.Fail(c, http.StatusUnauthorized, 40113, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, http.StatusBadRequest, 40110, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -72,6 +77,10 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
deviceKey := c.GetHeader(middleware.DeviceKeyHeader)
|
||||
res, err := h.Svc.Login(c.Request.Context(), userID, deviceKey, body.Phone, body.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, auth.ErrAccountRestricted) {
|
||||
response.Fail(c, http.StatusUnauthorized, 40113, err.Error())
|
||||
return
|
||||
}
|
||||
response.Fail(c, http.StatusBadRequest, 40111, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ func (h *ReportHandler) Register(rg *gin.RouterGroup) {
|
||||
rg.GET("/reports/latest", h.GetLatest)
|
||||
rg.GET("/reports/:id", h.Get)
|
||||
rg.GET("/membership/me", h.GetMembership)
|
||||
rg.POST("/membership/redeem", h.RedeemCode)
|
||||
rg.POST("/orders", h.CreateOrder)
|
||||
rg.POST("/orders/:id/pay-mock", h.PayMock)
|
||||
}
|
||||
@@ -257,6 +258,37 @@ func (h *ReportHandler) GetMembership(c *gin.Context) {
|
||||
response.OK(c, me)
|
||||
}
|
||||
|
||||
// RedeemCode handles POST /membership/redeem.
|
||||
func (h *ReportHandler) RedeemCode(c *gin.Context) {
|
||||
userID, ok := middleware.UserIDFromContext(c)
|
||||
if !ok {
|
||||
response.Fail(c, http.StatusUnauthorized, 40100, "unauthorized")
|
||||
return
|
||||
}
|
||||
msvc, ok := h.requireMembership(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.Code == "" {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, "code required")
|
||||
return
|
||||
}
|
||||
plan, err := msvc.Redeem(c.Request.Context(), userID, body.Code)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 40000, err.Error())
|
||||
return
|
||||
}
|
||||
me, err := msvc.Get(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 50000, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"plan": plan, "membership": me})
|
||||
}
|
||||
|
||||
// CreateOrder handles POST /orders.
|
||||
func (h *ReportHandler) CreateOrder(c *gin.Context) {
|
||||
userID, ok := middleware.UserIDFromContext(c)
|
||||
|
||||
Reference in New Issue
Block a user