227 lines
6.5 KiB
Go
227 lines
6.5 KiB
Go
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))
|
|
}
|