mirror of https://github.com/veypi/OneAuth.git
feat: simplify user login
parent
545f49c0f3
commit
7e7e6ed506
@ -0,0 +1,49 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"oa/cfg"
|
||||
"oa/models"
|
||||
"time"
|
||||
|
||||
"github.com/veypi/OneBD/rest"
|
||||
)
|
||||
|
||||
var _ = Router.Patch("/:token_id", tokenPatch)
|
||||
|
||||
type patchOpts struct {
|
||||
ID string `json:"id" parse:"path@token_id"`
|
||||
ExpiredAt *time.Time `json:"expired_at" parse:"json"`
|
||||
OverPerm *string `json:"over_perm" parse:"json"`
|
||||
}
|
||||
|
||||
func tokenPatch(x *rest.X) (any, error) {
|
||||
opts := &patchOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &models.Token{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optsMap := make(map[string]interface{})
|
||||
if opts.ExpiredAt != nil {
|
||||
optsMap["expired_at"] = opts.ExpiredAt
|
||||
}
|
||||
if opts.OverPerm != nil {
|
||||
optsMap["over_perm"] = opts.OverPerm
|
||||
}
|
||||
err = cfg.DB().Model(data).Updates(optsMap).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
var _ = Router.Delete("/:token_id", tokenDelete)
|
||||
|
||||
func tokenDelete(x *rest.X) (any, error) {
|
||||
data := &models.Token{}
|
||||
err := cfg.DB().Where("id = ?", x.Params.Get("token_id")).Delete(data).Error
|
||||
return data, err
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
//
|
||||
// get.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-09 17:24
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package token
|
||||
|
||||
import (
|
||||
"oa/cfg"
|
||||
"oa/models"
|
||||
|
||||
"github.com/veypi/OneBD/rest"
|
||||
)
|
||||
|
||||
type getOpts struct {
|
||||
ID string `json:"id" parse:"path@token_id"`
|
||||
}
|
||||
|
||||
var _ = Router.Get("/:token_id", tokenGet)
|
||||
|
||||
func tokenGet(x *rest.X) (any, error) {
|
||||
opts := &getOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &models.Token{}
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
return data, err
|
||||
}
|
||||
|
||||
type listOpts struct {
|
||||
Limit int `json:"limit"`
|
||||
UserID string `json:"user_id" gorm:"index;type:varchar(32)" parse:"query"`
|
||||
AppID string `json:"app_id" gorm:"index;type:varchar(32)" parse:"query"`
|
||||
}
|
||||
|
||||
var _ = Router.Get("/", tokenList)
|
||||
|
||||
func tokenList(x *rest.X) (any, error) {
|
||||
opts := &listOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]*models.Token, 0, 10)
|
||||
|
||||
query := cfg.DB()
|
||||
query = query.Where("user_id = ?", opts.UserID)
|
||||
query = query.Where("app_id = ?", opts.AppID)
|
||||
err = query.Limit(opts.Limit).Find(&data).Error
|
||||
return data, err
|
||||
}
|
||||
@ -1,258 +0,0 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"oa/cfg"
|
||||
"oa/errs"
|
||||
"oa/libs/auth"
|
||||
M "oa/models"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/veypi/OneBD/rest"
|
||||
"github.com/veypi/utils"
|
||||
"github.com/veypi/utils/logv"
|
||||
)
|
||||
|
||||
func useToken(r rest.Router) {
|
||||
r.Post("/salt", tokenSalt)
|
||||
r.Post("/", tokenPost)
|
||||
r.Get("/:token_id", tokenGet)
|
||||
r.Patch("/:token_id", tokenPatch)
|
||||
r.Delete("/:token_id", tokenDelete)
|
||||
r.Get("/", tokenList)
|
||||
}
|
||||
func tokenSalt(x *rest.X) (any, error) {
|
||||
opts := &M.TokenSalt{}
|
||||
err := x.Parse(opts)
|
||||
logv.Warn().Msg(opts.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.User{}
|
||||
query := "username = ?"
|
||||
if opts.Typ == nil {
|
||||
} else if *opts.Typ == "email" {
|
||||
query = "email = ?"
|
||||
} else if *opts.Typ == "phone" {
|
||||
query = "phone = ?"
|
||||
}
|
||||
|
||||
err = cfg.DB().Where(query, opts.Username).First(data).Error
|
||||
return map[string]string{"salt": data.Salt, "id": data.ID}, err
|
||||
}
|
||||
|
||||
// for user login app
|
||||
func tokenPost(x *rest.X) (any, error) {
|
||||
opts := &M.TokenPost{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aid := cfg.Config.ID
|
||||
if opts.AppID != nil && *opts.AppID != "" {
|
||||
aid = *opts.AppID
|
||||
}
|
||||
data := &M.Token{}
|
||||
claim := &auth.Claims{}
|
||||
claim.IssuedAt = jwt.NewNumericDate(time.Now())
|
||||
claim.Issuer = cfg.Config.ID
|
||||
if opts.Refresh != nil {
|
||||
typ := "app"
|
||||
if opts.Typ != nil {
|
||||
typ = *opts.Typ
|
||||
}
|
||||
// for other app redirect
|
||||
refresh, err := auth.ParseJwt(*opts.Refresh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if refresh.ID == "" {
|
||||
return nil, errs.AuthInvalid
|
||||
}
|
||||
err = cfg.DB().Where("id = ?", refresh.ID).First(data).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
claim.AID = aid
|
||||
claim.UID = refresh.UID
|
||||
claim.Name = refresh.Name
|
||||
claim.Icon = refresh.Icon
|
||||
claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10))
|
||||
if typ == "app" {
|
||||
if refresh.AID == aid {
|
||||
// refresh token
|
||||
acList := make(auth.Access, 0, 10)
|
||||
logv.AssertError(cfg.DB().Table("accesses a").
|
||||
Select("a.name, a.t_id, a.level").
|
||||
Joins("INNER JOIN user_roles ur ON ur.role_id = a.role_id AND ur.user_id = ? AND a.app_id = ?", refresh.UID, aid).
|
||||
Scan(&acList).Error)
|
||||
claim.Access = acList
|
||||
if aid == cfg.Config.ID {
|
||||
return auth.GenJwt(claim)
|
||||
}
|
||||
app := &M.App{}
|
||||
err = cfg.DB().Where("id = ?", aid).First(app).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return auth.GenJwtWithKey(claim, app.Key)
|
||||
} else if aid != cfg.Config.ID {
|
||||
// 只能生成其他应用的refresh token
|
||||
newToken := &M.Token{}
|
||||
newToken.UserID = refresh.UID
|
||||
newToken.AppID = aid
|
||||
newToken.ExpiredAt = time.Now().Add(time.Hour * 24)
|
||||
if opts.OverPerm != nil {
|
||||
newToken.OverPerm = *opts.OverPerm
|
||||
}
|
||||
if opts.Device != nil {
|
||||
newToken.Device = *opts.Device
|
||||
}
|
||||
newToken.Ip = x.GetRemoteIp()
|
||||
logv.AssertError(cfg.DB().Create(newToken).Error)
|
||||
// gen other app token
|
||||
claim.ID = newToken.ID
|
||||
claim.ExpiresAt = jwt.NewNumericDate(newToken.ExpiredAt)
|
||||
return auth.GenJwt(claim)
|
||||
} else {
|
||||
// 其他应用获取访问oa的token
|
||||
claim.Access = make(auth.Access, 0, 10)
|
||||
if data.OverPerm != "" {
|
||||
err = json.Unmarshal([]byte(data.OverPerm), &claim.Access)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return auth.GenJwt(claim)
|
||||
}
|
||||
} else if typ == "ufs" {
|
||||
claim.AID = refresh.AID
|
||||
claim.UID = refresh.UID
|
||||
claim.Name = refresh.Name
|
||||
claim.Icon = refresh.Icon
|
||||
claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10))
|
||||
claim.Access = auth.Access{
|
||||
{Name: "fs", TID: "/", Level: auth.Do},
|
||||
}
|
||||
token := logv.AssertFuncErr(auth.GenJwt(claim))
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: "fstoken", // Cookie 的名称
|
||||
Value: token, // Cookie 的值
|
||||
Path: "/fs/u/", // Cookie 的路径,通常是根路径
|
||||
MaxAge: 600, // Cookie 的最大年龄,单位是秒
|
||||
HttpOnly: true, // 是否仅限 HTTP(S) 访问
|
||||
Secure: false, // 是否通过安全连接传输 Cookie
|
||||
}
|
||||
http.SetCookie(x, cookie)
|
||||
|
||||
return token, nil
|
||||
} else {
|
||||
return nil, errs.ArgsInvalid
|
||||
}
|
||||
} else if opts.Code != nil && aid == cfg.Config.ID && opts.Salt != nil && opts.UserID != nil {
|
||||
// for oa login
|
||||
user := &M.User{}
|
||||
err = cfg.DB().Where("id = ?", opts.UserID).Find(user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logv.Info().Str("user", user.ID).Msg("login")
|
||||
code := *opts.Code
|
||||
salt := logv.AssertFuncErr(hex.DecodeString(*opts.Salt))
|
||||
key := logv.AssertFuncErr(hex.DecodeString(user.Code))
|
||||
de, err := utils.AesDecrypt([]byte(code), key, salt)
|
||||
if err != nil || de != user.ID {
|
||||
return nil, errs.AuthFailed
|
||||
}
|
||||
data.UserID = *opts.UserID
|
||||
data.AppID = aid
|
||||
data.ExpiredAt = time.Now().Add(time.Hour * 72)
|
||||
if opts.OverPerm != nil {
|
||||
data.OverPerm = *opts.OverPerm
|
||||
}
|
||||
if opts.Device != nil {
|
||||
data.Device = *opts.Device
|
||||
}
|
||||
data.Ip = x.GetRemoteIp()
|
||||
logv.AssertError(cfg.DB().Create(data).Error)
|
||||
claim.ID = data.ID
|
||||
claim.AID = aid
|
||||
claim.UID = user.ID
|
||||
claim.Name = user.Username
|
||||
claim.Icon = user.Icon
|
||||
claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt)
|
||||
if user.Nickname != "" {
|
||||
claim.Name = user.Nickname
|
||||
}
|
||||
return auth.GenJwt(claim)
|
||||
} else {
|
||||
return nil, errs.ArgsInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func tokenGet(x *rest.X) (any, error) {
|
||||
opts := &M.TokenGet{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.Token{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func tokenPatch(x *rest.X) (any, error) {
|
||||
opts := &M.TokenPatch{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.Token{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optsMap := make(map[string]interface{})
|
||||
if opts.ExpiredAt != nil {
|
||||
optsMap["expired_at"] = opts.ExpiredAt
|
||||
}
|
||||
if opts.OverPerm != nil {
|
||||
optsMap["over_perm"] = opts.OverPerm
|
||||
}
|
||||
err = cfg.DB().Model(data).Updates(optsMap).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func tokenDelete(x *rest.X) (any, error) {
|
||||
opts := &M.TokenDelete{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.Token{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func tokenList(x *rest.X) (any, error) {
|
||||
opts := &M.TokenList{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]*M.Token, 0, 10)
|
||||
|
||||
query := cfg.DB()
|
||||
query = query.Where("user_id = ?", opts.UserID)
|
||||
query = query.Where("app_id = ?", opts.AppID)
|
||||
err = query.Limit(opts.Limit).Find(&data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
//
|
||||
// create.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-06 15:05
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"oa/cfg"
|
||||
M "oa/models"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"math/rand"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/veypi/OneBD/rest"
|
||||
"github.com/veypi/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ = Router.Post("/", userPost)
|
||||
|
||||
type postOpts struct {
|
||||
Username string `json:"username" gorm:"varchar(100);unique;default:not null" parse:"json"`
|
||||
Code string `json:"code" gorm:"varchar(128)" parse:"json"`
|
||||
Nickname *string `json:"nickname" parse:"json"`
|
||||
Icon *string `json:"icon" parse:"json"`
|
||||
Email *string `json:"email" gorm:"varchar(20);unique;default:null" parse:"json"`
|
||||
Phone *string `json:"phone" gorm:"varchar(50);unique;default:null" parse:"json"`
|
||||
}
|
||||
|
||||
func userPost(x *rest.X) (any, error) {
|
||||
opts := &postOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.User{}
|
||||
|
||||
data.ID = strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
data.Username = opts.Username
|
||||
data.Code = opts.Code
|
||||
data.Salt = utils.RandSeq(16)
|
||||
if len(data.Username) < 2 {
|
||||
return nil, rest.ErrArgInvalid.WithArgs("username length")
|
||||
}
|
||||
code, err := base64.URLEncoding.DecodeString(opts.Code)
|
||||
if err != nil || len(code) < 8 {
|
||||
return nil, rest.ErrArgInvalid.WithArgs("code")
|
||||
}
|
||||
code = utils.PKCS7Padding(code, 32)
|
||||
data.Code, err = utils.AesEncrypt([]byte(data.ID), code, []byte(data.Salt))
|
||||
if err != nil {
|
||||
return nil, rest.ErrArgInvalid.WithArgs("code")
|
||||
}
|
||||
ncode, err := utils.AesDecrypt([]byte(data.Code), code, []byte(data.Salt))
|
||||
if err != nil || ncode != data.ID {
|
||||
return nil, rest.ErrInternalServer.AppendString("code decrypt failed")
|
||||
}
|
||||
if opts.Nickname != nil {
|
||||
data.Nickname = *opts.Nickname
|
||||
}
|
||||
if opts.Icon != nil {
|
||||
data.Icon = *opts.Icon
|
||||
} else {
|
||||
data.Icon = fmt.Sprintf("https://public.veypi.com/img/avatar/%04d.jpg", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(220))
|
||||
}
|
||||
if opts.Email != nil {
|
||||
data.Email = *opts.Email
|
||||
}
|
||||
if opts.Phone != nil {
|
||||
data.Phone = *opts.Phone
|
||||
}
|
||||
data.Status = 1
|
||||
err = cfg.DB().Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Create(data).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app := &M.App{}
|
||||
err = tx.Where("id = ?", cfg.Config.ID).First(app).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status := "ok"
|
||||
switch app.Typ {
|
||||
case "private":
|
||||
return rest.ErrNotPermitted.WithArgs("not enable register")
|
||||
case "apply":
|
||||
status = "applying"
|
||||
case "public":
|
||||
}
|
||||
if app.Typ != "public" {
|
||||
}
|
||||
|
||||
return tx.Create(&M.AppUser{
|
||||
UserID: data.ID,
|
||||
AppID: cfg.Config.ID,
|
||||
Status: status,
|
||||
}).Error
|
||||
})
|
||||
return data, err
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
//
|
||||
// login.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-12 17:35
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"oa/cfg"
|
||||
"oa/libs/auth"
|
||||
"oa/models"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/veypi/OneBD/rest"
|
||||
"github.com/veypi/utils"
|
||||
"github.com/veypi/utils/logv"
|
||||
)
|
||||
|
||||
var _ = Router.Post("/login", userLogin)
|
||||
|
||||
type loginOpts struct {
|
||||
UserName string `json:"username" parse:"json"`
|
||||
Code string `json:"code" parse:"json"`
|
||||
Phone *string `json:"phone" parse:"json"`
|
||||
Email *string `json:"email" parse:"json"`
|
||||
Type *string `json:"type" parse:"json"`
|
||||
AppID *string `json:"app_id" parse:"json"`
|
||||
Device *string `json:"device" parse:"json"`
|
||||
}
|
||||
|
||||
func userLogin(x *rest.X) (any, error) {
|
||||
// Implement login logic here
|
||||
// For example, validate user credentials and return a token
|
||||
opts := &loginOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := &models.User{}
|
||||
query := cfg.DB()
|
||||
typ := ""
|
||||
if opts.Type != nil {
|
||||
typ = *opts.Type
|
||||
}
|
||||
switch typ {
|
||||
case "phone":
|
||||
query = query.Where("phone = ?", opts.Phone)
|
||||
case "email":
|
||||
query = query.Where("email = ?", opts.Email)
|
||||
default:
|
||||
query = query.Where("username = ?", opts.UserName)
|
||||
}
|
||||
err = query.First(user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logv.Info().Str("user", user.ID).Msg("login")
|
||||
code, err := base64.URLEncoding.DecodeString(opts.Code)
|
||||
if err != nil {
|
||||
return nil, rest.ErrArgInvalid.WithArgs("code")
|
||||
}
|
||||
logv.Warn().Msgf("code: %s", code)
|
||||
|
||||
ncode, err := utils.AesDecrypt([]byte(user.Code), utils.PKCS7Padding(code, 32), []byte(user.Salt))
|
||||
logv.Warn().Msgf("id: %s\n%s", ncode, user.ID)
|
||||
if err != nil || string(ncode) != user.ID {
|
||||
return nil, rest.ErrNotAuthorized
|
||||
}
|
||||
aid := cfg.Config.ID
|
||||
if opts.AppID != nil && *opts.AppID != "" {
|
||||
aid = *opts.AppID
|
||||
}
|
||||
|
||||
data := &models.Token{}
|
||||
|
||||
data.UserID = user.ID
|
||||
data.AppID = aid
|
||||
data.ExpiredAt = time.Now().Add(time.Hour * 72)
|
||||
if opts.Device != nil {
|
||||
data.Device = *opts.Device
|
||||
}
|
||||
data.Ip = x.GetRemoteIp()
|
||||
logv.AssertError(cfg.DB().Create(data).Error)
|
||||
claim := &auth.Claims{}
|
||||
claim.IssuedAt = jwt.NewNumericDate(time.Now())
|
||||
claim.Issuer = cfg.Config.ID
|
||||
claim.ID = data.ID
|
||||
claim.AID = aid
|
||||
claim.UID = user.ID
|
||||
claim.Name = user.Username
|
||||
claim.Icon = user.Icon
|
||||
claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt)
|
||||
if user.Nickname != "" {
|
||||
claim.Name = user.Nickname
|
||||
}
|
||||
return auth.GenJwt(claim)
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
//
|
||||
// crud.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-06 15:12
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"github.com/veypi/OneBD/rest"
|
||||
"oa/cfg"
|
||||
M "oa/models"
|
||||
)
|
||||
|
||||
var _ = Router.Post("/", userRolePost)
|
||||
|
||||
type postOpts struct {
|
||||
UserID string `json:"user_id" parse:"path"`
|
||||
RoleID string `json:"role_id" parse:"json"`
|
||||
AppID string `json:"app_id" parse:"json"`
|
||||
Status string `json:"status" parse:"json"`
|
||||
}
|
||||
|
||||
func userRolePost(x *rest.X) (any, error) {
|
||||
opts := &postOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.UserRole{}
|
||||
|
||||
data.UserID = opts.UserID
|
||||
data.RoleID = opts.RoleID
|
||||
data.AppID = opts.AppID
|
||||
data.Status = opts.Status
|
||||
err = cfg.DB().Create(data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
//
|
||||
// del.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-06 15:24
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"oa/cfg"
|
||||
"oa/models"
|
||||
|
||||
"github.com/veypi/OneBD/rest"
|
||||
)
|
||||
|
||||
var _ = Router.Delete("/:id", userRoleDelete)
|
||||
|
||||
func userRoleDelete(x *rest.X) (any, error) {
|
||||
id := x.Params.Get("id")
|
||||
if id == "" {
|
||||
return nil, rest.ErrArgInvalid.WithArgs("id")
|
||||
}
|
||||
data := &models.UserRole{}
|
||||
err := cfg.DB().Where("id = ?", id).Delete(data).Error
|
||||
return data, err
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
//
|
||||
// get.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-06 15:21
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"oa/cfg"
|
||||
M "oa/models"
|
||||
|
||||
"github.com/veypi/OneBD/rest"
|
||||
)
|
||||
|
||||
var _ = Router.Get("/:id", `
|
||||
get user role
|
||||
`, userRoleGet)
|
||||
|
||||
func userRoleGet(x *rest.X) (any, error) {
|
||||
data := &M.UserRole{}
|
||||
err := cfg.DB().Where("id = ?", x.Params.Get("id")).First(data).Error
|
||||
return data, err
|
||||
}
|
||||
|
||||
var _ = Router.Get("/", `
|
||||
list user roles
|
||||
`, userRoleList)
|
||||
|
||||
type listOpts struct {
|
||||
UserID *string `json:"user_id" parse:"path"`
|
||||
RoleID *string `json:"role_id" parse:"query"`
|
||||
AppID *string `json:"app_id" parse:"query"`
|
||||
Status *string `json:"status" parse:"query"`
|
||||
}
|
||||
|
||||
func userRoleList(x *rest.X) (any, error) {
|
||||
opts := &listOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]*struct {
|
||||
M.UserRole
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Icon string `json:"icon"`
|
||||
RoleName string `json:"role_name"`
|
||||
}, 0, 10)
|
||||
// data := make([]*M.UserRole, 0, 10)
|
||||
|
||||
query := cfg.DB().Debug().Table("user_roles").Select("user_roles.*,users.username,users.nickname,users.icon,roles.name as role_name").
|
||||
Joins("JOIN users ON users.id = user_roles.user_id").
|
||||
Joins("JOIN roles ON roles.id = user_roles.role_id")
|
||||
if opts.UserID != nil && *opts.UserID != "-" {
|
||||
query = query.Where("user_id LIKE ?", opts.UserID)
|
||||
}
|
||||
if opts.RoleID != nil {
|
||||
query = query.Where("role_id LIKE ?", opts.RoleID)
|
||||
}
|
||||
if opts.AppID != nil {
|
||||
query = query.Where("app_id LIKE ?", opts.AppID)
|
||||
}
|
||||
if opts.Status != nil {
|
||||
query = query.Where("status LIKE ?", opts.Status)
|
||||
}
|
||||
err = query.Scan(&data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
//
|
||||
// init.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-06 15:12
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package role
|
||||
|
||||
import "github.com/veypi/OneBD/rest"
|
||||
|
||||
var Router = rest.NewRouter()
|
||||
@ -0,0 +1,42 @@
|
||||
//
|
||||
// crud.go
|
||||
// Copyright (C) 2025 veypi <i@veypi.com>
|
||||
// 2025-05-06 15:12
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"github.com/veypi/OneBD/rest"
|
||||
"oa/cfg"
|
||||
M "oa/models"
|
||||
)
|
||||
|
||||
type patchOpts struct {
|
||||
ID string `json:"id" parse:"path"`
|
||||
Status *string `json:"status" parse:"json"`
|
||||
}
|
||||
|
||||
var _ = Router.Patch("/:id", userRolePatch)
|
||||
|
||||
func userRolePatch(x *rest.X) (any, error) {
|
||||
opts := &patchOpts{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.UserRole{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optsMap := make(map[string]interface{})
|
||||
if opts.Status != nil {
|
||||
optsMap["status"] = opts.Status
|
||||
}
|
||||
err = cfg.DB().Model(data).Updates(optsMap).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/veypi/OneBD/rest"
|
||||
"oa/cfg"
|
||||
M "oa/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := Router.SubRouter(":user_id/user_role")
|
||||
r.Delete("/:user_role_id", userRoleDelete)
|
||||
r.Get("/:user_role_id", userRoleGet)
|
||||
r.Get("/", userRoleList)
|
||||
r.Patch("/:user_role_id", userRolePatch)
|
||||
r.Post("/", userRolePost)
|
||||
}
|
||||
|
||||
func userRoleList(x *rest.X) (any, error) {
|
||||
opts := &M.UserRoleList{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]*struct {
|
||||
M.UserRole
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Icon string `json:"icon"`
|
||||
RoleName string `json:"role_name"`
|
||||
}, 0, 10)
|
||||
// data := make([]*M.UserRole, 0, 10)
|
||||
|
||||
query := cfg.DB().Debug().Table("user_roles").Select("user_roles.*,users.username,users.nickname,users.icon,roles.name as role_name").
|
||||
Joins("JOIN users ON users.id = user_roles.user_id").
|
||||
Joins("JOIN roles ON roles.id = user_roles.role_id")
|
||||
if opts.UserID != nil && *opts.UserID != "-" {
|
||||
query = query.Where("user_id LIKE ?", opts.UserID)
|
||||
}
|
||||
if opts.RoleID != nil {
|
||||
query = query.Where("role_id LIKE ?", opts.RoleID)
|
||||
}
|
||||
if opts.AppID != nil {
|
||||
query = query.Where("app_id LIKE ?", opts.AppID)
|
||||
}
|
||||
if opts.Status != nil {
|
||||
query = query.Where("status LIKE ?", opts.Status)
|
||||
}
|
||||
err = query.Scan(&data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func userRolePost(x *rest.X) (any, error) {
|
||||
opts := &M.UserRolePost{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.UserRole{}
|
||||
|
||||
data.UserID = opts.UserID
|
||||
data.RoleID = opts.RoleID
|
||||
data.AppID = opts.AppID
|
||||
data.Status = opts.Status
|
||||
err = cfg.DB().Create(data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func userRoleGet(x *rest.X) (any, error) {
|
||||
opts := &M.UserRoleGet{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.UserRole{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func userRolePatch(x *rest.X) (any, error) {
|
||||
opts := &M.UserRolePatch{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.UserRole{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optsMap := make(map[string]interface{})
|
||||
if opts.Status != nil {
|
||||
optsMap["status"] = opts.Status
|
||||
}
|
||||
err = cfg.DB().Model(data).Updates(optsMap).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
func userRoleDelete(x *rest.X) (any, error) {
|
||||
opts := &M.UserRoleDelete{}
|
||||
err := x.Parse(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &M.UserRole{}
|
||||
|
||||
err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error
|
||||
|
||||
return data, err
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
package models
|
||||
|
||||
import ()
|
||||
|
||||
type UserGet struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_id"`
|
||||
}
|
||||
|
||||
type UserPatch struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_id"`
|
||||
Username *string `json:"username" gorm:"varchar(100);unique;default:not null" parse:"json"`
|
||||
Nickname *string `json:"nickname" parse:"json"`
|
||||
Icon *string `json:"icon" parse:"json"`
|
||||
Email *string `json:"email" gorm:"varchar(20);unique;default:null" parse:"json"`
|
||||
Phone *string `json:"phone" gorm:"varchar(50);unique;default:null" parse:"json"`
|
||||
Status *uint `json:"status" parse:"json"`
|
||||
}
|
||||
|
||||
type UserDelete struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_id"`
|
||||
}
|
||||
|
||||
type UserPost struct {
|
||||
Username string `json:"username" gorm:"varchar(100);unique;default:not null" parse:"json"`
|
||||
Nickname *string `json:"nickname" parse:"json"`
|
||||
Icon *string `json:"icon" parse:"json"`
|
||||
Email *string `json:"email" gorm:"varchar(20);unique;default:null" parse:"json"`
|
||||
Phone *string `json:"phone" gorm:"varchar(50);unique;default:null" parse:"json"`
|
||||
Salt string `json:"salt" gorm:"varchar(32)" parse:"json"`
|
||||
Code string `json:"code" gorm:"varchar(128)" parse:"json"`
|
||||
}
|
||||
|
||||
type UserList struct {
|
||||
Username *string `json:"username" gorm:"type:varchar(100);unique;default:not null" parse:"query"`
|
||||
Nickname *string `json:"nickname" gorm:"type:varchar(100)" parse:"query"`
|
||||
Email *string `json:"email" gorm:"unique;type:varchar(50);default:null" parse:"query"`
|
||||
Phone *string `json:"phone" gorm:"type:varchar(30);unique;default:null" parse:"query"`
|
||||
Status *uint `json:"status" parse:"query"`
|
||||
}
|
||||
|
||||
type UserRoleGet struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"`
|
||||
}
|
||||
|
||||
type UserRolePatch struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"`
|
||||
Status *string `json:"status" parse:"json"`
|
||||
}
|
||||
|
||||
type UserRoleDelete struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"`
|
||||
}
|
||||
|
||||
type UserRoleList struct {
|
||||
UserID *string `json:"user_id" parse:"path"`
|
||||
RoleID *string `json:"role_id" parse:"query"`
|
||||
AppID *string `json:"app_id" parse:"query"`
|
||||
Status *string `json:"status" parse:"query"`
|
||||
}
|
||||
|
||||
type UserRolePost struct {
|
||||
UserID string `json:"user_id" parse:"path"`
|
||||
RoleID string `json:"role_id" parse:"json"`
|
||||
AppID string `json:"app_id" parse:"json"`
|
||||
Status string `json:"status" parse:"json"`
|
||||
}
|
||||
Loading…
Reference in New Issue