Files
nixcn-cms/data/user.go
Asai Neko f8a3d0ca45
All checks were successful
Build Backend (NixCN CMS) TeamCity build finished
Build Frontend (NixCN CMS) TeamCity build finished
Remove some useless comments
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-01-06 15:19:54 +08:00

160 lines
3.6 KiB
Go

package data
import (
"github.com/go-viper/mapstructure/v2"
"github.com/google/uuid"
"github.com/meilisearch/meilisearch-go"
"gorm.io/gorm"
)
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"`
Username string `json:"username" gorm:"type:varchar(255);uniqueindex;not null"`
Nickname string `json:"nickname" gorm:"type:text"`
Subtitle string `json:"subtitle" gorm:"type:text"`
Avatar string `json:"avatar" gorm:"type:text"`
Bio string `json:"bio" gorm:"type:text"`
PermissionLevel uint `json:"permission_level" gorm:"default:10;not null"`
AllowPublic bool `json:"allow_public" gorm:"default:false;not null"`
}
type UserSearchDoc struct {
UserId string `json:"user_id"`
Email string `json:"email"`
Username string `json:"username"`
Type string `json:"type"`
Nickname string `json:"nickname"`
Subtitle string `json:"subtitle"`
Avatar string `json:"avatar"`
}
func (self *User) GetByEmail(email string) (*User, error) {
var user User
err := Database.
Where("email = ?", email).
First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, nil
}
func (self *User) GetByUserId(userId uuid.UUID) (*User, error) {
var user User
err := Database.
Where("user_id = ?", userId).
First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &user, err
}
func (self *User) Create() error {
self.UUID = uuid.New()
self.UserId = uuid.New()
// DB transaction only
if err := Database.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(self).Error; err != nil {
return err
}
return nil
}); err != nil {
return err
}
// Search index (eventual consistency)
if err := self.UpdateSearchIndex(); err != nil {
// TODO: async retry / log
return err
}
return nil
}
func (self *User) UpdateByUserID(userId uuid.UUID) error {
return Database.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&User{}).Where("user_id = ?", userId).Updates(&self).Error; err != nil {
return err
}
return nil
})
}
func (self *User) GetFullTable() (*[]User, error) {
var users []User
err := Database.Find(&users).Error
if err != nil {
return nil, err
}
return &users, nil
}
func (self *User) FastListUsers(limit, offset int64) (*[]UserSearchDoc, error) {
index := MeiliSearch.Index("user")
// Fast read from MeiliSearch, no DB involved
result, err := index.Search("", &meilisearch.SearchRequest{
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, err
}
var list []UserSearchDoc
if err := mapstructure.Decode(result.Hits, &list); err != nil {
return nil, err
}
return &list, nil
}
func (self *User) UpdateSearchIndex() error {
doc := UserSearchDoc{
UserId: self.UserId.String(),
Email: self.Email,
Username: self.Username,
Nickname: self.Nickname,
Subtitle: self.Subtitle,
Avatar: self.Avatar,
}
index := MeiliSearch.Index("user")
primaryKey := "user_id"
opts := &meilisearch.DocumentOptions{
PrimaryKey: &primaryKey,
}
if _, err := index.UpdateDocuments(
[]UserSearchDoc{doc},
opts,
); err != nil {
return err
}
return nil
}
func (self *User) DeleteSearchIndex() error {
index := MeiliSearch.Index("user")
_, err := index.DeleteDocument(self.UserId.String(), nil)
return err
}