Skip to content

Commit

Permalink
wip: captcha
Browse files Browse the repository at this point in the history
  • Loading branch information
ElaBosak233 committed May 30, 2024
1 parent 291f0f4 commit a622a06
Show file tree
Hide file tree
Showing 23 changed files with 398 additions and 42 deletions.
20 changes: 20 additions & 0 deletions api/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,22 @@ const docTemplate = `{
"responses": {}
}
},
"/configs/captcha": {
"get": {
"description": "Captcha 配置查询",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Config"
],
"summary": "Captcha 配置查询",
"responses": {}
}
},
"/games/": {
"get": {
"security": [
Expand Down Expand Up @@ -2408,6 +2424,10 @@ const docTemplate = `{
"description": "The user's password. Crypt.",
"type": "string"
},
"remote_ip": {
"description": "The user's remote ip.",
"type": "string"
},
"teams": {
"description": "The user's teams.",
"type": "array",
Expand Down
20 changes: 20 additions & 0 deletions api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,22 @@
"responses": {}
}
},
"/configs/captcha": {
"get": {
"description": "Captcha 配置查询",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Config"
],
"summary": "Captcha 配置查询",
"responses": {}
}
},
"/games/": {
"get": {
"security": [
Expand Down Expand Up @@ -2399,6 +2415,10 @@
"description": "The user's password. Crypt.",
"type": "string"
},
"remote_ip": {
"description": "The user's remote ip.",
"type": "string"
},
"teams": {
"description": "The user's teams.",
"type": "array",
Expand Down
14 changes: 14 additions & 0 deletions api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@ definitions:
password:
description: The user's password. Crypt.
type: string
remote_ip:
description: The user's remote ip.
type: string
teams:
description: The user's teams.
items:
Expand Down Expand Up @@ -1082,6 +1085,17 @@ paths:
summary: 更新配置
tags:
- Config
/configs/captcha:
get:
consumes:
- application/json
description: Captcha 配置查询
produces:
- application/json
responses: {}
summary: Captcha 配置查询
tags:
- Config
/games/:
get:
consumes:
Expand Down
2 changes: 1 addition & 1 deletion internal/app/config/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type ApplicationCfg struct {
SiteKey string `yaml:"site_key" json:"site_key" mapstructure:"site_key"`
SecretKey string `yaml:"secret_key" json:"secret_key" mapstructure:"secret_key"`
Threshold float64 `yaml:"threshold" json:"threshold" mapstructure:"threshold"`
} `yaml:"re_captcha" json:"re_captcha" mapstructure:"re_captcha"`
} `yaml:"recaptcha" json:"recaptcha" mapstructure:"recaptcha"`
Turnstile struct {
URL string `yaml:"url" json:"url" mapstructure:"url"`
SiteKey string `yaml:"site_key" json:"site_key" mapstructure:"site_key"`
Expand Down
27 changes: 26 additions & 1 deletion internal/controller/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package controller

import (
"github.com/elabosak233/cloudsdale/internal/app/config"
"github.com/elabosak233/cloudsdale/internal/model/request"
"github.com/elabosak233/cloudsdale/internal/service"
"github.com/elabosak233/cloudsdale/internal/utils/validator"
Expand All @@ -11,6 +12,7 @@ import (
type IConfigController interface {
Find(ctx *gin.Context)
Update(ctx *gin.Context)
FindCaptcha(ctx *gin.Context)
}

type ConfigController struct {
Expand All @@ -33,7 +35,7 @@ func NewConfigController(appService *service.Service) IConfigController {
func (c *ConfigController) Find(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"data": c.configService.FindAll(),
"data": *(config.PltCfg()),
})
}

Expand Down Expand Up @@ -68,3 +70,26 @@ func (c *ConfigController) Update(ctx *gin.Context) {
})
}
}

// FindCaptcha
// @Summary Captcha 配置查询
// @Description Captcha 配置查询
// @Tags Config
// @Accept json
// @Produce json
// @Router /configs/captcha [get]
func (c *ConfigController) FindCaptcha(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"data": map[string]any{
"enabled": config.AppCfg().Captcha.Enabled,
"provider": config.AppCfg().Captcha.Provider,
"turnstile": map[string]any{
"site_key": config.AppCfg().Captcha.Turnstile.SiteKey,
},
"recaptcha": map[string]any{
"site_key": config.AppCfg().Captcha.ReCaptcha.SiteKey,
},
},
})
}
4 changes: 4 additions & 0 deletions internal/controller/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ func (c *UserController) Login(ctx *gin.Context) {
zap.L().Warn(fmt.Sprintf("User %s login failed", user.Username), zap.Uint("user_id", user.ID))
return
}
_ = c.userService.Update(request.UserUpdateRequest{
ID: user.ID,
RemoteIP: ctx.RemoteIP(),
})
tokenString, err := c.userService.GetJwtTokenByID(user)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
Expand Down
2 changes: 1 addition & 1 deletion internal/extension/casbin/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func initDefaultPolicy() {
{"user", "/api/pods/{id}", "DELETE"},

{"guest", "/api/", "GET"},
{"guest", "/api/configs/", "GET"},
{"guest", "/api/configs/*", "GET"},
{"guest", "/api/categories/", "GET"},
{"guest", "/api/users/", "GET"},
{"guest", "/api/users/register", "POST"},
Expand Down
36 changes: 29 additions & 7 deletions internal/middleware/frontend.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
package middleware

import (
"github.com/elabosak233/cloudsdale/internal/app/config"
"github.com/elabosak233/cloudsdale/internal/utils"
"github.com/gin-gonic/gin"
"net/http"
"os"
"path/filepath"
"strings"
)

func index(ctx *gin.Context) {
filePath := filepath.Join(utils.FrontendPath, "index.html")
indexContent, err := os.ReadFile(filePath)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"code": http.StatusInternalServerError,
"msg": "Error reading index.html",
})
ctx.Abort()
return
}
indexContentStr := string(indexContent)
indexContentStr = strings.ReplaceAll(indexContentStr, "{{ Cloudsdale.Title }}", config.PltCfg().Site.Title)
ctx.Header("Content-Type", "text/html; charset=utf-8")
ctx.String(http.StatusOK, indexContentStr)
ctx.Abort()
}

