package themes_handler import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "strings" "github.com/dchest/captcha" "github.com/gin-gonic/gin" ) func (h *Handler) GetUserRole(c *gin.Context) { userID, _ := c.Get("user_id") userName, _ := c.Get("user_name") displayName, _ := c.Get("display_name") isAdmin, _ := c.Get("is_admin") userTag, _ := c.Get("user_tag") isAdminBool := false if isAdmin != nil { isAdminBool = isAdmin.(bool) } displayNameStr := "" if displayName != nil { displayNameStr = displayName.(string) } userTagStr := "" if userTag != nil { userTagStr = userTag.(string) } c.JSON(http.StatusOK, gin.H{ "code": http.StatusOK, "msg": "GET > user role", "data": gin.H{ "user_id": userID, "user_name": userName, "display_name": displayNameStr, "is_admin": isAdminBool, "is_verified": isAdminBool || userTagStr == "verified", }, }) } // GET /api/user/downloads (stretch goal) func (h *Handler) GetUserDownloads(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": http.StatusOK, "msg": "GET > download history not yet implemented", "data": []interface{}{}, }) } // GET /api/marketplace/auth/captcha func (h *Handler) GetCaptcha(c *gin.Context) { id := captcha.NewLen(4) var buf bytes.Buffer if err := captcha.WriteImage(&buf, id, 150, 50); err != nil { log.Printf("[E] CAPTCHA > failed to generate: %v", err) c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "msg": "captcha generation failed", "data": nil}) return } c.JSON(http.StatusOK, gin.H{ "code": http.StatusOK, "msg": "GET > captcha", "data": gin.H{ "captchaId": id, "captchaImage": "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes()), }, }) } // GET /api/marketplace/auth/config func (h *Handler) GetAuthConfig(c *gin.Context) { cfg := h.Config casdoorConfigured := cfg.Backend.Casdoor.Endpoint != "" && cfg.Backend.Casdoor.ClientID != "" c.JSON(http.StatusOK, gin.H{ "code": http.StatusOK, "msg": "GET > auth config", "data": gin.H{ "casdoor_configured": casdoorConfigured, "casdoor_endpoint": cfg.Backend.Casdoor.Endpoint, "casdoor_client_id": cfg.Backend.Casdoor.ClientID, "casdoor_organization": cfg.Backend.Casdoor.Organization, "casdoor_application": cfg.Backend.Casdoor.Application, }, }) } // POST /api/marketplace/auth/login func (h *Handler) LoginProxy(c *gin.Context) { cfg := h.Config var req struct { Username string `json:"username"` Password string `json:"password"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "msg": "PUT > invalid request body", "data": nil}) return } // Proxy to Casdoor OAuth token endpoint tokenURL := cfg.Backend.Casdoor.Endpoint + "/api/login/oauth/access_token" values := url.Values{ "grant_type": {"password"}, "client_id": {cfg.Backend.Casdoor.ClientID}, "client_secret": {cfg.Backend.Casdoor.ClientSecret}, "username": {req.Username}, "password": {req.Password}, "scope": {"read"}, } reqHTTP, err := http.NewRequestWithContext(c.Request.Context(), http.MethodPost, tokenURL, strings.NewReader(values.Encode())) if err != nil { fail(c, http.StatusBadGateway, "Invalid authentication server configuration") return } reqHTTP.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := h.HTTP.Do(reqHTTP) if err != nil { log.Printf("[E] AUTH > login proxy error: %v", err) c.JSON(http.StatusBadGateway, gin.H{"code": http.StatusBadGateway, "msg": "auth server unreachable", "data": nil}) return } defer resp.Body.Close() body, readErr := io.ReadAll(io.LimitReader(resp.Body, 2<<20)) if readErr != nil { fail(c, http.StatusBadGateway, "Unable to read authentication response") return } if resp.StatusCode != http.StatusOK { c.JSON(http.StatusUnauthorized, gin.H{"code": http.StatusUnauthorized, "msg": "invalid credentials", "data": nil}) return } c.Header("Content-Type", "application/json") c.String(http.StatusOK, string(body)) } // POST /api/marketplace/auth/signup func (h *Handler) SignupProxy(c *gin.Context) { cfg := h.Config var req struct { Username string `json:"username"` Password string `json:"password"` Email string `json:"email"` Name string `json:"name"` CaptchaId string `json:"captchaId"` CaptchaCode string `json:"captchaCode"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "msg": "PUT > invalid request body", "data": nil}) return } if req.Username == "" || req.Password == "" { c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "msg": "用户名和密码为必填项", "data": nil}) return } // Validate captcha if !captcha.VerifyString(req.CaptchaId, req.CaptchaCode) { c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "msg": "验证码错误或已过期", "data": nil}) return } // Proxy to Casdoor signup API signupData := map[string]interface{}{ "organization": cfg.Backend.Casdoor.Organization, "username": req.Username, "password": req.Password, "application": cfg.Backend.Casdoor.Application, } if req.Email != "" { signupData["email"] = req.Email } if req.Name != "" { signupData["name"] = req.Name } signupURL := cfg.Backend.Casdoor.Endpoint + "/api/signup" signupBody, _ := json.Marshal(signupData) reqHTTP, err := http.NewRequestWithContext(c.Request.Context(), http.MethodPost, signupURL, bytes.NewReader(signupBody)) if err != nil { fail(c, http.StatusBadGateway, "Invalid authentication server configuration") return } reqHTTP.Header.Set("Content-Type", "application/json") resp, err := h.HTTP.Do(reqHTTP) if err != nil { log.Printf("[E] AUTH > signup proxy error: %v", err) c.JSON(http.StatusBadGateway, gin.H{"code": http.StatusBadGateway, "msg": "auth server unreachable", "data": nil}) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var result map[string]interface{} json.Unmarshal(body, &result) if status, ok := result["status"]; !ok || status != "ok" { msg := "注册失败" if m, ok := result["msg"]; ok { msg = fmt.Sprintf("%v", m) } c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "msg": msg, "data": nil}) return } log.Printf("[I] AUTH > signup successful for user: %s", req.Username) c.Header("Content-Type", "application/json") c.String(http.StatusOK, string(body)) }