From b30d9db69d287cdca1a06b9dee8fd7338c473cbd Mon Sep 17 00:00:00 2001 From: Asai Neko Date: Thu, 25 Dec 2025 21:30:26 +0800 Subject: [PATCH] Auto reg user, event map Signed-off-by: Asai Neko --- data/data.go | 2 +- data/event.go | 12 ++++++++++++ data/user.go | 26 ++++++++++++++------------ go.mod | 4 ++++ go.sum | 10 ++++++++++ service/auth/magic.go | 40 ++++++++++++++++++++++++++++++++++------ service/auth/refresh.go | 7 +++---- service/user/checkin.go | 28 ++++++++++++++++++++++------ service/user/info.go | 21 +++++++++++++-------- service/user/update.go | 7 +++---- 10 files changed, 116 insertions(+), 41 deletions(-) diff --git a/data/data.go b/data/data.go index 783bb34..6a1089b 100644 --- a/data/data.go +++ b/data/data.go @@ -32,7 +32,7 @@ func Init() { } // Auto migrate - err = db.DB.AutoMigrate(&User{}) + err = db.DB.AutoMigrate(&User{}, &Event{}) if err != nil { log.Error("[Database] Error migrating database: ", err) } diff --git a/data/event.go b/data/event.go index 8cd11a0..14c666f 100644 --- a/data/event.go +++ b/data/event.go @@ -14,3 +14,15 @@ type Event struct { StartTime time.Time `json:"start_time" gorm:"index"` EndTime time.Time `json:"end_time" gorm:"index"` } + +func (self *Event) GetEventById(eventId uuid.UUID) error { + return nil +} + +func (self *Event) UpdateEventById(eventId uuid.UUID) error { + return nil +} + +func (self *Event) CreateEvent() error { + return nil +} diff --git a/data/user.go b/data/user.go index 8f3bf6e..a2a91f0 100644 --- a/data/user.go +++ b/data/user.go @@ -4,6 +4,7 @@ import ( "time" "github.com/google/uuid" + "gorm.io/datatypes" "gorm.io/gorm" "gorm.io/gorm/clause" ) @@ -15,16 +16,17 @@ import ( // Super User: 30 type User struct { - Id uint `json:"id" gorm:"primarykey;autoincrement"` - UUID uuid.UUID `json:"uuid" gorm:"type:uuid;uniqueindex;not null"` - UserId uuid.UUID `json:"user_id" gorm:"type:uuid;uniqueindex;not null"` - Email string `json:"email" gorm:"type:varchar(255);uniqueindex;not null"` - Type string `json:"type" gorm:"type:varchar(32);index;not null"` - Nickname string `json:"nickname"` - Subtitle string `json:"subtitle"` - Avatar string `json:"avatar"` - Checkin time.Time `json:"checkin" gorm:"index"` - PermissionLevel uint `json:"permission_level" gorm:"default:10;not null"` + Id uint `json:"id" gorm:"primarykey;autoincrement"` + UUID uuid.UUID `json:"uuid" gorm:"type:uuid;uniqueindex;not null"` + UserId uuid.UUID `json:"user_id" gorm:"type:uuid;uniqueindex;not null"` + Email string `json:"email" gorm:"type:varchar(255);uniqueindex;not null"` + Type string `json:"type" gorm:"type:varchar(32);index;not null"` + Nickname string `json:"nickname"` + Subtitle string `json:"subtitle"` + Avatar string `json:"avatar"` + Checkin datatypes.JSONMap `json:"checkin"` + JoinedEvent datatypes.JSONSlice[uuid.UUID] `json:"joined_event"` + PermissionLevel uint `json:"permission_level" gorm:"default:10;not null"` } func (self *User) GetByEmail(email string) error { @@ -41,7 +43,7 @@ func (self *User) GetByUserId(userId uuid.UUID) error { return nil } -func (self *User) UpdateCheckin(userId uuid.UUID, time time.Time) error { +func (self *User) UpdateCheckin(userId uuid.UUID, eventId uuid.UUID, time time.Time) error { return Database.Transaction(func(tx *gorm.DB) error { if err := tx. Clauses(clause.Locking{Strength: "UPDATE"}). @@ -50,7 +52,7 @@ func (self *User) UpdateCheckin(userId uuid.UUID, time time.Time) error { return err // if error then rollback } - self.Checkin = time + self.Checkin = datatypes.JSONMap{eventId.String(): time} if err := tx.Save(self).Error; err != nil { return err // rollback diff --git a/go.mod b/go.mod index dd3d69e..7191380 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module nixcn-cms go 1.25.5 require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.14.2 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect @@ -16,6 +17,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.29.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.1 // indirect @@ -59,6 +61,8 @@ require ( golang.org/x/text v0.32.0 // indirect golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.11 // indirect + gorm.io/datatypes v1.2.7 // indirect + gorm.io/driver/mysql v1.5.6 // indirect gorm.io/driver/postgres v1.6.0 // indirect gorm.io/gorm v1.31.1 // indirect ) diff --git a/go.sum b/go.sum index e826074..fbfe72b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= @@ -26,6 +28,9 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk= github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= @@ -130,7 +135,12 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk= +gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY= +gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= +gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/service/auth/magic.go b/service/auth/magic.go index 61f37b7..dfc709d 100644 --- a/service/auth/magic.go +++ b/service/auth/magic.go @@ -1,14 +1,15 @@ package auth import ( - "net/http" "nixcn-cms/data" "nixcn-cms/internal/cryptography" "nixcn-cms/pkgs/email" "nixcn-cms/pkgs/magiclink" "nixcn-cms/pkgs/turnstile" + "github.com/google/uuid" log "github.com/sirupsen/logrus" + "gorm.io/gorm" "github.com/gin-gonic/gin" "github.com/spf13/viper" @@ -74,14 +75,41 @@ func VerifyMagicLink(c *gin.Context) { return } - // Generate jwt - userInfo := new(data.User) - err := userInfo.GetByEmail(email) + // Verify if user exists + user := new(data.User) + err := user.GetByEmail(email) + if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{"status": "user not found"}) + if err == gorm.ErrRecordNotFound { + // Create user + newUUID, err := uuid.NewUUID() + if err != nil { + c.JSON(500, gin.H{"status": "internal server error"}) + return + } + newUserId, err := uuid.NewUUID() + if err != nil { + c.JSON(500, gin.H{"status": "internal server error"}) + return + } + user.UUID = newUUID + user.UserId = newUserId + user.Email = email + user.Type = "Normal" + user.PermissionLevel = 10 + if err := user.Create(); err != nil { + c.JSON(500, gin.H{"status": "internal server error"}) + return + } + } else { + c.JSON(500, gin.H{"status": "internal server error"}) + return + } } + + // Generate jwt JwtTool := cryptography.Token{ - UserID: userInfo.UserId, + UserID: user.UserId, Application: viper.GetString("server.application"), } accessToken, refreshToken, err := JwtTool.IssueTokens() diff --git a/service/auth/refresh.go b/service/auth/refresh.go index 1505b20..b9d9402 100644 --- a/service/auth/refresh.go +++ b/service/auth/refresh.go @@ -1,7 +1,6 @@ package auth import ( - "net/http" "nixcn-cms/internal/cryptography" "github.com/gin-gonic/gin" @@ -14,7 +13,7 @@ func Refresh(c *gin.Context) { } if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) + c.JSON(400, gin.H{"error": "invalid request"}) return } @@ -24,11 +23,11 @@ func Refresh(c *gin.Context) { access, err := JwtTool.RefreshAccessToken(req.RefreshToken) if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid refresh token"}) + c.JSON(401, gin.H{"error": "invalid refresh token"}) return } - c.JSON(http.StatusOK, gin.H{ + c.JSON(200, gin.H{ "access_token": access, }) } diff --git a/service/user/checkin.go b/service/user/checkin.go index 6a2fd5b..54ab139 100644 --- a/service/user/checkin.go +++ b/service/user/checkin.go @@ -1,7 +1,6 @@ package user import ( - "net/http" "nixcn-cms/data" "time" @@ -9,17 +8,34 @@ import ( "github.com/google/uuid" ) -func Checkin(ctx *gin.Context) { +func Checkin(c *gin.Context) { data := new(data.User) - userId, ok := ctx.Get("user_id") + userId, ok := c.Get("user_id") if !ok { - ctx.JSON(http.StatusUnauthorized, gin.H{ + c.JSON(401, gin.H{ "status": "unauthorized", }) return } - data.UpdateCheckin(userId.(uuid.UUID), time.Now()) - ctx.JSON(http.StatusOK, gin.H{ + + // Get event id from query + eventIdOrig, ok := c.GetQuery("event_id") + if !ok { + c.JSON(403, gin.H{ + "status": "undefinded event id", + }) + return + } + + // Parse event id to uuid + eventId, err := uuid.Parse(eventIdOrig) + if err != nil { + c.JSON(500, gin.H{ + "status": "error parsing string to uuid", + }) + } + data.UpdateCheckin(userId.(uuid.UUID), eventId, time.Now()) + c.JSON(200, gin.H{ "status": "success", }) } diff --git a/service/user/info.go b/service/user/info.go index d706238..ea1d7f5 100644 --- a/service/user/info.go +++ b/service/user/info.go @@ -1,8 +1,8 @@ package user import ( - "net/http" "nixcn-cms/data" + "time" "github.com/gin-gonic/gin" "github.com/google/uuid" @@ -12,30 +12,35 @@ func Info(c *gin.Context) { data := new(data.User) userId, ok := c.Get("user_id") if !ok { - c.JSON(http.StatusUnauthorized, gin.H{ + c.JSON(403, gin.H{ "status": "user not found", }) return } err := data.GetByUserId(userId.(uuid.UUID)) if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{ + c.JSON(403, gin.H{ "status": "user not found", }) return } - var checkinTime any = nil - if !data.Checkin.IsZero() { - checkinTime = data.Checkin + + // Set time nil if time is zero + for k, v := range data.Checkin { + if t, ok := v.(time.Time); ok && t.IsZero() { + data.Checkin[k] = nil + } } - c.JSON(http.StatusOK, gin.H{ + + c.JSON(200, gin.H{ "user_id": data.UserId, "email": data.Email, "type": data.Type, "nickname": data.Nickname, "subtitle": data.Subtitle, "avatar": data.Avatar, - "checkin": checkinTime, + "checkin": data.Checkin, + "joined_event": data.JoinedEvent, "permission_level": data.PermissionLevel, }) } diff --git a/service/user/update.go b/service/user/update.go index 87e2b72..27f8746 100644 --- a/service/user/update.go +++ b/service/user/update.go @@ -1,7 +1,6 @@ package user import ( - "net/http" "nixcn-cms/data" "github.com/gin-gonic/gin" @@ -16,7 +15,7 @@ func Update(c *gin.Context) { user := new(data.User) userId, ok := c.Get("user_id") if !ok { - c.JSON(http.StatusUnauthorized, gin.H{ + c.JSON(403, gin.H{ "status": "can not found user id", }) return @@ -27,7 +26,7 @@ func Update(c *gin.Context) { // Reject permission 0 user if user.PermissionLevel == 0 { - c.JSON(http.StatusForbidden, gin.H{ + c.JSON(403, gin.H{ "status": "premission denied", }) return @@ -41,7 +40,7 @@ func Update(c *gin.Context) { // Update user info user.UpdateByUserID(userId.(uuid.UUID), &ReqInfo) - c.JSON(http.StatusOK, gin.H{ + c.JSON(200, gin.H{ "status": "success", }) }