func Frontend(urlPrefix string) gin.HandlerFunc {
root := "./dist"
fileServer := http.FileServer(http.Dir(root))
fileServer := http.FileServer(http.Dir(utils.FrontendPath))
if !strings.HasSuffix(urlPrefix, "/") {
urlPrefix = urlPrefix + "/"
}
Expand All @@ -20,14 +39,17 @@ func Frontend(urlPrefix string) gin.HandlerFunc {
ctx.Next()
} else {
ctx.Set("skip_logging", true)
filePath := filepath.Join(root, ctx.Request.URL.Path)
filePath := filepath.Join(utils.FrontendPath, ctx.Request.URL.Path)
_, err := os.Stat(filePath)
if err == nil {
http.StripPrefix(staticServerPrefix, fileServer).ServeHTTP(ctx.Writer, ctx.Request)
ctx.Abort()
if ctx.Request.URL.Path == "/" || ctx.Request.URL.Path == "/index.html" {
index(ctx)
} else {
http.StripPrefix(staticServerPrefix, fileServer).ServeHTTP(ctx.Writer, ctx.Request)
ctx.Abort()
}
} else if os.IsNotExist(err) {
http.ServeFile(ctx.Writer, ctx.Request, filepath.Join(root, "index.html"))
ctx.Abort()
index(ctx)
} else {
ctx.Next()
}
Expand Down
1 change: 1 addition & 0 deletions internal/model/request/user_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type UserUpdateRequest struct {
Password string `binding:"omitempty,min=6" json:"password,omitempty"`
Email string `binding:"omitempty,email" json:"email,omitempty"`
Group string `json:"group"`
RemoteIP string `json:"-"`
}

type UserDeleteRequest struct {
Expand Down
1 change: 1 addition & 0 deletions internal/model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type User struct {
Avatar *File `gorm:"-" json:"avatar"` // The user's avatar.
Group string `gorm:"column:group;varchar(16);not null;" json:"group,omitempty"` // The user's group.
Password string `gorm:"column:password;type:varchar(255);not null" json:"password,omitempty"` // The user's password. Crypt.
RemoteIP string `gorm:"column:remote_ip;type:varchar(32)" json:"remote_ip,omitempty"` // The user's remote ip.
CreatedAt int64 `gorm:"autoUpdateTime:milli" json:"created_at,omitempty"` // The user's creation time.
UpdatedAt int64 `gorm:"autoUpdateTime:milli" json:"updated_at,omitempty"` // The user's last update time.
Teams []*Team `gorm:"many2many:user_teams;" json:"teams,omitempty"` // The user's teams.
Expand Down
1 change: 1 addition & 0 deletions internal/router/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ func NewConfigRouter(configRouter *gin.RouterGroup, configController controller.
func (c *ConfigRouter) Register() {
c.router.GET("/", c.controller.Find)
c.router.PUT("/", c.controller.Update)
c.router.GET("/captcha", c.controller.FindCaptcha)
}
5 changes: 0 additions & 5 deletions internal/service/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
)

type IConfigService interface {
FindAll() (cfg config.PlatformCfg)
Update(req request.ConfigUpdateRequest) (err error)
}

Expand All @@ -18,10 +17,6 @@ func NewConfigService(appRepository *repository.Repository) IConfigService {
return &ConfigService{}
}

func (c *ConfigService) FindAll() (cfg config.PlatformCfg) {
return *(config.PltCfg())
}

func (c *ConfigService) Update(req request.ConfigUpdateRequest) (err error) {
config.PltCfg().Site.Title = req.Site.Title
config.PltCfg().Site.Description = req.Site.Description
Expand Down
1 change: 1 addition & 0 deletions internal/utils/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ const (
MediaPath = "./media"
FilesPath = "./files"
CapturesPath = "./captures"
FrontendPath = "./dist"
)
2 changes: 1 addition & 1 deletion web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cloudsdale</title>
<title>{{ Cloudsdale.Title }}</title>
</head>
<body>
<div id="root"></div>
Expand Down
3 changes: 3 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
"prismjs": "^1.29.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-router-dom": "^6.23.1",
"react-turnstile": "^1.1.3",
"sass": "^1.77.1",
"vite-plugin-pages": "^0.32.1",
"zod": "^3.23.8",
Expand All @@ -48,6 +50,7 @@
"@types/prismjs": "^1.26.4",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/react-google-recaptcha": "^2.1.9",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
8 changes: 8 additions & 0 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ function App() {
});
}, [configStore.refresh]);

// Get captcha config
useEffect(() => {
configApi.getCaptchaCfg().then((res) => {
const r = res.data;
configStore.setCaptchaCfg(r.data);
});
}, []);

// Get exists categories
useEffect(() => {
categoryApi.getCategories().then((res) => {
Expand Down
5 changes: 5 additions & 0 deletions web/src/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ export function useConfigApi() {
return auth.put("/configs/", request);
};

const getCaptchaCfg = () => {
return auth.get("/configs/captcha");
};

return {
getPltCfg,
updatePltCfg,
getCaptchaCfg,
};
}
23 changes: 6 additions & 17 deletions web/src/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
UserCreateRequest,
UserDeleteRequest,
UserFindRequest,
UserLoginRequest,
UserRegisterRequest,
UserUpdateRequest,
} from "@/types/user";
import { useApi, useAuth } from "@/utils/axios";
Expand All @@ -11,25 +13,12 @@ export function useUserApi() {
const api = useApi();
const auth = useAuth();

const login = (username: string, password: string) => {
return api.post("/users/login", {
username,
password,
});
const login = (request: UserLoginRequest) => {
return api.post("/users/login", request);
};

const register = (
username: string,
nickname: string,
email: string,
password: string
) => {
return api.post("/users/register", {
username,
nickname,
email,
password,
});
const register = (request: UserRegisterRequest) => {
return api.post("/users/register", request);
};

const getUsers = (request: UserFindRequest) => {
Expand Down
Loading

0 comments on commit a622a06

Please sign in to comment.