146 lines
4.0 KiB
Go
146 lines
4.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"github.com/ArtalkJS/Artalk/internal/captcha"
|
|
"github.com/ArtalkJS/Artalk/internal/core"
|
|
"github.com/ArtalkJS/Artalk/internal/i18n"
|
|
"github.com/ArtalkJS/Artalk/internal/limiter"
|
|
"github.com/ArtalkJS/Artalk/internal/log"
|
|
"github.com/ArtalkJS/Artalk/server/common"
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
func Captcha(app *core.App, router fiber.Router) {
|
|
ca := router.Group("/captcha/", func(c *fiber.Ctx) error {
|
|
if !app.Conf().Captcha.Enabled {
|
|
return common.RespError(c, "Captcha disabled")
|
|
}
|
|
return c.Next()
|
|
})
|
|
{
|
|
ca.Post("/refresh", captchaGet(app))
|
|
ca.Get("/get", captchaGet(app))
|
|
ca.Post("/get", captchaGet(app))
|
|
ca.Post("/check", captchaCheck(app))
|
|
ca.Post("/status", captchaStatus(app))
|
|
}
|
|
}
|
|
|
|
// @Summary Captcha Status
|
|
// @Description Get the status of the user's captcha verification
|
|
// @Tags Captcha
|
|
// @Success 200 {object} common.JSONResult{data=object{is_pass=bool}}
|
|
// @Router /captcha/status [post]
|
|
func captchaStatus(app *core.App) func(c *fiber.Ctx) error {
|
|
return func(c *fiber.Ctx) error {
|
|
limiter, err := common.GetLimiter[limiter.Limiter](c)
|
|
if limiter == nil {
|
|
return err
|
|
}
|
|
|
|
return common.RespData(c, common.Map{"is_pass": limiter.IsPass(c.IP())})
|
|
}
|
|
}
|
|
|
|
// @Summary Captcha Get
|
|
// @Description Get a base64 encoded captcha image or a HTML page to verify for user
|
|
// @Tags Captcha
|
|
// @Success 200 {object} common.JSONResult{data=object{img_data=string}}
|
|
// @Router /captcha/refresh [post]
|
|
// @Router /captcha/get [get]
|
|
// @Router /captcha/get [post]
|
|
func captchaGet(app *core.App) func(c *fiber.Ctx) error {
|
|
return func(c *fiber.Ctx) error {
|
|
// create new captcha checker instance
|
|
cap := common.NewCaptchaChecker(app, c)
|
|
|
|
// get new captcha
|
|
got, err := cap.Get()
|
|
if err != nil {
|
|
return common.RespError(c, "captcha generate err: "+err.Error())
|
|
}
|
|
|
|
// response captcha
|
|
switch cap.Type() {
|
|
case captcha.Image:
|
|
return common.RespData(c, common.Map{
|
|
"img_data": string(got),
|
|
})
|
|
|
|
case captcha.IFrame:
|
|
c.Set(fiber.HeaderCacheControl, "no-cache, no-store, must-revalidate") // disable cache
|
|
c.Set(fiber.HeaderPragma, "no-cache")
|
|
c.Set(fiber.HeaderExpires, "0")
|
|
c.Set(fiber.HeaderContentType, "text/html") // response html body
|
|
return c.Send(got)
|
|
}
|
|
|
|
return common.RespError(c, "invalid captcha type")
|
|
}
|
|
}
|
|
|
|
type ParamsCaptchaCheck struct {
|
|
Value string `form:"value" validate:"required"`
|
|
}
|
|
|
|
// @Summary Captcha Check
|
|
// @Description Verify user enters correct captcha code
|
|
// @Tags Captcha
|
|
// @Param value formData string true "the captcha value to check"
|
|
// @Success 200 {object} common.JSONResult
|
|
// @Failure 400 {object} common.JSONResult{data=object{img_data=string}}
|
|
// @Router /captcha/check [post]
|
|
func captchaCheck(app *core.App) func(c *fiber.Ctx) error {
|
|
return func(c *fiber.Ctx) error {
|
|
// handle user input
|
|
var p ParamsCaptchaCheck
|
|
if isOK, resp := common.ParamsDecode(c, &p); !isOK {
|
|
return resp
|
|
}
|
|
|
|
limiter, err := common.GetLimiter[limiter.Limiter](c)
|
|
if limiter == nil {
|
|
return err
|
|
}
|
|
|
|
// create new captcha checker instance
|
|
cap := common.NewCaptchaChecker(app, c)
|
|
|
|
// check user input
|
|
isPass, err := cap.Check(p.Value)
|
|
|
|
// trigger global event
|
|
if isPass {
|
|
// 验证码正确
|
|
limiter.MarkVerifyPassed(c.IP())
|
|
} else {
|
|
// 验证码错误
|
|
limiter.MarkVerifyFailed(c.IP())
|
|
}
|
|
|
|
// response result
|
|
switch cap.Type() {
|
|
case captcha.Image:
|
|
if !isPass {
|
|
img, err := cap.Get()
|
|
if err != nil {
|
|
return common.RespError(c, "captcha generate err: "+err.Error())
|
|
}
|
|
|
|
return common.RespError(c, i18n.T("Wrong captcha"), common.Map{
|
|
"img_data": string(img),
|
|
})
|
|
}
|
|
case captcha.IFrame:
|
|
if !isPass {
|
|
log.Error("[Captcha] Failed to verify: ", err)
|
|
return common.RespError(c, i18n.T("Verification failed"), common.Map{
|
|
"detail": err.Error(),
|
|
})
|
|
}
|
|
}
|
|
|
|
return common.RespSuccess(c)
|
|
}
|
|
}
|