From bd726f80eacb931942bc051ff9caa2da0363864f Mon Sep 17 00:00:00 2001 From: Asai Neko Date: Thu, 25 Dec 2025 00:37:02 +0800 Subject: [PATCH] Impl magic login logic && checkin logic Signed-off-by: Asai Neko --- config.default.yaml | 7 ++++--- config/types.go | 7 ++++--- data/user.go | 4 ++-- internal/crypto/jwt/jwt.go | 4 ++-- pkgs/magiclink/magiclink.go | 6 ++++-- pkgs/turnstile/turnstile.go | 2 +- service/auth/magic.go | 3 +-- service/checkin/checkin.go | 24 ++++++++++++++++++++++++ service/info/handler.go | 11 +++++++++++ service/info/userinfo.go | 1 + 10 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 service/checkin/checkin.go create mode 100644 service/info/handler.go create mode 100644 service/info/userinfo.go diff --git a/config.default.yaml b/config.default.yaml index e90b85d..ab2b344 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -13,7 +13,8 @@ email: resend_api_key: abc from: secrets: - jwt: something - turnstile: something + jwt_secret: something + turnstile_secret: something ttl: - magic_link: 15000 + magic_link_ttl: 15000 + jwt_ttl: 86400000 diff --git a/config/types.go b/config/types.go index 6a30d14..58eb947 100644 --- a/config/types.go +++ b/config/types.go @@ -29,10 +29,11 @@ type email struct { } type secrets struct { - jwt string `yaml:"jwt"` - turnstile string `yaml:"turnstile"` + jwt_secret string `yaml:"jwt_secret"` + turnstile_secret string `yaml:"turnstile_secret"` } type ttl struct { - magic_token string `yaml:"magin_token"` + magic_link_ttl string `yaml:"magic_link_ttl"` + jwt_ttl string `yaml:"jwt_ttl"` } diff --git a/data/user.go b/data/user.go index 2bb3852..d55c4c4 100644 --- a/data/user.go +++ b/data/user.go @@ -28,8 +28,8 @@ func (self *User) GetByUserId(userId string) error { return nil } -func (self *User) SetCheckinState(email string, state bool) error { - if err := Database.Where("email = ?", email).First(&self).Error; err != nil { +func (self *User) SetCheckinState(userId uuid.UUID, state bool) error { + if err := Database.Where("user_id = ?", userId).First(&self).Error; err != nil { return err } self.Checkin = state diff --git a/internal/crypto/jwt/jwt.go b/internal/crypto/jwt/jwt.go index bb5cbc5..ab640ea 100644 --- a/internal/crypto/jwt/jwt.go +++ b/internal/crypto/jwt/jwt.go @@ -17,7 +17,7 @@ type Claims struct { } func JWTAuth() gin.HandlerFunc { - var JwtSecret = []byte(viper.GetString("secrets.jwt")) + var JwtSecret = []byte(viper.GetString("secrets.jwt_secret")) return func(c *gin.Context) { auth := c.GetHeader("Authorization") if auth == "" { @@ -66,7 +66,7 @@ func GenerateToken(userID uuid.UUID, application string) (string, error) { claims := Claims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(viper.GetDuration("ttl.jwt_ttl"))), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: application, }, diff --git a/pkgs/magiclink/magiclink.go b/pkgs/magiclink/magiclink.go index 44b037b..dc394c8 100644 --- a/pkgs/magiclink/magiclink.go +++ b/pkgs/magiclink/magiclink.go @@ -5,6 +5,8 @@ import ( "encoding/base64" "sync" "time" + + "github.com/spf13/viper" ) type Token struct { @@ -17,7 +19,7 @@ var ( ) // Generate magic token -func NewMagicToken(email string, ttl time.Duration) (string, error) { +func NewMagicToken(email string) (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { return "", err @@ -27,7 +29,7 @@ func NewMagicToken(email string, ttl time.Duration) (string, error) { store.Store(token, Token{ Email: email, - ExpiresAt: time.Now().Add(ttl), + ExpiresAt: time.Now().Add(viper.GetDuration("ttl.magic_link_ttl")), }) return token, nil diff --git a/pkgs/turnstile/turnstile.go b/pkgs/turnstile/turnstile.go index 9b07b41..70c3b91 100644 --- a/pkgs/turnstile/turnstile.go +++ b/pkgs/turnstile/turnstile.go @@ -10,7 +10,7 @@ import ( func VerifyTurnstile(token, ip string) (bool, error) { form := url.Values{} - form.Set("secret", viper.GetString("secrets.turnstile")) + form.Set("secret", viper.GetString("secrets.turnstile_secret")) form.Set("response", token) form.Set("remoteip", ip) diff --git a/service/auth/magic.go b/service/auth/magic.go index 847aa7d..d71c23e 100644 --- a/service/auth/magic.go +++ b/service/auth/magic.go @@ -5,7 +5,6 @@ import ( "nixcn-cms/internal/crypto/jwt" "nixcn-cms/pkgs/magiclink" "nixcn-cms/pkgs/turnstile" - "time" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -35,7 +34,7 @@ func RequestMagicLink(c *gin.Context) { } // Generate magic token - token, err := magiclink.NewMagicToken(req.Email, 15*time.Minute) + token, err := magiclink.NewMagicToken(req.Email) if err != nil { c.JSON(500, gin.H{"error": "internal error"}) return diff --git a/service/checkin/checkin.go b/service/checkin/checkin.go new file mode 100644 index 0000000..77b21b4 --- /dev/null +++ b/service/checkin/checkin.go @@ -0,0 +1,24 @@ +package checkin + +import ( + "net/http" + "nixcn-cms/data" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +func Checkin(ctx *gin.Context) { + data := new(data.User) + userId, ok := ctx.Get("user_id") + if !ok { + ctx.JSON(http.StatusUnauthorized, gin.H{ + "status": "unauthorized", + }) + return + } + data.SetCheckinState(userId.(uuid.UUID), true) + ctx.JSON(http.StatusOK, gin.H{ + "status": "success", + }) +} diff --git a/service/info/handler.go b/service/info/handler.go new file mode 100644 index 0000000..82dbcc9 --- /dev/null +++ b/service/info/handler.go @@ -0,0 +1,11 @@ +package info + +import ( + "nixcn-cms/internal/crypto/jwt" + + "github.com/gin-gonic/gin" +) + +func Handler(r *gin.RouterGroup) { + r.Use(jwt.JWTAuth()) +} diff --git a/service/info/userinfo.go b/service/info/userinfo.go new file mode 100644 index 0000000..e612023 --- /dev/null +++ b/service/info/userinfo.go @@ -0,0 +1 @@ +package info