认证授权主要流程实现
This commit is contained in:
47
web/auth.go
47
web/auth.go
@@ -1 +1,48 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"platform/web/common"
|
||||
"strings"
|
||||
|
||||
"platform/web/services"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// Protect 创建针对单个路由的鉴权中间件
|
||||
func Protect(permissions ...string) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
// 获取令牌
|
||||
var header = c.Get("Authorization")
|
||||
var token = strings.TrimPrefix(header, "Bearer ")
|
||||
if token == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "没有权限",
|
||||
})
|
||||
}
|
||||
|
||||
// 验证令牌
|
||||
auth, err := services.Session.Find(c.Context(), token)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "没有权限",
|
||||
})
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{
|
||||
Error: true,
|
||||
Message: "拒绝访问",
|
||||
})
|
||||
}
|
||||
|
||||
// 将认证信息存储在上下文中
|
||||
c.Locals("auth", auth)
|
||||
c.Locals("access_token", token) // 存储原始令牌,便于后续操作
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
7
web/common/types.go
Normal file
7
web/common/types.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package common
|
||||
|
||||
// ErrResp 定义通用错误响应格式
|
||||
type ErrResp struct {
|
||||
Message string `json:"message"`
|
||||
Error bool `json:"error"`
|
||||
}
|
||||
19
web/error.go
Normal file
19
web/error.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func ErrorHandler(c *fiber.Ctx, err error) error {
|
||||
code := fiber.StatusInternalServerError
|
||||
message := "服务器异常"
|
||||
var e *fiber.Error
|
||||
if errors.As(err, &e) {
|
||||
code = e.Code
|
||||
message = e.Message
|
||||
}
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
|
||||
return c.Status(code).SendString(message)
|
||||
}
|
||||
52
web/handlers/client.go
Normal file
52
web/handlers/client.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type CreateClientReq struct {
|
||||
ClientID string `query:"client_id"`
|
||||
ClientSecret string `query:"client_secret"`
|
||||
}
|
||||
|
||||
func CreateClient(c *fiber.Ctx) error {
|
||||
// 验证请求参数
|
||||
req := new(CreateClientReq)
|
||||
if err := c.QueryParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.ClientID == "" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "client_id不能为空")
|
||||
}
|
||||
if req.ClientSecret == "" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "client_secret不能为空")
|
||||
}
|
||||
|
||||
// 创建客户端
|
||||
hashedSecret, err := bcrypt.GenerateFromPassword([]byte(req.ClientSecret), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &models.Client{
|
||||
ClientID: req.ClientID,
|
||||
ClientSecret: string(hashedSecret),
|
||||
Name: "默认客户端 - " + time.Now().String(),
|
||||
Spec: 0,
|
||||
GrantCode: true,
|
||||
GrantClient: true,
|
||||
GrantRefresh: true,
|
||||
Version: 0,
|
||||
}
|
||||
|
||||
err = q.Client.Create(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(client)
|
||||
}
|
||||
107
web/handlers/login.go
Normal file
107
web/handlers/login.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/services"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type LoginReq struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Remember bool `json:"remember"`
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
Token string `json:"token"`
|
||||
Expires int64 `json:"expires"`
|
||||
}
|
||||
|
||||
func Login(c *fiber.Ctx) error {
|
||||
|
||||
// 验证请求参数
|
||||
req := new(LoginReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Username == "" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "手机号不能为空")
|
||||
}
|
||||
if req.Password == "" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "验证码不能为空")
|
||||
}
|
||||
|
||||
return loginByPhone(c, req)
|
||||
}
|
||||
|
||||
func loginByPhone(c *fiber.Ctx, req *LoginReq) error {
|
||||
|
||||
// 验证验证码
|
||||
ok, err := services.Verifier.VerifySms(c.Context(), req.Username, req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "验证码错误")
|
||||
}
|
||||
|
||||
// 查找用户 todo 获取权限信息
|
||||
var tx = q.Q.Begin()
|
||||
|
||||
var user *models.User
|
||||
user, err = tx.User.
|
||||
Where(tx.User.Phone.Eq(req.Username)).
|
||||
Take()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果用户不存在,初始化用户 todo 保存默认权限信息
|
||||
if user == nil {
|
||||
user = &models.User{
|
||||
Phone: req.Username,
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户的登录时间
|
||||
user.LastLogin = time.Now()
|
||||
user.LastLoginHost = c.IP()
|
||||
user.LastLoginAgent = c.Get("User-Agent")
|
||||
if err := tx.User.Omit(q.User.AdminID).Save(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存到会话
|
||||
auth := services.AuthContext{
|
||||
Permissions: map[string]struct{}{
|
||||
"user": {},
|
||||
},
|
||||
Payload: services.Payload{
|
||||
Type: services.PayloadUser,
|
||||
Id: user.ID,
|
||||
},
|
||||
}
|
||||
duration := time.Hour * 24
|
||||
if req.Remember {
|
||||
duration *= 7
|
||||
}
|
||||
token, err := services.Session.Create(c.Context(), auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(LoginResp{
|
||||
Token: token.AccessToken,
|
||||
Expires: token.AccessTokenExpires.Unix(),
|
||||
})
|
||||
}
|
||||
243
web/handlers/oauth.go
Normal file
243
web/handlers/oauth.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/services"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// region Token
|
||||
|
||||
type TokenReq struct {
|
||||
ClientID string `json:"client_id" form:"client_id"`
|
||||
ClientSecret string `json:"client_secret" form:"client_secret"`
|
||||
GrantType TokenGrantType `json:"grant_type" form:"grant_type"`
|
||||
Code string `json:"code" form:"code"`
|
||||
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
|
||||
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
|
||||
RefreshToken string `json:"refresh_token" form:"refresh_token"`
|
||||
Scope string `json:"scope" form:"scope"`
|
||||
}
|
||||
|
||||
type TokenResp struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
TokenType string `json:"token_type"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
type TokenErrResp struct {
|
||||
Error string `json:"error"`
|
||||
Description string `json:"error_description,omitempty"`
|
||||
}
|
||||
|
||||
type TokenGrantType string
|
||||
|
||||
const (
|
||||
AuthorizationCode = TokenGrantType("authorization_code")
|
||||
ClientCredentials = TokenGrantType("client_credentials")
|
||||
RefreshToken = TokenGrantType("refresh_token")
|
||||
)
|
||||
|
||||
// Token 处理 OAuth2.0 授权请求
|
||||
func Token(c *fiber.Ctx) error {
|
||||
|
||||
// 验证请求参数
|
||||
req := new(TokenReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "无法解析请求参数")
|
||||
}
|
||||
if req.GrantType == "" {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数:grant_type")
|
||||
}
|
||||
|
||||
// 基于授权类型处理请求
|
||||
switch req.GrantType {
|
||||
|
||||
case AuthorizationCode:
|
||||
return authorizationCode(c, req)
|
||||
|
||||
case ClientCredentials:
|
||||
return clientCredentials(c, req)
|
||||
|
||||
case RefreshToken:
|
||||
return refreshToken(c, req)
|
||||
|
||||
default:
|
||||
return sendError(c, services.ErrOauthUnsupportedGrantType)
|
||||
}
|
||||
}
|
||||
|
||||
// 授权码
|
||||
func authorizationCode(c *fiber.Ctx, req *TokenReq) error {
|
||||
if req.Code == "" {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数:code")
|
||||
}
|
||||
|
||||
client, err := protect(c, services.GrantTypeAuthorizationCode, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
token, err := services.Auth.OauthAuthorizationCode(c.Context(), client, req.Code, req.RedirectURI, req.CodeVerifier)
|
||||
if err != nil {
|
||||
return sendError(c, err.(services.AuthServiceOauthError))
|
||||
}
|
||||
|
||||
return sendSuccess(c, token)
|
||||
}
|
||||
|
||||
// 客户端凭证
|
||||
func clientCredentials(c *fiber.Ctx, req *TokenReq) error {
|
||||
client, err := protect(c, services.GrantTypeClientCredentials, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
scope := strings.Split(req.Scope, ",")
|
||||
token, err := services.Auth.OauthClientCredentials(c.Context(), client, scope)
|
||||
if err != nil {
|
||||
return sendError(c, err.(services.AuthServiceOauthError))
|
||||
}
|
||||
|
||||
return sendSuccess(c, token)
|
||||
}
|
||||
|
||||
// 刷新令牌
|
||||
func refreshToken(c *fiber.Ctx, req *TokenReq) error {
|
||||
if req.RefreshToken == "" {
|
||||
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数:refresh_token")
|
||||
}
|
||||
|
||||
client, err := protect(c, services.GrantTypeRefreshToken, req.ClientID, req.ClientSecret)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
|
||||
scope := strings.Split(req.Scope, ",")
|
||||
token, err := services.Auth.OauthRefreshToken(c.Context(), client, req.RefreshToken, scope)
|
||||
if err != nil {
|
||||
return sendError(c, err.(services.AuthServiceOauthError))
|
||||
}
|
||||
|
||||
return sendSuccess(c, token)
|
||||
}
|
||||
|
||||
// 检查客户端凭证
|
||||
func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret string) (*models.Client, error) {
|
||||
header := c.Get("Authorization")
|
||||
if header != "" {
|
||||
basic := strings.TrimPrefix(header, "Basic ")
|
||||
if basic != "" {
|
||||
base, err := base64.URLEncoding.DecodeString(basic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts := strings.SplitN(string(base), ":", 2)
|
||||
if len(parts) == 2 {
|
||||
clientId = parts[0]
|
||||
clientSecret = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找客户端
|
||||
if clientId == "" {
|
||||
return nil, services.ErrOauthInvalidRequest
|
||||
}
|
||||
client, err := q.Client.Where(q.Client.ClientID.Eq(clientId)).Take()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, services.ErrOauthInvalidClient
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 验证客户端状态
|
||||
if client.Status != 1 {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
}
|
||||
|
||||
// 验证授权类型
|
||||
switch grant {
|
||||
case services.GrantTypeAuthorizationCode:
|
||||
if !client.GrantCode {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
}
|
||||
case services.GrantTypeClientCredentials:
|
||||
if !client.GrantClient || client.Spec != 0 {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
}
|
||||
case services.GrantTypeRefreshToken:
|
||||
if !client.GrantRefresh {
|
||||
return nil, services.ErrOauthUnauthorizedClient
|
||||
}
|
||||
}
|
||||
|
||||
// 如果客户端是 confidential,验证 client_secret,失败返回错误
|
||||
if client.Spec == 0 {
|
||||
if clientSecret == "" {
|
||||
return nil, services.ErrOauthInvalidRequest
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
|
||||
return nil, services.ErrOauthInvalidClient
|
||||
}
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// 发送成功响应
|
||||
func sendSuccess(c *fiber.Ctx, details *services.TokenDetails) error {
|
||||
return c.JSON(TokenResp{
|
||||
AccessToken: details.AccessToken,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: int(time.Until(details.AccessTokenExpires).Seconds()),
|
||||
RefreshToken: details.RefreshToken,
|
||||
})
|
||||
}
|
||||
|
||||
// 发送错误响应
|
||||
func sendError(c *fiber.Ctx, err error, description ...string) error {
|
||||
var sErr services.AuthServiceOauthError
|
||||
if errors.As(err, &sErr) {
|
||||
status := fiber.StatusBadRequest
|
||||
var desc string
|
||||
switch {
|
||||
case errors.Is(sErr, services.ErrOauthInvalidRequest):
|
||||
desc = "无效的请求"
|
||||
case errors.Is(sErr, services.ErrOauthInvalidClient):
|
||||
status = fiber.StatusUnauthorized
|
||||
desc = "无效的客户端凭证"
|
||||
case errors.Is(sErr, services.ErrOauthInvalidGrant):
|
||||
desc = "无效的授权凭证"
|
||||
case errors.Is(sErr, services.ErrOauthInvalidScope):
|
||||
desc = "无效的授权范围"
|
||||
case errors.Is(sErr, services.ErrOauthUnauthorizedClient):
|
||||
desc = "未授权的客户端"
|
||||
case errors.Is(sErr, services.ErrOauthUnsupportedGrantType):
|
||||
desc = "不支持的授权类型"
|
||||
}
|
||||
if len(description) > 0 {
|
||||
desc = description[0]
|
||||
}
|
||||
|
||||
return c.Status(status).JSON(TokenErrResp{
|
||||
Error: string(sErr),
|
||||
Description: desc,
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// endregion
|
||||
44
web/handlers/verifier.go
Normal file
44
web/handlers/verifier.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/web/services"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type VerifierReq struct {
|
||||
Purpose services.VerifierSmsPurpose `json:"purpose"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
func SmsCode(c *fiber.Ctx) error {
|
||||
|
||||
// 解析请求参数
|
||||
req := new(VerifierReq)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
match, err := regexp.MatchString(`^1[3-9]\d{9}$`, req.Phone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !match {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "手机号格式错误")
|
||||
}
|
||||
|
||||
// 发送身份验证码
|
||||
err = services.Verifier.SendSms(c.Context(), req.Phone, req.Purpose)
|
||||
if err != nil {
|
||||
var sErr services.VerifierServiceSendLimitErr
|
||||
if errors.As(err, &sErr) {
|
||||
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送成功
|
||||
return nil
|
||||
}
|
||||
@@ -23,7 +23,7 @@ type Admin struct {
|
||||
Email string `gorm:"column:email;comment:邮箱" json:"email"` // 邮箱
|
||||
Status int32 `gorm:"column:status;not null;default:1;comment:状态:1-正常,0-禁用" json:"status"` // 状态:1-正常,0-禁用
|
||||
LastLogin time.Time `gorm:"column:last_login;comment:最后登录时间" json:"last_login"` // 最后登录时间
|
||||
LastLoginAddr string `gorm:"column:last_login_addr;comment:最后登录地址" json:"last_login_addr"` // 最后登录地址
|
||||
LastLoginHost string `gorm:"column:last_login_host;comment:最后登录地址" json:"last_login_host"` // 最后登录地址
|
||||
LastLoginAgent string `gorm:"column:last_login_agent;comment:最后登录代理" json:"last_login_agent"` // 最后登录代理
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
|
||||
@@ -17,7 +17,7 @@ type Channel struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:通道ID" json:"id"` // 通道ID
|
||||
UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
|
||||
NodeID int32 `gorm:"column:node_id;comment:节点ID" json:"node_id"` // 节点ID
|
||||
UserAddr string `gorm:"column:user_addr;not null;comment:用户地址" json:"user_addr"` // 用户地址
|
||||
UserHost string `gorm:"column:user_host;not null;comment:用户地址" json:"user_host"` // 用户地址
|
||||
NodePort int32 `gorm:"column:node_port;comment:节点端口" json:"node_port"` // 节点端口
|
||||
AuthIP bool `gorm:"column:auth_ip;not null;comment:IP认证" json:"auth_ip"` // IP认证
|
||||
AuthPass bool `gorm:"column:auth_pass;not null;comment:密码认证" json:"auth_pass"` // 密码认证
|
||||
|
||||
36
web/models/client.gen.go
Normal file
36
web/models/client.gen.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const TableNameClient = "client"
|
||||
|
||||
// Client mapped from table <client>
|
||||
type Client struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:客户端ID" json:"id"` // 客户端ID
|
||||
ClientID string `gorm:"column:client_id;not null;comment:OAuth2客户端标识符" json:"client_id"` // OAuth2客户端标识符
|
||||
ClientSecret string `gorm:"column:client_secret;not null;comment:OAuth2客户端密钥" json:"client_secret"` // OAuth2客户端密钥
|
||||
RedirectURI string `gorm:"column:redirect_uri;comment:OAuth2 重定向URI" json:"redirect_uri"` // OAuth2 重定向URI
|
||||
GrantCode bool `gorm:"column:grant_code;not null;comment:允许授权码授予" json:"grant_code"` // 允许授权码授予
|
||||
GrantClient bool `gorm:"column:grant_client;not null;comment:允许客户端凭证授予" json:"grant_client"` // 允许客户端凭证授予
|
||||
GrantRefresh bool `gorm:"column:grant_refresh;not null;comment:允许刷新令牌授予" json:"grant_refresh"` // 允许刷新令牌授予
|
||||
Spec int32 `gorm:"column:spec;not null;comment:安全规范:0-web,1-native,2-browser" json:"spec"` // 安全规范:0-web,1-native,2-browser
|
||||
Name string `gorm:"column:name;not null;comment:名称" json:"name"` // 名称
|
||||
Version int32 `gorm:"column:version;not null;comment:版本" json:"version"` // 版本
|
||||
Status int32 `gorm:"column:status;not null;default:1;comment:状态:1-正常,0-禁用" json:"status"` // 状态:1-正常,0-禁用
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
}
|
||||
|
||||
// TableName Client's table name
|
||||
func (*Client) TableName() string {
|
||||
return TableNameClient
|
||||
}
|
||||
28
web/models/client_permission_link.gen.go
Normal file
28
web/models/client_permission_link.gen.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const TableNameClientPermissionLink = "client_permission_link"
|
||||
|
||||
// ClientPermissionLink mapped from table <client_permission_link>
|
||||
type ClientPermissionLink struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:关联ID" json:"id"` // 关联ID
|
||||
ClientID int32 `gorm:"column:client_id;not null;comment:客户端ID" json:"client_id"` // 客户端ID
|
||||
PermissionID int32 `gorm:"column:permission_id;not null;comment:权限ID" json:"permission_id"` // 权限ID
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
}
|
||||
|
||||
// TableName ClientPermissionLink's table name
|
||||
func (*ClientPermissionLink) TableName() string {
|
||||
return TableNameClientPermissionLink
|
||||
}
|
||||
@@ -14,27 +14,27 @@ const TableNameUser = "user"
|
||||
|
||||
// User mapped from table <user>
|
||||
type User struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
AdminID int32 `gorm:"column:admin_id" json:"admin_id"`
|
||||
Phone string `gorm:"column:phone;not null" json:"phone"`
|
||||
Username string `gorm:"column:username" json:"username"`
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:用户ID" json:"id"` // 用户ID
|
||||
AdminID int32 `gorm:"column:admin_id;comment:管理员ID" json:"admin_id"` // 管理员ID
|
||||
Phone string `gorm:"column:phone;not null;comment:手机号码" json:"phone"` // 手机号码
|
||||
Username string `gorm:"column:username;comment:用户名" json:"username"` // 用户名
|
||||
Email string `gorm:"column:email" json:"email"`
|
||||
Password string `gorm:"column:password" json:"password"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Avatar string `gorm:"column:avatar" json:"avatar"`
|
||||
Status int32 `gorm:"column:status;not null;default:1" json:"status"`
|
||||
Balance float64 `gorm:"column:balance;not null" json:"balance"`
|
||||
IDType int32 `gorm:"column:id_type;not null" json:"id_type"`
|
||||
IDNo string `gorm:"column:id_no" json:"id_no"`
|
||||
IDToken string `gorm:"column:id_token" json:"id_token"`
|
||||
ContactQq string `gorm:"column:contact_qq" json:"contact_qq"`
|
||||
ContactWechat string `gorm:"column:contact_wechat" json:"contact_wechat"`
|
||||
LastLogin time.Time `gorm:"column:last_login" json:"last_login"`
|
||||
LastLoginAddr string `gorm:"column:last_login_addr" json:"last_login_addr"`
|
||||
LastLoginAgent string `gorm:"column:last_login_agent" json:"last_login_agent"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"`
|
||||
Password string `gorm:"column:password;comment:用户密码" json:"password"` // 用户密码
|
||||
Name string `gorm:"column:name;comment:真实姓名" json:"name"` // 真实姓名
|
||||
Avatar string `gorm:"column:avatar;comment:头像URL" json:"avatar"` // 头像URL
|
||||
Status int32 `gorm:"column:status;not null;default:1;comment:用户状态:1-正常,0-禁用" json:"status"` // 用户状态:1-正常,0-禁用
|
||||
Balance float64 `gorm:"column:balance;not null;comment:账户余额" json:"balance"` // 账户余额
|
||||
IDType int32 `gorm:"column:id_type;not null;comment:认证类型:0-未认证,1-个人认证,2-企业认证" json:"id_type"` // 认证类型:0-未认证,1-个人认证,2-企业认证
|
||||
IDNo string `gorm:"column:id_no;comment:身份证号或营业执照号" json:"id_no"` // 身份证号或营业执照号
|
||||
IDToken string `gorm:"column:id_token;comment:身份验证标识" json:"id_token"` // 身份验证标识
|
||||
ContactQq string `gorm:"column:contact_qq;comment:QQ联系方式" json:"contact_qq"` // QQ联系方式
|
||||
ContactWechat string `gorm:"column:contact_wechat;comment:微信联系方式" json:"contact_wechat"` // 微信联系方式
|
||||
LastLogin time.Time `gorm:"column:last_login;comment:最后登录时间" json:"last_login"` // 最后登录时间
|
||||
LastLoginHost string `gorm:"column:last_login_host;comment:最后登录地址" json:"last_login_host"` // 最后登录地址
|
||||
LastLoginAgent string `gorm:"column:last_login_agent;comment:最后登录代理" json:"last_login_agent"` // 最后登录代理
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
}
|
||||
|
||||
// TableName User's table name
|
||||
|
||||
@@ -16,7 +16,7 @@ const TableNameWhitelist = "whitelist"
|
||||
type Whitelist struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:白名单ID" json:"id"` // 白名单ID
|
||||
UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID
|
||||
Address string `gorm:"column:address;not null;comment:IP地址" json:"address"` // IP地址
|
||||
Host string `gorm:"column:host;not null;comment:IP地址" json:"host"` // IP地址
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||
|
||||
@@ -36,7 +36,7 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
|
||||
_admin.Email = field.NewString(tableName, "email")
|
||||
_admin.Status = field.NewInt32(tableName, "status")
|
||||
_admin.LastLogin = field.NewTime(tableName, "last_login")
|
||||
_admin.LastLoginAddr = field.NewString(tableName, "last_login_addr")
|
||||
_admin.LastLoginHost = field.NewString(tableName, "last_login_host")
|
||||
_admin.LastLoginAgent = field.NewString(tableName, "last_login_agent")
|
||||
_admin.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_admin.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
@@ -60,7 +60,7 @@ type admin struct {
|
||||
Email field.String // 邮箱
|
||||
Status field.Int32 // 状态:1-正常,0-禁用
|
||||
LastLogin field.Time // 最后登录时间
|
||||
LastLoginAddr field.String // 最后登录地址
|
||||
LastLoginHost field.String // 最后登录地址
|
||||
LastLoginAgent field.String // 最后登录代理
|
||||
CreatedAt field.Time // 创建时间
|
||||
UpdatedAt field.Time // 更新时间
|
||||
@@ -90,7 +90,7 @@ func (a *admin) updateTableName(table string) *admin {
|
||||
a.Email = field.NewString(table, "email")
|
||||
a.Status = field.NewInt32(table, "status")
|
||||
a.LastLogin = field.NewTime(table, "last_login")
|
||||
a.LastLoginAddr = field.NewString(table, "last_login_addr")
|
||||
a.LastLoginHost = field.NewString(table, "last_login_host")
|
||||
a.LastLoginAgent = field.NewString(table, "last_login_agent")
|
||||
a.CreatedAt = field.NewTime(table, "created_at")
|
||||
a.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
@@ -121,7 +121,7 @@ func (a *admin) fillFieldMap() {
|
||||
a.fieldMap["email"] = a.Email
|
||||
a.fieldMap["status"] = a.Status
|
||||
a.fieldMap["last_login"] = a.LastLogin
|
||||
a.fieldMap["last_login_addr"] = a.LastLoginAddr
|
||||
a.fieldMap["last_login_host"] = a.LastLoginHost
|
||||
a.fieldMap["last_login_agent"] = a.LastLoginAgent
|
||||
a.fieldMap["created_at"] = a.CreatedAt
|
||||
a.fieldMap["updated_at"] = a.UpdatedAt
|
||||
|
||||
@@ -30,7 +30,7 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
|
||||
_channel.ID = field.NewInt32(tableName, "id")
|
||||
_channel.UserID = field.NewInt32(tableName, "user_id")
|
||||
_channel.NodeID = field.NewInt32(tableName, "node_id")
|
||||
_channel.UserAddr = field.NewString(tableName, "user_addr")
|
||||
_channel.UserHost = field.NewString(tableName, "user_host")
|
||||
_channel.NodePort = field.NewInt32(tableName, "node_port")
|
||||
_channel.AuthIP = field.NewBool(tableName, "auth_ip")
|
||||
_channel.AuthPass = field.NewBool(tableName, "auth_pass")
|
||||
@@ -54,7 +54,7 @@ type channel struct {
|
||||
ID field.Int32 // 通道ID
|
||||
UserID field.Int32 // 用户ID
|
||||
NodeID field.Int32 // 节点ID
|
||||
UserAddr field.String // 用户地址
|
||||
UserHost field.String // 用户地址
|
||||
NodePort field.Int32 // 节点端口
|
||||
AuthIP field.Bool // IP认证
|
||||
AuthPass field.Bool // 密码认证
|
||||
@@ -84,7 +84,7 @@ func (c *channel) updateTableName(table string) *channel {
|
||||
c.ID = field.NewInt32(table, "id")
|
||||
c.UserID = field.NewInt32(table, "user_id")
|
||||
c.NodeID = field.NewInt32(table, "node_id")
|
||||
c.UserAddr = field.NewString(table, "user_addr")
|
||||
c.UserHost = field.NewString(table, "user_host")
|
||||
c.NodePort = field.NewInt32(table, "node_port")
|
||||
c.AuthIP = field.NewBool(table, "auth_ip")
|
||||
c.AuthPass = field.NewBool(table, "auth_pass")
|
||||
@@ -115,7 +115,7 @@ func (c *channel) fillFieldMap() {
|
||||
c.fieldMap["id"] = c.ID
|
||||
c.fieldMap["user_id"] = c.UserID
|
||||
c.fieldMap["node_id"] = c.NodeID
|
||||
c.fieldMap["user_addr"] = c.UserAddr
|
||||
c.fieldMap["user_host"] = c.UserHost
|
||||
c.fieldMap["node_port"] = c.NodePort
|
||||
c.fieldMap["auth_ip"] = c.AuthIP
|
||||
c.fieldMap["auth_pass"] = c.AuthPass
|
||||
|
||||
371
web/queries/client.gen.go
Normal file
371
web/queries/client.gen.go
Normal file
@@ -0,0 +1,371 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"platform/web/models"
|
||||
)
|
||||
|
||||
func newClient(db *gorm.DB, opts ...gen.DOOption) client {
|
||||
_client := client{}
|
||||
|
||||
_client.clientDo.UseDB(db, opts...)
|
||||
_client.clientDo.UseModel(&models.Client{})
|
||||
|
||||
tableName := _client.clientDo.TableName()
|
||||
_client.ALL = field.NewAsterisk(tableName)
|
||||
_client.ID = field.NewInt32(tableName, "id")
|
||||
_client.ClientID = field.NewString(tableName, "client_id")
|
||||
_client.ClientSecret = field.NewString(tableName, "client_secret")
|
||||
_client.RedirectURI = field.NewString(tableName, "redirect_uri")
|
||||
_client.GrantCode = field.NewBool(tableName, "grant_code")
|
||||
_client.GrantClient = field.NewBool(tableName, "grant_client")
|
||||
_client.GrantRefresh = field.NewBool(tableName, "grant_refresh")
|
||||
_client.Spec = field.NewInt32(tableName, "spec")
|
||||
_client.Name = field.NewString(tableName, "name")
|
||||
_client.Version = field.NewInt32(tableName, "version")
|
||||
_client.Status = field.NewInt32(tableName, "status")
|
||||
_client.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_client.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_client.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
|
||||
_client.fillFieldMap()
|
||||
|
||||
return _client
|
||||
}
|
||||
|
||||
type client struct {
|
||||
clientDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 客户端ID
|
||||
ClientID field.String // OAuth2客户端标识符
|
||||
ClientSecret field.String // OAuth2客户端密钥
|
||||
RedirectURI field.String // OAuth2 重定向URI
|
||||
GrantCode field.Bool // 允许授权码授予
|
||||
GrantClient field.Bool // 允许客户端凭证授予
|
||||
GrantRefresh field.Bool // 允许刷新令牌授予
|
||||
Spec field.Int32 // 安全规范:0-web,1-native,2-browser
|
||||
Name field.String // 名称
|
||||
Version field.Int32 // 版本
|
||||
Status field.Int32 // 状态:1-正常,0-禁用
|
||||
CreatedAt field.Time // 创建时间
|
||||
UpdatedAt field.Time // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (c client) Table(newTableName string) *client {
|
||||
c.clientDo.UseTable(newTableName)
|
||||
return c.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (c client) As(alias string) *client {
|
||||
c.clientDo.DO = *(c.clientDo.As(alias).(*gen.DO))
|
||||
return c.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (c *client) updateTableName(table string) *client {
|
||||
c.ALL = field.NewAsterisk(table)
|
||||
c.ID = field.NewInt32(table, "id")
|
||||
c.ClientID = field.NewString(table, "client_id")
|
||||
c.ClientSecret = field.NewString(table, "client_secret")
|
||||
c.RedirectURI = field.NewString(table, "redirect_uri")
|
||||
c.GrantCode = field.NewBool(table, "grant_code")
|
||||
c.GrantClient = field.NewBool(table, "grant_client")
|
||||
c.GrantRefresh = field.NewBool(table, "grant_refresh")
|
||||
c.Spec = field.NewInt32(table, "spec")
|
||||
c.Name = field.NewString(table, "name")
|
||||
c.Version = field.NewInt32(table, "version")
|
||||
c.Status = field.NewInt32(table, "status")
|
||||
c.CreatedAt = field.NewTime(table, "created_at")
|
||||
c.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
c.DeletedAt = field.NewField(table, "deleted_at")
|
||||
|
||||
c.fillFieldMap()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *client) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := c.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (c *client) fillFieldMap() {
|
||||
c.fieldMap = make(map[string]field.Expr, 14)
|
||||
c.fieldMap["id"] = c.ID
|
||||
c.fieldMap["client_id"] = c.ClientID
|
||||
c.fieldMap["client_secret"] = c.ClientSecret
|
||||
c.fieldMap["redirect_uri"] = c.RedirectURI
|
||||
c.fieldMap["grant_code"] = c.GrantCode
|
||||
c.fieldMap["grant_client"] = c.GrantClient
|
||||
c.fieldMap["grant_refresh"] = c.GrantRefresh
|
||||
c.fieldMap["spec"] = c.Spec
|
||||
c.fieldMap["name"] = c.Name
|
||||
c.fieldMap["version"] = c.Version
|
||||
c.fieldMap["status"] = c.Status
|
||||
c.fieldMap["created_at"] = c.CreatedAt
|
||||
c.fieldMap["updated_at"] = c.UpdatedAt
|
||||
c.fieldMap["deleted_at"] = c.DeletedAt
|
||||
}
|
||||
|
||||
func (c client) clone(db *gorm.DB) client {
|
||||
c.clientDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c client) replaceDB(db *gorm.DB) client {
|
||||
c.clientDo.ReplaceDB(db)
|
||||
return c
|
||||
}
|
||||
|
||||
type clientDo struct{ gen.DO }
|
||||
|
||||
func (c clientDo) Debug() *clientDo {
|
||||
return c.withDO(c.DO.Debug())
|
||||
}
|
||||
|
||||
func (c clientDo) WithContext(ctx context.Context) *clientDo {
|
||||
return c.withDO(c.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (c clientDo) ReadDB() *clientDo {
|
||||
return c.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (c clientDo) WriteDB() *clientDo {
|
||||
return c.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (c clientDo) Session(config *gorm.Session) *clientDo {
|
||||
return c.withDO(c.DO.Session(config))
|
||||
}
|
||||
|
||||
func (c clientDo) Clauses(conds ...clause.Expression) *clientDo {
|
||||
return c.withDO(c.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Returning(value interface{}, columns ...string) *clientDo {
|
||||
return c.withDO(c.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (c clientDo) Not(conds ...gen.Condition) *clientDo {
|
||||
return c.withDO(c.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Or(conds ...gen.Condition) *clientDo {
|
||||
return c.withDO(c.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Select(conds ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Where(conds ...gen.Condition) *clientDo {
|
||||
return c.withDO(c.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Order(conds ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Distinct(cols ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (c clientDo) Omit(cols ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (c clientDo) Join(table schema.Tabler, on ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (c clientDo) LeftJoin(table schema.Tabler, on ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (c clientDo) RightJoin(table schema.Tabler, on ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (c clientDo) Group(cols ...field.Expr) *clientDo {
|
||||
return c.withDO(c.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (c clientDo) Having(conds ...gen.Condition) *clientDo {
|
||||
return c.withDO(c.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (c clientDo) Limit(limit int) *clientDo {
|
||||
return c.withDO(c.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (c clientDo) Offset(offset int) *clientDo {
|
||||
return c.withDO(c.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (c clientDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *clientDo {
|
||||
return c.withDO(c.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (c clientDo) Unscoped() *clientDo {
|
||||
return c.withDO(c.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (c clientDo) Create(values ...*models.Client) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.DO.Create(values)
|
||||
}
|
||||
|
||||
func (c clientDo) CreateInBatches(values []*models.Client, batchSize int) error {
|
||||
return c.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (c clientDo) Save(values ...*models.Client) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.DO.Save(values)
|
||||
}
|
||||
|
||||
func (c clientDo) First() (*models.Client, error) {
|
||||
if result, err := c.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.Client), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientDo) Take() (*models.Client, error) {
|
||||
if result, err := c.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.Client), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientDo) Last() (*models.Client, error) {
|
||||
if result, err := c.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.Client), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientDo) Find() ([]*models.Client, error) {
|
||||
result, err := c.DO.Find()
|
||||
return result.([]*models.Client), err
|
||||
}
|
||||
|
||||
func (c clientDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Client, err error) {
|
||||
buf := make([]*models.Client, 0, batchSize)
|
||||
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (c clientDo) FindInBatches(result *[]*models.Client, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return c.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (c clientDo) Attrs(attrs ...field.AssignExpr) *clientDo {
|
||||
return c.withDO(c.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (c clientDo) Assign(attrs ...field.AssignExpr) *clientDo {
|
||||
return c.withDO(c.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (c clientDo) Joins(fields ...field.RelationField) *clientDo {
|
||||
for _, _f := range fields {
|
||||
c = *c.withDO(c.DO.Joins(_f))
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c clientDo) Preload(fields ...field.RelationField) *clientDo {
|
||||
for _, _f := range fields {
|
||||
c = *c.withDO(c.DO.Preload(_f))
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c clientDo) FirstOrInit() (*models.Client, error) {
|
||||
if result, err := c.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.Client), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientDo) FirstOrCreate() (*models.Client, error) {
|
||||
if result, err := c.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.Client), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientDo) FindByPage(offset int, limit int) (result []*models.Client, count int64, err error) {
|
||||
result, err = c.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = c.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (c clientDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = c.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = c.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (c clientDo) Scan(result interface{}) (err error) {
|
||||
return c.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (c clientDo) Delete(models ...*models.Client) (result gen.ResultInfo, err error) {
|
||||
return c.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (c *clientDo) withDO(do gen.Dao) *clientDo {
|
||||
c.DO = *do.(*gen.DO)
|
||||
return c
|
||||
}
|
||||
339
web/queries/client_permission_link.gen.go
Normal file
339
web/queries/client_permission_link.gen.go
Normal file
@@ -0,0 +1,339 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"platform/web/models"
|
||||
)
|
||||
|
||||
func newClientPermissionLink(db *gorm.DB, opts ...gen.DOOption) clientPermissionLink {
|
||||
_clientPermissionLink := clientPermissionLink{}
|
||||
|
||||
_clientPermissionLink.clientPermissionLinkDo.UseDB(db, opts...)
|
||||
_clientPermissionLink.clientPermissionLinkDo.UseModel(&models.ClientPermissionLink{})
|
||||
|
||||
tableName := _clientPermissionLink.clientPermissionLinkDo.TableName()
|
||||
_clientPermissionLink.ALL = field.NewAsterisk(tableName)
|
||||
_clientPermissionLink.ID = field.NewInt32(tableName, "id")
|
||||
_clientPermissionLink.ClientID = field.NewInt32(tableName, "client_id")
|
||||
_clientPermissionLink.PermissionID = field.NewInt32(tableName, "permission_id")
|
||||
_clientPermissionLink.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_clientPermissionLink.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_clientPermissionLink.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
|
||||
_clientPermissionLink.fillFieldMap()
|
||||
|
||||
return _clientPermissionLink
|
||||
}
|
||||
|
||||
type clientPermissionLink struct {
|
||||
clientPermissionLinkDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 关联ID
|
||||
ClientID field.Int32 // 客户端ID
|
||||
PermissionID field.Int32 // 权限ID
|
||||
CreatedAt field.Time // 创建时间
|
||||
UpdatedAt field.Time // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (c clientPermissionLink) Table(newTableName string) *clientPermissionLink {
|
||||
c.clientPermissionLinkDo.UseTable(newTableName)
|
||||
return c.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (c clientPermissionLink) As(alias string) *clientPermissionLink {
|
||||
c.clientPermissionLinkDo.DO = *(c.clientPermissionLinkDo.As(alias).(*gen.DO))
|
||||
return c.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (c *clientPermissionLink) updateTableName(table string) *clientPermissionLink {
|
||||
c.ALL = field.NewAsterisk(table)
|
||||
c.ID = field.NewInt32(table, "id")
|
||||
c.ClientID = field.NewInt32(table, "client_id")
|
||||
c.PermissionID = field.NewInt32(table, "permission_id")
|
||||
c.CreatedAt = field.NewTime(table, "created_at")
|
||||
c.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
c.DeletedAt = field.NewField(table, "deleted_at")
|
||||
|
||||
c.fillFieldMap()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *clientPermissionLink) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := c.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (c *clientPermissionLink) fillFieldMap() {
|
||||
c.fieldMap = make(map[string]field.Expr, 6)
|
||||
c.fieldMap["id"] = c.ID
|
||||
c.fieldMap["client_id"] = c.ClientID
|
||||
c.fieldMap["permission_id"] = c.PermissionID
|
||||
c.fieldMap["created_at"] = c.CreatedAt
|
||||
c.fieldMap["updated_at"] = c.UpdatedAt
|
||||
c.fieldMap["deleted_at"] = c.DeletedAt
|
||||
}
|
||||
|
||||
func (c clientPermissionLink) clone(db *gorm.DB) clientPermissionLink {
|
||||
c.clientPermissionLinkDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c clientPermissionLink) replaceDB(db *gorm.DB) clientPermissionLink {
|
||||
c.clientPermissionLinkDo.ReplaceDB(db)
|
||||
return c
|
||||
}
|
||||
|
||||
type clientPermissionLinkDo struct{ gen.DO }
|
||||
|
||||
func (c clientPermissionLinkDo) Debug() *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Debug())
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) WithContext(ctx context.Context) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) ReadDB() *clientPermissionLinkDo {
|
||||
return c.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) WriteDB() *clientPermissionLinkDo {
|
||||
return c.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Session(config *gorm.Session) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Session(config))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Clauses(conds ...clause.Expression) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Returning(value interface{}, columns ...string) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Not(conds ...gen.Condition) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Or(conds ...gen.Condition) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Select(conds ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Where(conds ...gen.Condition) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Order(conds ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Distinct(cols ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Omit(cols ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Join(table schema.Tabler, on ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) LeftJoin(table schema.Tabler, on ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) RightJoin(table schema.Tabler, on ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Group(cols ...field.Expr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Having(conds ...gen.Condition) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Limit(limit int) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Offset(offset int) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Unscoped() *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Create(values ...*models.ClientPermissionLink) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.DO.Create(values)
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) CreateInBatches(values []*models.ClientPermissionLink, batchSize int) error {
|
||||
return c.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (c clientPermissionLinkDo) Save(values ...*models.ClientPermissionLink) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.DO.Save(values)
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) First() (*models.ClientPermissionLink, error) {
|
||||
if result, err := c.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.ClientPermissionLink), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Take() (*models.ClientPermissionLink, error) {
|
||||
if result, err := c.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.ClientPermissionLink), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Last() (*models.ClientPermissionLink, error) {
|
||||
if result, err := c.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.ClientPermissionLink), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Find() ([]*models.ClientPermissionLink, error) {
|
||||
result, err := c.DO.Find()
|
||||
return result.([]*models.ClientPermissionLink), err
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.ClientPermissionLink, err error) {
|
||||
buf := make([]*models.ClientPermissionLink, 0, batchSize)
|
||||
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) FindInBatches(result *[]*models.ClientPermissionLink, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return c.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Attrs(attrs ...field.AssignExpr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Assign(attrs ...field.AssignExpr) *clientPermissionLinkDo {
|
||||
return c.withDO(c.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Joins(fields ...field.RelationField) *clientPermissionLinkDo {
|
||||
for _, _f := range fields {
|
||||
c = *c.withDO(c.DO.Joins(_f))
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Preload(fields ...field.RelationField) *clientPermissionLinkDo {
|
||||
for _, _f := range fields {
|
||||
c = *c.withDO(c.DO.Preload(_f))
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) FirstOrInit() (*models.ClientPermissionLink, error) {
|
||||
if result, err := c.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.ClientPermissionLink), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) FirstOrCreate() (*models.ClientPermissionLink, error) {
|
||||
if result, err := c.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.ClientPermissionLink), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) FindByPage(offset int, limit int) (result []*models.ClientPermissionLink, count int64, err error) {
|
||||
result, err = c.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = c.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = c.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = c.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Scan(result interface{}) (err error) {
|
||||
return c.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (c clientPermissionLinkDo) Delete(models ...*models.ClientPermissionLink) (result gen.ResultInfo, err error) {
|
||||
return c.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (c *clientPermissionLinkDo) withDO(do gen.Dao) *clientPermissionLinkDo {
|
||||
c.DO = *do.(*gen.DO)
|
||||
return c
|
||||
}
|
||||
@@ -23,6 +23,8 @@ var (
|
||||
AdminRolePermissionLink *adminRolePermissionLink
|
||||
Bill *bill
|
||||
Channel *channel
|
||||
Client *client
|
||||
ClientPermissionLink *clientPermissionLink
|
||||
Node *node
|
||||
Permission *permission
|
||||
Product *product
|
||||
@@ -47,6 +49,8 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
AdminRolePermissionLink = &Q.AdminRolePermissionLink
|
||||
Bill = &Q.Bill
|
||||
Channel = &Q.Channel
|
||||
Client = &Q.Client
|
||||
ClientPermissionLink = &Q.ClientPermissionLink
|
||||
Node = &Q.Node
|
||||
Permission = &Q.Permission
|
||||
Product = &Q.Product
|
||||
@@ -72,6 +76,8 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
AdminRolePermissionLink: newAdminRolePermissionLink(db, opts...),
|
||||
Bill: newBill(db, opts...),
|
||||
Channel: newChannel(db, opts...),
|
||||
Client: newClient(db, opts...),
|
||||
ClientPermissionLink: newClientPermissionLink(db, opts...),
|
||||
Node: newNode(db, opts...),
|
||||
Permission: newPermission(db, opts...),
|
||||
Product: newProduct(db, opts...),
|
||||
@@ -98,6 +104,8 @@ type Query struct {
|
||||
AdminRolePermissionLink adminRolePermissionLink
|
||||
Bill bill
|
||||
Channel channel
|
||||
Client client
|
||||
ClientPermissionLink clientPermissionLink
|
||||
Node node
|
||||
Permission permission
|
||||
Product product
|
||||
@@ -125,6 +133,8 @@ func (q *Query) clone(db *gorm.DB) *Query {
|
||||
AdminRolePermissionLink: q.AdminRolePermissionLink.clone(db),
|
||||
Bill: q.Bill.clone(db),
|
||||
Channel: q.Channel.clone(db),
|
||||
Client: q.Client.clone(db),
|
||||
ClientPermissionLink: q.ClientPermissionLink.clone(db),
|
||||
Node: q.Node.clone(db),
|
||||
Permission: q.Permission.clone(db),
|
||||
Product: q.Product.clone(db),
|
||||
@@ -159,6 +169,8 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
AdminRolePermissionLink: q.AdminRolePermissionLink.replaceDB(db),
|
||||
Bill: q.Bill.replaceDB(db),
|
||||
Channel: q.Channel.replaceDB(db),
|
||||
Client: q.Client.replaceDB(db),
|
||||
ClientPermissionLink: q.ClientPermissionLink.replaceDB(db),
|
||||
Node: q.Node.replaceDB(db),
|
||||
Permission: q.Permission.replaceDB(db),
|
||||
Product: q.Product.replaceDB(db),
|
||||
@@ -183,6 +195,8 @@ type queryCtx struct {
|
||||
AdminRolePermissionLink *adminRolePermissionLinkDo
|
||||
Bill *billDo
|
||||
Channel *channelDo
|
||||
Client *clientDo
|
||||
ClientPermissionLink *clientPermissionLinkDo
|
||||
Node *nodeDo
|
||||
Permission *permissionDo
|
||||
Product *productDo
|
||||
@@ -207,6 +221,8 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
AdminRolePermissionLink: q.AdminRolePermissionLink.WithContext(ctx),
|
||||
Bill: q.Bill.WithContext(ctx),
|
||||
Channel: q.Channel.WithContext(ctx),
|
||||
Client: q.Client.WithContext(ctx),
|
||||
ClientPermissionLink: q.ClientPermissionLink.WithContext(ctx),
|
||||
Node: q.Node.WithContext(ctx),
|
||||
Permission: q.Permission.WithContext(ctx),
|
||||
Product: q.Product.WithContext(ctx),
|
||||
|
||||
@@ -43,7 +43,7 @@ func newUser(db *gorm.DB, opts ...gen.DOOption) user {
|
||||
_user.ContactQq = field.NewString(tableName, "contact_qq")
|
||||
_user.ContactWechat = field.NewString(tableName, "contact_wechat")
|
||||
_user.LastLogin = field.NewTime(tableName, "last_login")
|
||||
_user.LastLoginAddr = field.NewString(tableName, "last_login_addr")
|
||||
_user.LastLoginHost = field.NewString(tableName, "last_login_host")
|
||||
_user.LastLoginAgent = field.NewString(tableName, "last_login_agent")
|
||||
_user.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_user.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
@@ -58,27 +58,27 @@ type user struct {
|
||||
userDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32
|
||||
AdminID field.Int32
|
||||
Phone field.String
|
||||
Username field.String
|
||||
ID field.Int32 // 用户ID
|
||||
AdminID field.Int32 // 管理员ID
|
||||
Phone field.String // 手机号码
|
||||
Username field.String // 用户名
|
||||
Email field.String
|
||||
Password field.String
|
||||
Name field.String
|
||||
Avatar field.String
|
||||
Status field.Int32
|
||||
Balance field.Float64
|
||||
IDType field.Int32
|
||||
IDNo field.String
|
||||
IDToken field.String
|
||||
ContactQq field.String
|
||||
ContactWechat field.String
|
||||
LastLogin field.Time
|
||||
LastLoginAddr field.String
|
||||
LastLoginAgent field.String
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
DeletedAt field.Field
|
||||
Password field.String // 用户密码
|
||||
Name field.String // 真实姓名
|
||||
Avatar field.String // 头像URL
|
||||
Status field.Int32 // 用户状态:1-正常,0-禁用
|
||||
Balance field.Float64 // 账户余额
|
||||
IDType field.Int32 // 认证类型:0-未认证,1-个人认证,2-企业认证
|
||||
IDNo field.String // 身份证号或营业执照号
|
||||
IDToken field.String // 身份验证标识
|
||||
ContactQq field.String // QQ联系方式
|
||||
ContactWechat field.String // 微信联系方式
|
||||
LastLogin field.Time // 最后登录时间
|
||||
LastLoginHost field.String // 最后登录地址
|
||||
LastLoginAgent field.String // 最后登录代理
|
||||
CreatedAt field.Time // 创建时间
|
||||
UpdatedAt field.Time // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func (u *user) updateTableName(table string) *user {
|
||||
u.ContactQq = field.NewString(table, "contact_qq")
|
||||
u.ContactWechat = field.NewString(table, "contact_wechat")
|
||||
u.LastLogin = field.NewTime(table, "last_login")
|
||||
u.LastLoginAddr = field.NewString(table, "last_login_addr")
|
||||
u.LastLoginHost = field.NewString(table, "last_login_host")
|
||||
u.LastLoginAgent = field.NewString(table, "last_login_agent")
|
||||
u.CreatedAt = field.NewTime(table, "created_at")
|
||||
u.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
@@ -149,7 +149,7 @@ func (u *user) fillFieldMap() {
|
||||
u.fieldMap["contact_qq"] = u.ContactQq
|
||||
u.fieldMap["contact_wechat"] = u.ContactWechat
|
||||
u.fieldMap["last_login"] = u.LastLogin
|
||||
u.fieldMap["last_login_addr"] = u.LastLoginAddr
|
||||
u.fieldMap["last_login_host"] = u.LastLoginHost
|
||||
u.fieldMap["last_login_agent"] = u.LastLoginAgent
|
||||
u.fieldMap["created_at"] = u.CreatedAt
|
||||
u.fieldMap["updated_at"] = u.UpdatedAt
|
||||
|
||||
@@ -29,7 +29,7 @@ func newWhitelist(db *gorm.DB, opts ...gen.DOOption) whitelist {
|
||||
_whitelist.ALL = field.NewAsterisk(tableName)
|
||||
_whitelist.ID = field.NewInt32(tableName, "id")
|
||||
_whitelist.UserID = field.NewInt32(tableName, "user_id")
|
||||
_whitelist.Address = field.NewString(tableName, "address")
|
||||
_whitelist.Host = field.NewString(tableName, "host")
|
||||
_whitelist.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_whitelist.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_whitelist.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
@@ -45,7 +45,7 @@ type whitelist struct {
|
||||
ALL field.Asterisk
|
||||
ID field.Int32 // 白名单ID
|
||||
UserID field.Int32 // 用户ID
|
||||
Address field.String // IP地址
|
||||
Host field.String // IP地址
|
||||
CreatedAt field.Time // 创建时间
|
||||
UpdatedAt field.Time // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
@@ -67,7 +67,7 @@ func (w *whitelist) updateTableName(table string) *whitelist {
|
||||
w.ALL = field.NewAsterisk(table)
|
||||
w.ID = field.NewInt32(table, "id")
|
||||
w.UserID = field.NewInt32(table, "user_id")
|
||||
w.Address = field.NewString(table, "address")
|
||||
w.Host = field.NewString(table, "host")
|
||||
w.CreatedAt = field.NewTime(table, "created_at")
|
||||
w.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
w.DeletedAt = field.NewField(table, "deleted_at")
|
||||
@@ -90,7 +90,7 @@ func (w *whitelist) fillFieldMap() {
|
||||
w.fieldMap = make(map[string]field.Expr, 6)
|
||||
w.fieldMap["id"] = w.ID
|
||||
w.fieldMap["user_id"] = w.UserID
|
||||
w.fieldMap["address"] = w.Address
|
||||
w.fieldMap["host"] = w.Host
|
||||
w.fieldMap["created_at"] = w.CreatedAt
|
||||
w.fieldMap["updated_at"] = w.UpdatedAt
|
||||
w.fieldMap["deleted_at"] = w.DeletedAt
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"platform/web/handlers"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func UseRoute(app *fiber.App) {
|
||||
func ApplyRouters(app *fiber.App) {
|
||||
api := app.Group("/api")
|
||||
|
||||
// 认证路由
|
||||
auth := api.Group("/auth")
|
||||
auth.Post("/verify/sms", Protect(), handlers.SmsCode)
|
||||
auth.Post("/login/sms", Protect(), handlers.Login)
|
||||
auth.Post("/token", handlers.Token)
|
||||
|
||||
// 客户端路由
|
||||
client := api.Group("/client")
|
||||
client.Get("/test/create", handlers.CreateClient)
|
||||
}
|
||||
|
||||
85
web/services/auth.go
Normal file
85
web/services/auth.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"platform/web/models"
|
||||
)
|
||||
|
||||
var Auth = &authService{}
|
||||
|
||||
type authService struct{}
|
||||
|
||||
type AuthServiceError string
|
||||
|
||||
func (e AuthServiceError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
type AuthServiceOauthError string
|
||||
|
||||
func (e AuthServiceOauthError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrOauthInvalidRequest = AuthServiceOauthError("invalid_request")
|
||||
ErrOauthInvalidClient = AuthServiceOauthError("invalid_client")
|
||||
ErrOauthInvalidGrant = AuthServiceOauthError("invalid_grant")
|
||||
ErrOauthInvalidScope = AuthServiceOauthError("invalid_scope")
|
||||
ErrOauthUnauthorizedClient = AuthServiceOauthError("unauthorized_client")
|
||||
ErrOauthUnsupportedGrantType = AuthServiceOauthError("unsupported_grant_type")
|
||||
)
|
||||
|
||||
// OauthAuthorizationCode 验证授权码
|
||||
func (s *authService) OauthAuthorizationCode(ctx context.Context, client *models.Client, code, redirectURI, codeVerifier string) (*TokenDetails, error) {
|
||||
// TODO: 从数据库验证授权码
|
||||
return nil, errors.New("TODO")
|
||||
}
|
||||
|
||||
// OauthClientCredentials 验证客户端凭证
|
||||
func (s *authService) OauthClientCredentials(ctx context.Context, client *models.Client, scope ...[]string) (*TokenDetails, error) {
|
||||
|
||||
var clientType PayloadType
|
||||
switch client.Spec {
|
||||
case 0:
|
||||
clientType = PayloadClientConfidential
|
||||
case 1:
|
||||
clientType = PayloadClientPublic
|
||||
case 2:
|
||||
clientType = PayloadClientConfidential
|
||||
}
|
||||
|
||||
// 保存会话并返回令牌
|
||||
auth := AuthContext{
|
||||
Permissions: map[string]struct{}{
|
||||
"client": {},
|
||||
},
|
||||
Payload: Payload{
|
||||
Type: clientType,
|
||||
Id: client.ID,
|
||||
},
|
||||
}
|
||||
|
||||
// todo 数据库定义会话持续时间
|
||||
token, err := Session.Create(ctx, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// OauthRefreshToken 验证刷新令牌
|
||||
func (s *authService) OauthRefreshToken(ctx context.Context, client *models.Client, refreshToken string, scope ...[]string) (*TokenDetails, error) {
|
||||
// TODO: 从数据库验证刷新令牌
|
||||
return nil, errors.New("TODO")
|
||||
}
|
||||
|
||||
type GrantType int
|
||||
|
||||
const (
|
||||
GrantTypeAuthorizationCode GrantType = iota
|
||||
GrantTypeClientCredentials
|
||||
GrantTypeRefreshToken
|
||||
)
|
||||
288
web/services/session.go
Normal file
288
web/services/session.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"platform/init/rds"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// region SessionService
|
||||
|
||||
var Session = &sessionService{}
|
||||
|
||||
type sessionService struct {
|
||||
}
|
||||
|
||||
type SessionServiceError string
|
||||
|
||||
func (e SessionServiceError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidToken = SessionServiceError("invalid_token")
|
||||
)
|
||||
|
||||
// Find 通过访问令牌获取会话信息
|
||||
func (s *sessionService) Find(ctx context.Context, token string) (*AuthContext, error) {
|
||||
|
||||
// 读取认证数据
|
||||
authJSON, err := rds.Client.Get(ctx, accessKey(token)).Result()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 反序列化
|
||||
auth := new(AuthContext)
|
||||
if err := json.Unmarshal([]byte(authJSON), auth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
// Create 创建一个新的会话
|
||||
func (s *sessionService) Create(ctx context.Context, auth AuthContext, config ...SessionConfig) (*TokenDetails, error) {
|
||||
// 解析可选配置
|
||||
cfg := DefaultSessionConfig
|
||||
if len(config) > 0 {
|
||||
cfg = mergeConfig(DefaultSessionConfig, config[0])
|
||||
}
|
||||
|
||||
// 生成令牌组
|
||||
accessToken := genToken()
|
||||
refreshToken := genToken()
|
||||
|
||||
// 序列化认证数据
|
||||
authData, err := json.Marshal(auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 序列化刷新令牌数据
|
||||
refreshData, err := json.Marshal(RefreshData{
|
||||
AuthContext: auth,
|
||||
AccessToken: accessToken,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 事务保存数据到 Redis
|
||||
pipe := rds.Client.TxPipeline()
|
||||
pipe.Set(ctx, accessKey(accessToken), authData, cfg.AccessTokenDuration)
|
||||
pipe.Set(ctx, refreshKey(refreshToken), refreshData, cfg.RefreshTokenDuration)
|
||||
_, err = pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TokenDetails{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExpires: time.Now().Add(cfg.AccessTokenDuration),
|
||||
RefreshToken: refreshToken,
|
||||
RefreshTokenExpires: time.Now().Add(cfg.RefreshTokenDuration),
|
||||
Auth: auth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Refresh 刷新一个会话
|
||||
func (s *sessionService) Refresh(ctx context.Context, refreshToken string, config ...SessionConfig) (*TokenDetails, error) {
|
||||
// 解析可选配置
|
||||
cfg := DefaultSessionConfig
|
||||
if len(config) > 0 {
|
||||
cfg = mergeConfig(DefaultSessionConfig, config[0])
|
||||
}
|
||||
|
||||
rKey := refreshKey(refreshToken)
|
||||
var tokenDetails *TokenDetails
|
||||
|
||||
// 刷新令牌
|
||||
err := rds.Client.Watch(ctx, func(tx *redis.Tx) error {
|
||||
|
||||
// 先获取刷新令牌数据
|
||||
refreshJson, err := tx.Get(ctx, rKey).Result()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return ErrInvalidToken
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析刷新令牌数据
|
||||
refreshData := new(RefreshData)
|
||||
if err := json.Unmarshal([]byte(refreshJson), refreshData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除旧的令牌
|
||||
pipeline := tx.Pipeline()
|
||||
pipeline.Del(ctx, accessKey(refreshData.AccessToken))
|
||||
pipeline.Del(ctx, refreshKey(refreshToken))
|
||||
|
||||
// 生成新的令牌
|
||||
newAccessToken := genToken()
|
||||
newRefreshToken := genToken()
|
||||
|
||||
authData, err := json.Marshal(refreshData.AuthContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRefreshData, err := json.Marshal(RefreshData{
|
||||
AuthContext: refreshData.AuthContext,
|
||||
AccessToken: newAccessToken,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pipeline.Set(ctx, accessKey(newAccessToken), authData, cfg.AccessTokenDuration)
|
||||
pipeline.Set(ctx, refreshKey(newRefreshToken), newRefreshData, cfg.RefreshTokenDuration)
|
||||
|
||||
_, err = pipeline.Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tokenDetails = &TokenDetails{
|
||||
AccessToken: newAccessToken,
|
||||
RefreshToken: newRefreshToken,
|
||||
AccessTokenExpires: time.Now().Add(cfg.AccessTokenDuration),
|
||||
RefreshTokenExpires: time.Now().Add(cfg.RefreshTokenDuration),
|
||||
Auth: refreshData.AuthContext,
|
||||
}
|
||||
return nil
|
||||
}, rKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("刷新令牌失败: %w", err)
|
||||
}
|
||||
|
||||
return tokenDetails, nil
|
||||
}
|
||||
|
||||
// Remove 删除会话
|
||||
func (s *sessionService) Remove(ctx context.Context, accessToken, refreshToken string) error {
|
||||
rds.Client.Del(ctx, accessKey(accessToken), refreshKey(refreshToken))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 令牌键的格式为 "session:<token>"
|
||||
func accessKey(token string) string {
|
||||
return fmt.Sprintf("session:%s", token)
|
||||
}
|
||||
|
||||
// 刷新令牌键的格式为 "session:refreshKey:<token>"
|
||||
func refreshKey(token string) string {
|
||||
return fmt.Sprintf("session:refresh:%s", token)
|
||||
}
|
||||
|
||||
// 生成一个新的令牌
|
||||
func genToken() string {
|
||||
return uuid.NewString()
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SessionConfig
|
||||
|
||||
// SessionConfig 定义会话管理的配置选项
|
||||
type SessionConfig struct {
|
||||
// 令牌配置
|
||||
AccessTokenDuration time.Duration
|
||||
RefreshTokenDuration time.Duration
|
||||
}
|
||||
|
||||
// DefaultSessionConfig 默认会话配置
|
||||
var DefaultSessionConfig = SessionConfig{
|
||||
AccessTokenDuration: 2 * time.Hour,
|
||||
RefreshTokenDuration: 7 * 24 * time.Hour,
|
||||
}
|
||||
|
||||
// 合并配置,保留非零值
|
||||
func mergeConfig(defaultCfg SessionConfig, customCfg SessionConfig) SessionConfig {
|
||||
result := defaultCfg
|
||||
|
||||
if customCfg.AccessTokenDuration != 0 {
|
||||
result.AccessTokenDuration = customCfg.AccessTokenDuration
|
||||
}
|
||||
|
||||
if customCfg.RefreshTokenDuration != 0 {
|
||||
result.RefreshTokenDuration = customCfg.RefreshTokenDuration
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region AuthContext
|
||||
|
||||
// AuthContext 定义认证信息
|
||||
type AuthContext struct {
|
||||
Payload Payload
|
||||
Permissions map[string]struct{}
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
// Payload 定义负载信息
|
||||
type Payload struct {
|
||||
Type PayloadType
|
||||
Id int32
|
||||
}
|
||||
|
||||
// PayloadType 定义负载类型
|
||||
type PayloadType int
|
||||
|
||||
const (
|
||||
// PayloadUser 用户类型
|
||||
PayloadUser PayloadType = iota
|
||||
// PayloadAdmin 管理员类型
|
||||
PayloadAdmin
|
||||
// PayloadClientPublic 公共客户端类型
|
||||
PayloadClientPublic
|
||||
// PayloadClientConfidential 机密客户端类型
|
||||
PayloadClientConfidential
|
||||
)
|
||||
|
||||
// AnyPermission 检查认证是否包含指定权限
|
||||
func (a *AuthContext) AnyPermission(requiredPermission ...string) bool {
|
||||
if a == nil || a.Permissions == nil {
|
||||
return false
|
||||
}
|
||||
for _, permission := range requiredPermission {
|
||||
if _, ok := a.Permissions[permission]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
type RefreshData struct {
|
||||
AuthContext AuthContext
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
// TokenDetails 存储令牌详细信息
|
||||
type TokenDetails struct {
|
||||
// 访问令牌
|
||||
AccessToken string
|
||||
// 刷新令牌
|
||||
RefreshToken string
|
||||
// 访问令牌过期时间
|
||||
AccessTokenExpires time.Time
|
||||
// 刷新令牌过期时间
|
||||
RefreshTokenExpires time.Time
|
||||
// 认证信息
|
||||
Auth AuthContext
|
||||
}
|
||||
124
web/services/verifier.go
Normal file
124
web/services/verifier.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/rand"
|
||||
"platform/init/rds"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var Verifier = &verifierService{}
|
||||
|
||||
type verifierService struct {
|
||||
}
|
||||
|
||||
type VerifierServiceError string
|
||||
|
||||
func (e VerifierServiceError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrVerifierServiceInvalid = VerifierServiceError("验证码错误")
|
||||
)
|
||||
|
||||
type VerifierServiceSendLimitErr int
|
||||
|
||||
func (e VerifierServiceSendLimitErr) Error() string {
|
||||
return "发送频率过快"
|
||||
}
|
||||
|
||||
type VerifierSmsPurpose int
|
||||
|
||||
const (
|
||||
Login VerifierSmsPurpose = iota
|
||||
)
|
||||
|
||||
func smsKey(phone string, purpose VerifierSmsPurpose) string {
|
||||
return fmt.Sprintf("verify:sms:%d:%s", purpose, phone)
|
||||
}
|
||||
|
||||
func (s *verifierService) SendSms(ctx context.Context, phone string, purpose VerifierSmsPurpose) error {
|
||||
key := smsKey(phone, purpose)
|
||||
keyLock := key + ":lock"
|
||||
|
||||
// 生成验证码
|
||||
code := rand.Intn(900000) + 100000 // 6-digit code between 100000-999999
|
||||
|
||||
// 检查发送频率,1 分钟内只能发送一次
|
||||
err := rds.Client.Watch(ctx, func(tx *redis.Tx) error {
|
||||
result, err := tx.TTL(ctx, keyLock).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result > 0 {
|
||||
return VerifierServiceSendLimitErr(result.Seconds())
|
||||
}
|
||||
if result != -2 {
|
||||
return VerifierServiceError("验证码检查异常")
|
||||
}
|
||||
|
||||
pipe := rds.Client.Pipeline()
|
||||
pipe.Set(ctx, key, code, 10*time.Minute)
|
||||
pipe.Set(ctx, keyLock, "", 1*time.Minute)
|
||||
_, err = pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, keyLock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: 发送短信验证码
|
||||
slog.Debug("发送验证码", slog.String("phone", phone), slog.String("code", strconv.Itoa(code)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *verifierService) VerifySms(ctx context.Context, phone, code string) (bool, error) {
|
||||
key := smsKey(phone, Login)
|
||||
keyLock := key + ":lock"
|
||||
|
||||
err := rds.Client.Watch(ctx, func(tx *redis.Tx) error {
|
||||
|
||||
// 检查验证码
|
||||
val, err := rds.Client.Get(ctx, key).Result()
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
slog.Error("验证码获取失败", slog.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
if val != code {
|
||||
return ErrVerifierServiceInvalid
|
||||
}
|
||||
|
||||
// 删除验证码
|
||||
_, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
|
||||
pipe.Del(ctx, key)
|
||||
pipe.Del(ctx, keyLock)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
slog.Error("验证码删除失败", slog.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, key)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrVerifierServiceInvalid) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package utils
|
||||
|
||||
type ErrResp struct {
|
||||
Cause string `json:"cause"`
|
||||
}
|
||||
32
web/web.go
32
web/web.go
@@ -1,18 +1,20 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"platform/init/env"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||
)
|
||||
import "log/slog"
|
||||
|
||||
type Config struct {
|
||||
Logger *slog.Logger
|
||||
Listen string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
config *Config
|
||||
log *slog.Logger
|
||||
fiber *fiber.App
|
||||
}
|
||||
|
||||
@@ -22,33 +24,35 @@ func New(config *Config) (*Server, error) {
|
||||
_config = &Config{}
|
||||
}
|
||||
|
||||
if _config.Logger == nil {
|
||||
_config.Logger = slog.Default()
|
||||
}
|
||||
|
||||
return &Server{
|
||||
config: _config,
|
||||
log: _config.Logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
s.fiber = fiber.New(fiber.Config{})
|
||||
UseRoute(s.fiber)
|
||||
s.fiber = fiber.New(fiber.Config{
|
||||
ErrorHandler: ErrorHandler,
|
||||
})
|
||||
|
||||
s.log.Info("Server started on :8080")
|
||||
err := s.fiber.Listen(":8080")
|
||||
s.fiber.Use(requestid.New())
|
||||
s.fiber.Use(logger.New())
|
||||
|
||||
ApplyRouters(s.fiber)
|
||||
|
||||
port := env.AppPort
|
||||
slog.Info("Server started on :" + port)
|
||||
err := s.fiber.Listen(":" + port)
|
||||
if err != nil {
|
||||
s.log.Error("Failed to start server", slog.Any("error", err))
|
||||
slog.Error("Failed to start server", slog.Any("err", err))
|
||||
}
|
||||
|
||||
s.log.Info("Server stopped")
|
||||
slog.Info("Server stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Stop() {
|
||||
err := s.fiber.Shutdown()
|
||||
if err != nil {
|
||||
s.log.Error("Failed to shutdown server", slog.Any("error", err))
|
||||
slog.Error("Failed to shutdown server", slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user