From b3fe91444dde6063f2b9d0b339ffde24999d7c91 Mon Sep 17 00:00:00 2001 From: Asai Neko Date: Sun, 28 Dec 2025 01:05:47 +0800 Subject: [PATCH] Add event query api Signed-off-by: Asai Neko --- .env.development | 2 +- config/types.go | 2 +- internal/cryptography/token.go | 56 +++++++++++++++++----------------- service/auth/magic.go | 3 +- service/auth/refresh.go | 13 +++----- service/event/handler.go | 8 ++++- service/event/query.go | 36 ++++++++++++++++++++++ service/user/handler.go | 2 +- 8 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 service/event/query.go diff --git a/.env.development b/.env.development index 359739c..928e6ff 100644 --- a/.env.development +++ b/.env.development @@ -28,5 +28,5 @@ SECRETS_TURNSTILE_SECRET=0x4AAAAAACI5pgVONOZ0rzyAYsdUcoOBF8w TTL_MAGIC_LINK_TTL=10m TTL_ACCESS_TTL=15s -TTL_REFRESH_TTL=7d +TTL_REFRESH_TTL=168h TTL_CHECKIN_COSE_TTL=10m diff --git a/config/types.go b/config/types.go index 9731473..3e68154 100644 --- a/config/types.go +++ b/config/types.go @@ -51,7 +51,7 @@ type secrets struct { type ttl struct { MagicLinkTTL string `yaml:"magic_link_ttl"` - JwtTTL string `yaml:"jwt_ttl"` + AccessTTL string `yaml:"access_ttl"` RefreshTTL string `yaml:"refresh_ttl"` CheckinCodeTTL string `yaml:"checkin_code_ttl"` } diff --git a/internal/cryptography/token.go b/internal/cryptography/token.go index 364075b..e49551a 100644 --- a/internal/cryptography/token.go +++ b/internal/cryptography/token.go @@ -4,19 +4,16 @@ import ( "context" "crypto/rand" "encoding/base64" - "errors" "fmt" "nixcn-cms/data" "time" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" - "github.com/redis/go-redis/v9" "github.com/spf13/viper" ) type Token struct { - UserID uuid.UUID Application string } @@ -26,9 +23,9 @@ type JwtClaims struct { } // Generate jwt clames -func (self *Token) NewClaims() JwtClaims { +func (self *Token) NewClaims(userId uuid.UUID) JwtClaims { return JwtClaims{ - UserID: self.UserID, + UserID: userId, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(viper.GetDuration("ttl.access_ttl"))), IssuedAt: jwt.NewNumericDate(time.Now()), @@ -38,8 +35,8 @@ func (self *Token) NewClaims() JwtClaims { } // Generate access token -func (self *Token) GenerateAccessToken() (string, error) { - claims := self.NewClaims() +func (self *Token) GenerateAccessToken(userId uuid.UUID) (string, error) { + claims := self.NewClaims(userId) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) secret := viper.GetString("secrets.jwt_secret") signedToken, err := token.SignedString([]byte(secret)) @@ -59,9 +56,9 @@ func (self *Token) GenerateRefreshToken() (string, error) { } // Issue both access and refresh token -func (self *Token) IssueTokens() (string, string, error) { +func (self *Token) IssueTokens(userId uuid.UUID) (string, string, error) { // Gen atk - access, err := self.GenerateAccessToken() + access, err := self.GenerateAccessToken(userId) if err != nil { return "", "", err } @@ -80,14 +77,14 @@ func (self *Token) IssueTokens() (string, string, error) { if err := data.Redis.Set( ctx, "refresh:"+refresh, - self.UserID.String(), + userId.String(), ttl, ).Err(); err != nil { return "", "", err } // user -> refresh tokens - userSetKey := "user:" + self.UserID.String() + ":refresh_tokens" + userSetKey := "user:" + userId.String() + ":refresh_tokens" if err := data.Redis.SAdd( ctx, @@ -109,49 +106,52 @@ func (self *Token) RefreshAccessToken(refreshToken string) (string, error) { ctx := context.Background() key := "refresh:" + refreshToken - userIDStr, err := data.Redis.Get(ctx, key).Result() - if err != nil { - if err == redis.Nil { - return "", errors.New("invalid refresh token") - } - return "", err - } - - userID, err := uuid.Parse(userIDStr) + userIdStr, err := data.Redis.Get(ctx, key).Result() if err != nil { return "", err } - self.UserID = userID + userId, err := uuid.Parse(userIdStr) + if err != nil { + return "", err + } // Generate access token - return self.GenerateAccessToken() + return self.GenerateAccessToken(userId) } func (self *Token) RenewRefreshToken(refreshToken string) (string, error) { - err := self.RevokeRefreshToken(refreshToken) + ctx := context.Background() + ttl := viper.GetDuration("ttl.refresh_ttl") + + key := "refresh:" + refreshToken + userIdStr, err := data.Redis.Get(ctx, key).Result() if err != nil { return "", err } refresh, err := self.GenerateRefreshToken() + if err != nil { + return "", err + } - // Store to redis - ctx := context.Background() - ttl := viper.GetDuration("ttl.refresh_ttl") + err = self.RevokeRefreshToken(refreshToken) + if err != nil { + return "", err + } // refresh -> user if err := data.Redis.Set( ctx, "refresh:"+refresh, - self.UserID.String(), + userIdStr, ttl, ).Err(); err != nil { return "", err } // user -> refresh tokens - userSetKey := "user:" + self.UserID.String() + ":refresh_tokens" + userSetKey := "user:" + userIdStr + ":refresh_tokens" if err := data.Redis.SAdd( ctx, diff --git a/service/auth/magic.go b/service/auth/magic.go index fd9845b..68fa761 100644 --- a/service/auth/magic.go +++ b/service/auth/magic.go @@ -99,10 +99,9 @@ func VerifyMagicLink(c *gin.Context) { // Generate jwt JwtTool := cryptography.Token{ - UserID: user.UserId, Application: viper.GetString("server.application"), } - accessToken, refreshToken, err := JwtTool.IssueTokens() + accessToken, refreshToken, err := JwtTool.IssueTokens(user.UserId) if err != nil { c.JSON(500, gin.H{ "status": "error generating tokens", diff --git a/service/auth/refresh.go b/service/auth/refresh.go index 0aa7dde..f817a88 100644 --- a/service/auth/refresh.go +++ b/service/auth/refresh.go @@ -13,7 +13,7 @@ func Refresh(c *gin.Context) { } if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(400, gin.H{"error": "invalid request"}) + c.JSON(400, gin.H{"status": "invalid request"}) return } @@ -23,18 +23,13 @@ func Refresh(c *gin.Context) { access, err := JwtTool.RefreshAccessToken(req.RefreshToken) if err != nil { - c.JSON(401, gin.H{"error": "invalid refresh token"}) + c.JSON(401, gin.H{"status": "invalid refresh token"}) return } - err = JwtTool.RevokeRefreshToken(req.RefreshToken) + refresh, err := JwtTool.RenewRefreshToken(req.RefreshToken) if err != nil { - c.JSON(500, gin.H{"status": "cannot revoke refresh token"}) - } - - refresh, err := JwtTool.GenerateRefreshToken() - if err != nil { - c.JSON(401, gin.H{"status": "cannot generate new refresh token"}) + c.JSON(500, gin.H{"statis": "error renew refresh token"}) } c.JSON(200, gin.H{ diff --git a/service/event/handler.go b/service/event/handler.go index 3496e9d..d339294 100644 --- a/service/event/handler.go +++ b/service/event/handler.go @@ -1,7 +1,13 @@ package event -import "github.com/gin-gonic/gin" +import ( + "nixcn-cms/middleware" + + "github.com/gin-gonic/gin" +) func Handler(r *gin.RouterGroup) { + r.Use(middleware.JWTAuth()) r.GET("/info", Info) + r.GET("/query", Query) } diff --git a/service/event/query.go b/service/event/query.go new file mode 100644 index 0000000..d1879c1 --- /dev/null +++ b/service/event/query.go @@ -0,0 +1,36 @@ +package event + +import ( + "nixcn-cms/data" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +func Query(c *gin.Context) { + userId, ok := c.Get("user_id") + if !ok { + c.JSON(400, gin.H{"status": "could not found user_id"}) + return + } + eventId, ok := c.GetQuery("event_id") + if !ok { + c.JSON(400, gin.H{"status": "could not found event_id"}) + return + } + + data := new(data.User) + err := data.GetByUserId(userId.(uuid.UUID)) + if err != nil { + c.JSON(404, gin.H{"status": "cannot found user"}) + return + } + if data.Checkin[eventId] == nil { + c.JSON(404, gin.H{"status": "cannot found user checked in"}) + return + } + + c.JSON(200, gin.H{ + "checkin_time": data.Checkin[eventId], + }) +} diff --git a/service/user/handler.go b/service/user/handler.go index bb73682..9b10ee5 100644 --- a/service/user/handler.go +++ b/service/user/handler.go @@ -9,7 +9,7 @@ import ( func Handler(r *gin.RouterGroup) { r.Use(middleware.JWTAuth()) r.GET("/info", Info) - r.POST("/checkin", Checkin) + r.GET("/checkin", Checkin) r.POST("/checkin/submit", CheckinSubmit) r.PATCH("/update", Update) r.GET("/list", List)