package httpapi import ( "encoding/json" "net/http" "strconv" "strings" "time" "git.118im.com/im_team/im_workbench/internal/store" ) func (a *API) adminWorkbenchApps(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: page := queryInt(r, "page", 1) perPage := queryInt(r, "per_page", 20) var enabled *int if raw := strings.TrimSpace(r.URL.Query().Get("enabled")); raw != "" { if value, err := strconv.Atoi(raw); err == nil { enabled = &value } } items, total, err := a.store.AdminWorkbenchModules(r.Context(), page, perPage, r.URL.Query().Get("code"), r.URL.Query().Get("name"), enabled) if err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, map[string]any{"items": adminWorkbenchModulePayloads(items), "total": total, "page": page, "per_page": perPage}) case http.MethodPost: var body adminWorkbenchModuleRequest if !decodeJSON(w, r, &body) { return } item, err := a.store.UpsertAdminWorkbenchModule(r.Context(), store.AdminWorkbenchModuleInput{ ID: body.ID, Code: body.Code, Name: body.Name, IconFileID: body.IconFileID, Route: body.Route, SortOrder: body.SortOrder, UnreadKey: body.UnreadKey, Enabled: body.Enabled, Permission: body.Permission, }) if err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, adminWorkbenchModulePayload(*item)) default: methodNotAllowed(w, r) } } func (a *API) adminWorkbenchAppDisable(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { methodNotAllowed(w, r) return } var body struct { ID int64 `json:"id"` Code string `json:"code"` } if !decodeJSON(w, r, &body) { return } if err := a.store.DisableAdminWorkbenchModule(r.Context(), body.ID, body.Code); err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, map[string]any{"ok": true}) } func (a *API) adminWorkbenchAppsReorder(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { methodNotAllowed(w, r) return } var body struct { Items []struct { Code string `json:"code"` SortOrder int `json:"sort_order"` } `json:"items"` } if !decodeJSON(w, r, &body) { return } items := make([]store.AdminWorkbenchReorderItem, 0, len(body.Items)) for _, item := range body.Items { items = append(items, store.AdminWorkbenchReorderItem{Code: item.Code, SortOrder: item.SortOrder}) } if err := a.store.ReorderAdminWorkbenchModules(r.Context(), items); err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, map[string]any{"ok": true}) } func (a *API) adminMailboxConfig(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: cfg, err := a.store.AdminMailboxConfig(r.Context()) if err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, adminMailboxConfigPayload(*cfg)) case http.MethodPost: var body adminMailboxConfigRequest if !decodeJSON(w, r, &body) { return } if err := a.store.UpdateAdminMailboxConfig(r.Context(), store.AdminMailboxConfig{ Enabled: body.Enabled, AllowAnonymous: body.AllowAnonymous, RequiredFields: body.RequiredFields, MaxLength: body.MaxLength, AttachmentPolicy: body.AttachmentPolicy, NoticeTitle: body.NoticeTitle, NoticeContent: body.NoticeContent, }); err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, map[string]any{"ok": true}) default: methodNotAllowed(w, r) } } func (a *API) adminFeedbackConfig(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: cfg, err := a.store.AdminFeedbackConfig(r.Context()) if err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, adminFeedbackConfigPayload(*cfg)) case http.MethodPost: var body adminFeedbackConfigRequest if !decodeJSON(w, r, &body) { return } if err := a.store.UpdateAdminFeedbackConfig(r.Context(), store.AdminFeedbackConfig{ Categories: body.Categories, MaxLength: body.MaxLength, AttachmentPolicy: body.AttachmentPolicy, AllowContact: body.AllowContact, DiagnosticsNotice: body.DiagnosticsNotice, }); err != nil { writeStoreError(w, r, err) return } writeSuccess(w, r, map[string]any{"ok": true}) default: methodNotAllowed(w, r) } } type adminWorkbenchModuleRequest struct { ID int64 `json:"id"` Code string `json:"code"` Name string `json:"name"` IconFileID string `json:"icon_file_id"` Route string `json:"route"` SortOrder int `json:"sort_order"` UnreadKey string `json:"unread_key"` Enabled int `json:"enabled"` Permission string `json:"permission"` } type adminMailboxConfigRequest struct { Enabled int `json:"enabled"` AllowAnonymous int `json:"allow_anonymous"` RequiredFields string `json:"required_fields"` MaxLength int `json:"max_length"` AttachmentPolicy string `json:"attachment_policy"` NoticeTitle string `json:"notice_title"` NoticeContent string `json:"notice_content"` } type adminFeedbackConfigRequest struct { Categories string `json:"categories"` MaxLength int `json:"max_length"` AttachmentPolicy string `json:"attachment_policy"` AllowContact int `json:"allow_contact"` DiagnosticsNotice string `json:"diagnostics_notice"` } func adminWorkbenchModulePayloads(items []store.AdminWorkbenchModule) []map[string]any { res := make([]map[string]any, 0, len(items)) for _, item := range items { res = append(res, adminWorkbenchModulePayload(item)) } return res } func adminWorkbenchModulePayload(item store.AdminWorkbenchModule) map[string]any { return map[string]any{ "id": item.ID, "code": item.Code, "name": item.Name, "iconFileId": item.IconFileID, "route": item.Route, "sortOrder": item.SortOrder, "unreadKey": item.UnreadKey, "enabled": item.Enabled, "permission": item.Permission, "createdAt": formatAdminTime(item.CreatedAt), "updatedAt": formatAdminTime(item.UpdatedAt), } } func adminMailboxConfigPayload(cfg store.AdminMailboxConfig) map[string]any { return map[string]any{ "id": cfg.ID, "enabled": cfg.Enabled, "allowAnonymous": cfg.AllowAnonymous, "requiredFields": cfg.RequiredFields, "maxLength": cfg.MaxLength, "attachmentPolicy": cfg.AttachmentPolicy, "noticeTitle": cfg.NoticeTitle, "noticeContent": cfg.NoticeContent, "noticeUpdatedAt": formatAdminTime(cfg.NoticeUpdatedAt), "updatedAt": formatAdminTime(cfg.UpdatedAt), } } func adminFeedbackConfigPayload(cfg store.AdminFeedbackConfig) map[string]any { return map[string]any{ "id": cfg.ID, "categories": cfg.Categories, "maxLength": cfg.MaxLength, "attachmentPolicy": cfg.AttachmentPolicy, "allowContact": cfg.AllowContact, "diagnosticsNotice": cfg.DiagnosticsNotice, "updatedAt": formatAdminTime(cfg.UpdatedAt), } } func decodeJSON(w http.ResponseWriter, r *http.Request, out any) bool { if err := json.NewDecoder(r.Body).Decode(out); err != nil { writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", nil) return false } return true } func queryInt(r *http.Request, key string, fallback int) int { value, err := strconv.Atoi(strings.TrimSpace(r.URL.Query().Get(key))) if err != nil || value <= 0 { return fallback } return value } func formatAdminTime(t time.Time) string { if t.IsZero() { return "" } return t.UTC().Format(time.RFC3339Nano) }