完善登录逻辑,登录接口统一到 /token

This commit is contained in:
2025-04-23 19:01:08 +08:00
parent b181864a2f
commit 1374757eab
28 changed files with 404 additions and 266 deletions

View File

@@ -22,6 +22,12 @@
- [ ] Limiter - [ ] Limiter
- [ ] Compress - [ ] Compress
使用 fiber 自带 validator 进行参数验证
增加 domain 层,缓解同包字段过长的问题
移动端适配
授权过期跳转登录,成功后返回原链接 授权过期跳转登录,成功后返回原链接
错误处理类型转换失败问题 错误处理类型转换失败问题

View File

@@ -179,20 +179,21 @@ comment on column user_role.deleted_at is '删除时间';
drop table if exists client cascade; drop table if exists client cascade;
create table client ( create table client (
id serial primary key, id serial primary key,
client_id varchar(255) not null unique, client_id varchar(255) not null unique,
client_secret varchar(255) not null, client_secret varchar(255) not null,
redirect_uri varchar(255), redirect_uri varchar(255),
grant_code bool not null default false, grant_code bool not null default false,
grant_client bool not null default false, grant_client bool not null default false,
grant_refresh bool not null default false, grant_refresh bool not null default false,
spec int not null, grant_password bool not null default false,
name varchar(255) not null, spec int not null,
icon varchar(255), name varchar(255) not null,
status int not null default 1, icon varchar(255),
created_at timestamp default current_timestamp, status int not null default 1,
updated_at timestamp default current_timestamp, created_at timestamp default current_timestamp,
deleted_at timestamp updated_at timestamp default current_timestamp,
deleted_at timestamp
); );
create index client_client_id_index on client (client_id); create index client_client_id_index on client (client_id);
@@ -209,6 +210,7 @@ comment on column client.redirect_uri is 'OAuth2 重定向URI';
comment on column client.grant_code is '允许授权码授予'; comment on column client.grant_code is '允许授权码授予';
comment on column client.grant_client is '允许客户端凭证授予'; comment on column client.grant_client is '允许客户端凭证授予';
comment on column client.grant_refresh is '允许刷新令牌授予'; comment on column client.grant_refresh is '允许刷新令牌授予';
comment on column client.grant_password is '允许密码授予';
comment on column client.spec is '安全规范0-web1-native2-browser'; comment on column client.spec is '安全规范0-web1-native2-browser';
comment on column client.name is '名称'; comment on column client.name is '名称';
comment on column client.icon is '图标URL'; comment on column client.icon is '图标URL';
@@ -822,19 +824,19 @@ comment on column bill.deleted_at is '删除时间';
-- coupon 优惠券 -- coupon 优惠券
drop table if exists coupon cascade; drop table if exists coupon cascade;
create table coupon ( create table coupon (
id serial primary key, id serial primary key,
user_id int references "user" (id) user_id int references "user" (id)
on update cascade on update cascade
on delete cascade, on delete cascade,
code varchar(255) not null unique, code varchar(255) not null unique,
remark varchar(255), remark varchar(255),
amount decimal(12, 2) not null default 0, amount decimal(12, 2) not null default 0,
min_amount decimal(12, 2) not null default 0, min_amount decimal(12, 2) not null default 0,
status int not null default 0, status int not null default 0,
expire_at timestamp, expire_at timestamp,
created_at timestamp default current_timestamp, created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp, updated_at timestamp default current_timestamp,
deleted_at timestamp deleted_at timestamp
); );
create index coupon_user_id_index on coupon (user_id); create index coupon_user_id_index on coupon (user_id);
create index coupon_code_index on coupon (code); create index coupon_code_index on coupon (code);

View File

@@ -76,7 +76,6 @@ func Permit(types []services.PayloadType, permissions ...string) fiber.Handler {
return c.Next() return c.Next()
} }
} }
func PermitAll(permissions ...string) fiber.Handler { func PermitAll(permissions ...string) fiber.Handler {
@@ -88,14 +87,6 @@ func PermitAll(permissions ...string) fiber.Handler {
}, permissions...) }, permissions...)
} }
// PermitUser 创建针对单个路由的鉴权中间件
func PermitUser(permissions ...string) fiber.Handler {
return Permit([]services.PayloadType{
services.PayloadUser,
services.PayloadAdmin,
}, permissions...)
}
func PermitDevice(permissions ...string) fiber.Handler { func PermitDevice(permissions ...string) fiber.Handler {
return Permit([]services.PayloadType{ return Permit([]services.PayloadType{
services.PayloadClientPublic, services.PayloadClientPublic,
@@ -104,74 +95,6 @@ func PermitDevice(permissions ...string) fiber.Handler {
}, permissions...) }, permissions...)
} }
func PermitPublic(permissions ...string) fiber.Handler {
return Permit([]services.PayloadType{
services.PayloadClientPublic,
services.PayloadAdmin,
}, permissions...)
}
func PermitConfidential(permissions ...string) fiber.Handler {
return Permit([]services.PayloadType{
services.PayloadClientConfidential,
services.PayloadAdmin,
}, permissions...)
}
func authBearer(ctx context.Context, token string) (*services.AuthContext, error) {
auth, err := services.Session.Find(ctx, token)
if err != nil {
slog.Debug(err.Error())
return nil, err
}
return auth, nil
}
func authBasic(ctx context.Context, token string) (*services.AuthContext, error) {
// 解析 Basic 认证信息
var base, err = base64.URLEncoding.DecodeString(token)
if err != nil {
slog.Debug(err.Error())
return nil, err
}
var split = strings.Split(string(base), ":")
if len(split) != 2 {
msg := "无法解析 Basic 认证信息"
slog.Debug(msg)
return nil, errors.New(msg)
}
var clientID = split[0]
// 获取客户端信息
client, err := q.Client.
Where(
q.Client.ClientID.Eq(clientID),
q.Client.Spec.Eq(0),
q.Client.GrantClient.Is(true),
q.Client.Status.Eq(1)).
Take()
if err != nil {
return nil, err
}
// todo 查询客户端关联权限
// 组织授权信息(一次性请求)
return &services.AuthContext{
Payload: services.Payload{
Id: client.ID,
Type: services.PayloadClientConfidential,
Name: client.Name,
Avatar: client.Icon,
},
Permissions: nil,
Metadata: nil,
}, nil
}
func Protect(c *fiber.Ctx, types []services.PayloadType, permissions []string) (*services.AuthContext, error) { func Protect(c *fiber.Ctx, types []services.PayloadType, permissions []string) (*services.AuthContext, error) {
// 获取令牌 // 获取令牌
var header = c.Get("Authorization") var header = c.Get("Authorization")
@@ -216,3 +139,57 @@ func Protect(c *fiber.Ctx, types []services.PayloadType, permissions []string) (
return auth, nil return auth, nil
} }
func authBearer(ctx context.Context, token string) (*services.AuthContext, error) {
auth, err := services.Session.Find(ctx, token)
if err != nil {
slog.Debug(err.Error())
return nil, err
}
return auth, nil
}
func authBasic(_ context.Context, token string) (*services.AuthContext, error) {
// 解析 Basic 认证信息
var base, err = base64.URLEncoding.DecodeString(token)
if err != nil {
slog.Debug(err.Error())
return nil, err
}
var split = strings.Split(string(base), ":")
if len(split) != 2 {
msg := "无法解析 Basic 认证信息"
slog.Debug(msg)
return nil, errors.New(msg)
}
var clientID = split[0]
// 获取客户端信息
client, err := q.Client.
Where(
q.Client.ClientID.Eq(clientID),
q.Client.Spec.Eq(0),
q.Client.GrantClient.Is(true),
q.Client.Status.Eq(1)).
Take()
if err != nil {
return nil, err
}
// todo 查询客户端关联权限
// 组织授权信息(一次性请求)
return &services.AuthContext{
Payload: services.Payload{
Id: client.ID,
Type: services.PayloadClientConfidential,
Name: client.Name,
Avatar: client.Icon,
},
Permissions: nil,
Metadata: nil,
}, nil
}

View File

@@ -51,7 +51,7 @@ func ListBill(c *fiber.Ctx) error {
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo)) do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
} }
bills, err := do.Debug(). bills, err := do.
Preload(q.Bill.Resource, q.Bill.Trade, q.Bill.Refund). Preload(q.Bill.Resource, q.Bill.Trade, q.Bill.Refund).
Preload(q.Bill.Resource.Pss). Preload(q.Bill.Resource.Pss).
Order(q.Bill.CreatedAt.Desc()). Order(q.Bill.CreatedAt.Desc()).

View File

@@ -3,9 +3,10 @@ package handlers
import ( import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"platform/web/models" "log/slog"
m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
"platform/web/services" s "platform/web/services"
"strings" "strings"
"time" "time"
@@ -17,22 +18,42 @@ import (
// region Token // region Token
type TokenReq struct { type TokenReq struct {
ClientID string `json:"client_id" form:"client_id"` GrantType s.OauthGrantType `json:"grant_type" form:"grant_type"`
ClientSecret string `json:"client_secret" form:"client_secret"` ClientID string `json:"client_id" form:"client_id"`
GrantType TokenGrantType `json:"grant_type" form:"grant_type"` ClientSecret string `json:"client_secret" form:"client_secret"`
Code string `json:"code" form:"code"` Scope string `json:"scope" form:"scope"`
RedirectURI string `json:"redirect_uri" form:"redirect_uri"` TokenReqCode
CodeVerifier string `json:"code_verifier" form:"code_verifier"` TokenReqClient
RefreshToken string `json:"refresh_token" form:"refresh_token"` TokenReqRefresh
Scope string `json:"scope" form:"scope"` TokenReqPassword
}
type TokenReqCode struct {
Code string `json:"code" form:"code"`
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
}
type TokenReqClient struct {
}
type TokenReqRefresh struct {
RefreshToken string `json:"refresh_token" form:"refresh_token"`
}
type TokenReqPassword struct {
LoginType s.OauthGrantLoginType `json:"login_type" form:"login_type"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
Remember bool `json:"remember" form:"remember"`
} }
type TokenResp struct { type TokenResp struct {
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"` RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"` TokenType string `json:"token_type"`
Scope string `json:"scope,omitempty"` Scope string `json:"scope,omitempty"`
ExpiresIn int `json:"expires_in"`
} }
type TokenErrResp struct { type TokenErrResp struct {
@@ -40,57 +61,57 @@ type TokenErrResp struct {
Description string `json:"error_description,omitempty"` 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 授权请求 // Token 处理 OAuth2.0 授权请求
func Token(c *fiber.Ctx) error { func Token(c *fiber.Ctx) error {
// 验证请求参数 // 验证请求参数
req := new(TokenReq) req := new(TokenReq)
if err := c.BodyParser(req); err != nil { if err := c.BodyParser(req); err != nil {
return sendError(c, services.ErrOauthInvalidRequest, "无法解析请求参数") return sendError(c, s.ErrOauthInvalidRequest, "无法解析请求参数")
} }
if req.GrantType == "" { if req.GrantType == "" {
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数grant_type") return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数grant_type")
} }
slog.Debug("oauth token", slog.String("grant_type",
string(req.GrantType)),
slog.String("client_id", req.ClientID),
)
// 基于授权类型处理请求 // 基于授权类型处理请求
switch req.GrantType { switch req.GrantType {
case AuthorizationCode: case s.OauthGrantTypeAuthorizationCode:
return authorizationCode(c, req) return authorizationCode(c, req)
case ClientCredentials: case s.OauthGrantTypeClientCredentials:
return clientCredentials(c, req) return clientCredentials(c, req)
case RefreshToken: case s.OauthGrantTypeRefreshToken:
return refreshToken(c, req) return refreshToken(c, req)
case s.OauthGrantTypePassword:
return password(c, req)
default: default:
return sendError(c, services.ErrOauthUnsupportedGrantType) return sendError(c, s.ErrOauthUnsupportedGrantType)
} }
} }
// 授权码 // 授权码
func authorizationCode(c *fiber.Ctx, req *TokenReq) error { func authorizationCode(c *fiber.Ctx, req *TokenReq) error {
if req.Code == "" { if req.Code == "" {
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数code") return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数code")
} }
client, err := protect(c, services.GrantTypeAuthorizationCode, req.ClientID, req.ClientSecret) client, err := protect(c, s.OauthGrantTypeAuthorizationCode, req.ClientID, req.ClientSecret)
if err != nil { if err != nil {
return sendError(c, err) return sendError(c, err)
} }
token, err := services.Auth.OauthAuthorizationCode(c.Context(), client, req.Code, req.RedirectURI, req.CodeVerifier) token, err := s.Auth.OauthAuthorizationCode(c.Context(), client, req.Code, req.RedirectURI, req.CodeVerifier)
if err != nil { if err != nil {
return sendError(c, err.(services.AuthServiceOauthError)) return sendError(c, err.(s.AuthServiceOauthError))
} }
return sendSuccess(c, token) return sendSuccess(c, token)
@@ -98,15 +119,15 @@ func authorizationCode(c *fiber.Ctx, req *TokenReq) error {
// 客户端凭证 // 客户端凭证
func clientCredentials(c *fiber.Ctx, req *TokenReq) error { func clientCredentials(c *fiber.Ctx, req *TokenReq) error {
client, err := protect(c, services.GrantTypeClientCredentials, req.ClientID, req.ClientSecret) client, err := protect(c, s.OauthGrantTypeClientCredentials, req.ClientID, req.ClientSecret)
if err != nil { if err != nil {
return sendError(c, err) return sendError(c, err)
} }
scope := strings.Split(req.Scope, ",") scope := strings.Split(req.Scope, ",")
token, err := services.Auth.OauthClientCredentials(c.Context(), client, scope...) token, err := s.Auth.OauthClientCredentials(c.Context(), client, scope...)
if err != nil { if err != nil {
return sendError(c, err.(services.AuthServiceOauthError)) return sendError(c, err.(s.AuthServiceOauthError))
} }
return sendSuccess(c, token) return sendSuccess(c, token)
@@ -115,19 +136,19 @@ func clientCredentials(c *fiber.Ctx, req *TokenReq) error {
// 刷新令牌 // 刷新令牌
func refreshToken(c *fiber.Ctx, req *TokenReq) error { func refreshToken(c *fiber.Ctx, req *TokenReq) error {
if req.RefreshToken == "" { if req.RefreshToken == "" {
return sendError(c, services.ErrOauthInvalidRequest, "缺少必要参数refresh_token") return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数refresh_token")
} }
client, err := protect(c, services.GrantTypeRefreshToken, req.ClientID, req.ClientSecret) client, err := protect(c, s.OauthGrantTypeRefreshToken, req.ClientID, req.ClientSecret)
if err != nil { if err != nil {
return sendError(c, err) return sendError(c, err)
} }
scope := strings.Split(req.Scope, ",") scope := strings.Split(req.Scope, ",")
token, err := services.Auth.OauthRefreshToken(c.Context(), client, req.RefreshToken, scope) token, err := s.Auth.OauthRefreshToken(c.Context(), client, req.RefreshToken, scope)
if err != nil { if err != nil {
if errors.Is(err, services.ErrInvalidToken) { if errors.Is(err, s.ErrInvalidToken) {
return sendError(c, services.ErrOauthInvalidGrant) return sendError(c, s.ErrOauthInvalidGrant)
} }
return sendError(c, err) return sendError(c, err)
} }
@@ -135,8 +156,108 @@ func refreshToken(c *fiber.Ctx, req *TokenReq) error {
return sendSuccess(c, token) return sendSuccess(c, token)
} }
func password(c *fiber.Ctx, req *TokenReq) error {
if req.LoginType == "" {
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数password_type")
}
if req.Username == "" {
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数username")
}
if req.Password == "" {
return sendError(c, s.ErrOauthInvalidRequest, "缺少必要参数password")
}
// 验证客户端凭证
_, err := protect(c, s.OauthGrantTypePassword, req.ClientID, req.ClientSecret)
if err != nil {
return sendError(c, err)
}
// 验证验证码
err = s.Verifier.VerifySms(c.Context(), req.Username, req.Password)
if err != nil {
if errors.Is(err, s.ErrVerifierServiceInvalid) {
return fiber.NewError(fiber.StatusBadRequest, "验证码错误")
}
return err
}
// 查找用户
var user *m.User
err = q.Q.Transaction(func(tx *q.Query) error {
switch req.LoginType {
case s.OauthGrantPasswordTypePhoneCode:
user, err = tx.User.Where(tx.User.Phone.Eq(req.Username)).Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
case s.OauthGrantPasswordTypeEmailCode:
user, err = tx.User.Where(tx.User.Email.Eq(req.Username)).Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
case s.OauthGrantPasswordTypePassword:
user, err = tx.User.
Where(tx.User.Or(
tx.User.Phone.Eq(req.Username),
tx.User.Email.Eq(req.Username),
tx.User.Username.Eq(req.Username),
)).
Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
default:
return sendError(c, s.ErrOauthInvalidRequest, "无效的登录类型")
}
// 如果用户不存在,初始化用户 todo 初始化默认权限信息
if user == nil {
user = &m.User{
Phone: req.Username,
Username: 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
}
return nil
})
if err != nil {
return err
}
// 保存到会话
auth := s.AuthContext{
Payload: s.Payload{
Id: user.ID,
Type: s.PayloadUser,
Name: user.Name,
Avatar: user.Avatar,
},
}
duration := s.DefaultSessionConfig
if !req.Remember {
duration.RefreshTokenDuration = 0
}
token, err := s.Session.Create(c.Context(), auth)
if err != nil {
return err
}
return sendSuccess(c, token)
}
// 检查客户端凭证 // 检查客户端凭证
func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret string) (*models.Client, error) { func protect(c *fiber.Ctx, grant s.OauthGrantType, clientId, clientSecret string) (*m.Client, error) {
header := c.Get("Authorization") header := c.Get("Authorization")
if header != "" { if header != "" {
basic := strings.TrimPrefix(header, "Basic ") basic := strings.TrimPrefix(header, "Basic ")
@@ -155,44 +276,48 @@ func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret stri
// 查找客户端 // 查找客户端
if clientId == "" { if clientId == "" {
return nil, services.ErrOauthInvalidRequest return nil, s.ErrOauthInvalidRequest
} }
client, err := q.Client.Where(q.Client.ClientID.Eq(clientId)).Take() client, err := q.Client.Where(q.Client.ClientID.Eq(clientId)).Take()
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, services.ErrOauthInvalidClient return nil, s.ErrOauthInvalidClient
} }
return nil, err return nil, err
} }
// 验证客户端状态 // 验证客户端状态
if client.Status != 1 { if client.Status != 1 {
return nil, services.ErrOauthUnauthorizedClient return nil, s.ErrOauthUnauthorizedClient
} }
// 验证授权类型 // 验证授权类型
switch grant { switch grant {
case services.GrantTypeAuthorizationCode: case s.OauthGrantTypeAuthorizationCode:
if !client.GrantCode { if !client.GrantCode {
return nil, services.ErrOauthUnauthorizedClient return nil, s.ErrOauthUnauthorizedClient
} }
case services.GrantTypeClientCredentials: case s.OauthGrantTypeClientCredentials:
if !client.GrantClient || client.Spec != 0 { if !client.GrantClient || client.Spec != 0 {
return nil, services.ErrOauthUnauthorizedClient return nil, s.ErrOauthUnauthorizedClient
} }
case services.GrantTypeRefreshToken: case s.OauthGrantTypeRefreshToken:
if !client.GrantRefresh { if !client.GrantRefresh {
return nil, services.ErrOauthUnauthorizedClient return nil, s.ErrOauthUnauthorizedClient
}
case s.OauthGrantTypePassword:
if !client.GrantPassword {
return nil, s.ErrOauthUnauthorizedClient
} }
} }
// 如果客户端是 confidential验证 client_secret失败返回错误 // 如果客户端是 confidential验证 client_secret失败返回错误
if client.Spec == 0 { if client.Spec == 0 {
if clientSecret == "" { if clientSecret == "" {
return nil, services.ErrOauthInvalidRequest return nil, s.ErrOauthInvalidRequest
} }
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil { if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
return nil, services.ErrOauthInvalidClient return nil, s.ErrOauthInvalidClient
} }
} }
@@ -200,7 +325,7 @@ func protect(c *fiber.Ctx, grant services.GrantType, clientId, clientSecret stri
} }
// 发送成功响应 // 发送成功响应
func sendSuccess(c *fiber.Ctx, details *services.TokenDetails) error { func sendSuccess(c *fiber.Ctx, details *s.TokenDetails) error {
return c.JSON(TokenResp{ return c.JSON(TokenResp{
AccessToken: details.AccessToken, AccessToken: details.AccessToken,
TokenType: "Bearer", TokenType: "Bearer",
@@ -211,23 +336,23 @@ func sendSuccess(c *fiber.Ctx, details *services.TokenDetails) error {
// 发送错误响应 // 发送错误响应
func sendError(c *fiber.Ctx, err error, description ...string) error { func sendError(c *fiber.Ctx, err error, description ...string) error {
var sErr services.AuthServiceOauthError var sErr s.AuthServiceOauthError
if errors.As(err, &sErr) { if errors.As(err, &sErr) {
status := fiber.StatusBadRequest status := fiber.StatusBadRequest
var desc string var desc string
switch { switch {
case errors.Is(sErr, services.ErrOauthInvalidRequest): case errors.Is(sErr, s.ErrOauthInvalidRequest):
desc = "无效的请求" desc = "无效的请求"
case errors.Is(sErr, services.ErrOauthInvalidClient): case errors.Is(sErr, s.ErrOauthInvalidClient):
status = fiber.StatusUnauthorized status = fiber.StatusUnauthorized
desc = "无效的客户端凭证" desc = "无效的客户端凭证"
case errors.Is(sErr, services.ErrOauthInvalidGrant): case errors.Is(sErr, s.ErrOauthInvalidGrant):
desc = "无效的授权凭证" desc = "无效的授权凭证"
case errors.Is(sErr, services.ErrOauthInvalidScope): case errors.Is(sErr, s.ErrOauthInvalidScope):
desc = "无效的授权范围" desc = "无效的授权范围"
case errors.Is(sErr, services.ErrOauthUnauthorizedClient): case errors.Is(sErr, s.ErrOauthUnauthorizedClient):
desc = "未授权的客户端" desc = "未授权的客户端"
case errors.Is(sErr, services.ErrOauthUnsupportedGrantType): case errors.Is(sErr, s.ErrOauthUnsupportedGrantType):
desc = "不支持的授权类型" desc = "不支持的授权类型"
} }
if len(description) > 0 { if len(description) > 0 {

View File

@@ -2,6 +2,7 @@ package handlers
import ( import (
"errors" "errors"
"platform/web/auth"
"platform/web/services" "platform/web/services"
"regexp" "regexp"
"strconv" "strconv"
@@ -16,6 +17,13 @@ type VerifierReq struct {
func SmsCode(c *fiber.Ctx) error { func SmsCode(c *fiber.Ctx) error {
_, err := auth.Protect(c, []services.PayloadType{
services.PayloadClientConfidential,
}, []string{})
if err != nil {
return err
}
// 解析请求参数 // 解析请求参数
req := new(VerifierReq) req := new(VerifierReq)
if err := c.BodyParser(req); err != nil { if err := c.BodyParser(req); err != nil {

View File

@@ -20,12 +20,12 @@ type Bill struct {
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
TradeID int32 `gorm:"column:trade_id" json:"trade_id"` TradeID int32 `gorm:"column:trade_id;comment:订单ID" json:"trade_id"` // 订单ID
ResourceID int32 `gorm:"column:resource_id" json:"resource_id"` ResourceID int32 `gorm:"column:resource_id;comment:套餐ID" json:"resource_id"` // 套餐ID
Type int32 `gorm:"column:type;not null" json:"type"` Type int32 `gorm:"column:type;not null;comment:账单类型0-充值1-消费2-退款" json:"type"` // 账单类型0-充值1-消费2-退款
BillNo string `gorm:"column:bill_no;not null" json:"bill_no"` BillNo string `gorm:"column:bill_no;not null;comment:易读账单号" json:"bill_no"` // 易读账单号
RefundID int32 `gorm:"column:refund_id" json:"refund_id"` RefundID int32 `gorm:"column:refund_id;comment:退款ID" json:"refund_id"` // 退款ID
Amount float64 `gorm:"column:amount;not null" json:"amount"` Amount float64 `gorm:"column:amount;not null;comment:账单金额" json:"amount"` // 账单金额
Trade *Trade `gorm:"foreignKey:TradeID" json:"trade"` Trade *Trade `gorm:"foreignKey:TradeID" json:"trade"`
Refund *Refund `gorm:"foreignKey:RefundID" json:"refund"` Refund *Refund `gorm:"foreignKey:RefundID" json:"refund"`
Resource *Resource `gorm:"foreignKey:ResourceID" json:"resource"` Resource *Resource `gorm:"foreignKey:ResourceID" json:"resource"`

View File

@@ -31,7 +31,7 @@ type Channel struct {
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
ProxyHost string `gorm:"column:proxy_host;not null" json:"proxy_host"` ProxyHost string `gorm:"column:proxy_host;not null" json:"proxy_host"`
Protocol int32 `gorm:"column:protocol" json:"protocol"` Protocol int32 `gorm:"column:protocol;comment:协议类型1-http2-https3-socks5" json:"protocol"` // 协议类型1-http2-https3-socks5
} }
// TableName Channel's table name // TableName Channel's table name

View File

@@ -14,20 +14,21 @@ const TableNameClient = "client"
// Client mapped from table <client> // Client mapped from table <client>
type Client struct { type Client struct {
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:客户端ID" json:"id"` // 客户端ID 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客户端标识符 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客户端密钥 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 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"` // 允许授权码授予 GrantCode bool `gorm:"column:grant_code;not null;comment:允许授权码授予" json:"grant_code"` // 允许授权码授予
GrantClient bool `gorm:"column:grant_client;not null;comment:允许客户端凭证授予" json:"grant_client"` // 允许客户端凭证授予 GrantClient bool `gorm:"column:grant_client;not null;comment:允许客户端凭证授予" json:"grant_client"` // 允许客户端凭证授予
GrantRefresh bool `gorm:"column:grant_refresh;not null;comment:允许刷新令牌授予" json:"grant_refresh"` // 允许刷新令牌授予 GrantRefresh bool `gorm:"column:grant_refresh;not null;comment:允许刷新令牌授予" json:"grant_refresh"` // 允许刷新令牌授予
Spec int32 `gorm:"column:spec;not null;comment:安全规范0-web1-native2-browser" json:"spec"` // 安全规范0-web1-native2-browser Spec int32 `gorm:"column:spec;not null;comment:安全规范0-web1-native2-browser" json:"spec"` // 安全规范0-web1-native2-browser
Name string `gorm:"column:name;not null;comment:名称" json:"name"` // 名称 Name string `gorm:"column:name;not null;comment:名称" json:"name"` // 名称
Icon string `gorm:"column:icon;comment:图标URL" json:"icon"` // 图标URL Icon string `gorm:"column:icon;comment:图标URL" json:"icon"` // 图标URL
Status int32 `gorm:"column:status;not null;default:1;comment:状态1-正常0-禁用" json:"status"` // 状态1-正常0-禁用 Status int32 `gorm:"column:status;not null;default:1;comment:状态1-正常0-禁用" json:"status"` // 状态1-正常0-禁用
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
GrantPassword bool `gorm:"column:grant_password;not null" json:"grant_password"`
} }
// TableName Client's table name // TableName Client's table name

View File

@@ -15,17 +15,17 @@ const TableNameCoupon = "coupon"
// Coupon mapped from table <coupon> // Coupon mapped from table <coupon>
type Coupon struct { type Coupon struct {
ExpireAt time.Time `gorm:"column:expire_at" json:"expire_at"` ExpireAt time.Time `gorm:"column:expire_at;comment:过期时间" json:"expire_at"` // 过期时间
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP" json:"updated_at"` UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"` DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:优惠券ID" json:"id"` // 优惠券ID
UserID int32 `gorm:"column:user_id" json:"user_id"` UserID int32 `gorm:"column:user_id;comment:用户ID" json:"user_id"` // 用户ID
Status int32 `gorm:"column:status;not null" json:"status"` Status int32 `gorm:"column:status;not null;comment:优惠券状态0-未使用1-已使用2-已过期" json:"status"` // 优惠券状态0-未使用1-已使用2-已过期
Code string `gorm:"column:code;not null" json:"code"` Code string `gorm:"column:code;not null;comment:优惠券代码" json:"code"` // 优惠券代码
Remark string `gorm:"column:remark" json:"remark"` Remark string `gorm:"column:remark;comment:优惠券备注" json:"remark"` // 优惠券备注
Amount float64 `gorm:"column:amount;not null" json:"amount"` Amount float64 `gorm:"column:amount;not null;comment:优惠券金额" json:"amount"` // 优惠券金额
MinAmount float64 `gorm:"column:min_amount;not null" json:"min_amount"` MinAmount float64 `gorm:"column:min_amount;not null;comment:最低消费金额" json:"min_amount"` // 最低消费金额
} }
// TableName Coupon's table name // TableName Coupon's table name

View File

@@ -22,13 +22,13 @@ type Node struct {
City string `gorm:"column:city;not null;comment:城市" json:"city"` // 城市 City string `gorm:"column:city;not null;comment:城市" json:"city"` // 城市
ProxyID int32 `gorm:"column:proxy_id;comment:代理ID" json:"proxy_id"` // 代理ID ProxyID int32 `gorm:"column:proxy_id;comment:代理ID" json:"proxy_id"` // 代理ID
ProxyPort int32 `gorm:"column:proxy_port;comment:代理端口" json:"proxy_port"` // 代理端口 ProxyPort int32 `gorm:"column:proxy_port;comment:代理端口" json:"proxy_port"` // 代理端口
Status int32 `gorm:"column:status;not null;comment:节点状态:1-正常,0-离线" json:"status"` // 节点状态:1-正常,0-离线 Status int32 `gorm:"column:status;not null;comment:节点状态0-离线1-正常" json:"status"` // 节点状态0-离线1-正常
Rtt int32 `gorm:"column:rtt;comment:延迟" json:"rtt"` // 延迟 Rtt int32 `gorm:"column:rtt;comment:延迟" json:"rtt"` // 延迟
Loss int32 `gorm:"column:loss;comment:丢包率" json:"loss"` // 丢包率 Loss int32 `gorm:"column:loss;comment:丢包率" json:"loss"` // 丢包率
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
Isp int32 `gorm:"column:isp;not null" json:"isp"` Isp int32 `gorm:"column:isp;not null;comment:运营商0-其他1-电信2-联通3-移动" json:"isp"` // 运营商0-其他1-电信2-联通3-移动
} }
// TableName Node's table name // TableName Node's table name

View File

@@ -20,9 +20,9 @@ type Refund struct {
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
TradeID int32 `gorm:"column:trade_id;not null" json:"trade_id"` TradeID int32 `gorm:"column:trade_id;not null;comment:订单ID" json:"trade_id"` // 订单ID
Reason string `gorm:"column:reason" json:"reason"` Reason string `gorm:"column:reason;comment:退款原因" json:"reason"` // 退款原因
Status int32 `gorm:"column:status;not null" json:"status"` Status int32 `gorm:"column:status;not null;comment:退款状态0-待处理1-已退款2-已拒绝" json:"status"` // 退款状态0-待处理1-已退款2-已拒绝
} }
// TableName Refund's table name // TableName Refund's table name

View File

@@ -20,8 +20,8 @@ type Resource struct {
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
ResourceNo string `gorm:"column:resource_no" json:"resource_no"` ResourceNo string `gorm:"column:resource_no;comment:套餐编号" json:"resource_no"` // 套餐编号
Type int32 `gorm:"column:type;not null" json:"type"` Type int32 `gorm:"column:type;not null;comment:套餐类型1-动态2-隧道3-独享" json:"type"` // 套餐类型1-动态2-隧道3-独享
Pss *ResourcePss `gorm:"foreignKey:ResourceID;references:ID" json:"pss"` Pss *ResourcePss `gorm:"foreignKey:ResourceID;references:ID" json:"pss"`
} }

View File

@@ -27,10 +27,10 @@ type Trade struct {
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
Type int32 `gorm:"column:type;not null" json:"type"` Type int32 `gorm:"column:type;not null;comment:订单类型0-充值余额1-购买产品" json:"type"` // 订单类型0-充值余额1-购买产品
CancelAt common.LocalDateTime `gorm:"column:cancel_at" json:"cancel_at"` CancelAt common.LocalDateTime `gorm:"column:cancel_at;comment:取消时间" json:"cancel_at"` // 取消时间
PaidAt common.LocalDateTime `gorm:"column:paid_at" json:"paid_at"` PaidAt common.LocalDateTime `gorm:"column:paid_at;comment:支付时间" json:"paid_at"` // 支付时间
PayURL string `gorm:"column:pay_url" json:"pay_url"` PayURL string `gorm:"column:pay_url;comment:支付链接" json:"pay_url"` // 支付链接
} }
// TableName Trade's table name // TableName Trade's table name

View File

@@ -20,7 +20,7 @@ type Whitelist struct {
CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt common.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt common.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
Remark string `gorm:"column:remark" json:"remark"` Remark string `gorm:"column:remark;comment:备注" json:"remark"` // 备注
} }
// TableName Whitelist's table name // TableName Whitelist's table name

View File

@@ -71,18 +71,18 @@ type bill struct {
billDo billDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 // 账单ID ID field.Int32 // 账单ID
UserID field.Int32 // 用户ID UserID field.Int32 // 用户ID
Info field.String // 产品可读信息 Info field.String // 产品可读信息
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
TradeID field.Int32 TradeID field.Int32 // 订单ID
ResourceID field.Int32 ResourceID field.Int32 // 套餐ID
Type field.Int32 Type field.Int32 // 账单类型0-充值1-消费2-退款
BillNo field.String BillNo field.String // 易读账单号
RefundID field.Int32 RefundID field.Int32 // 退款ID
Amount field.Float64 Amount field.Float64 // 账单金额
Trade billBelongsToTrade Trade billBelongsToTrade
Refund billBelongsToRefund Refund billBelongsToRefund

View File

@@ -70,7 +70,7 @@ type channel struct {
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
ProxyHost field.String ProxyHost field.String
Protocol field.Int32 Protocol field.Int32 // 协议类型1-http2-https3-socks5
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }

View File

@@ -41,6 +41,7 @@ func newClient(db *gorm.DB, opts ...gen.DOOption) client {
_client.CreatedAt = field.NewField(tableName, "created_at") _client.CreatedAt = field.NewField(tableName, "created_at")
_client.UpdatedAt = field.NewField(tableName, "updated_at") _client.UpdatedAt = field.NewField(tableName, "updated_at")
_client.DeletedAt = field.NewField(tableName, "deleted_at") _client.DeletedAt = field.NewField(tableName, "deleted_at")
_client.GrantPassword = field.NewBool(tableName, "grant_password")
_client.fillFieldMap() _client.fillFieldMap()
@@ -50,21 +51,22 @@ func newClient(db *gorm.DB, opts ...gen.DOOption) client {
type client struct { type client struct {
clientDo clientDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 // 客户端ID ID field.Int32 // 客户端ID
ClientID field.String // OAuth2客户端标识符 ClientID field.String // OAuth2客户端标识符
ClientSecret field.String // OAuth2客户端密钥 ClientSecret field.String // OAuth2客户端密钥
RedirectURI field.String // OAuth2 重定向URI RedirectURI field.String // OAuth2 重定向URI
GrantCode field.Bool // 允许授权码授予 GrantCode field.Bool // 允许授权码授予
GrantClient field.Bool // 允许客户端凭证授予 GrantClient field.Bool // 允许客户端凭证授予
GrantRefresh field.Bool // 允许刷新令牌授予 GrantRefresh field.Bool // 允许刷新令牌授予
Spec field.Int32 // 安全规范0-web1-native2-browser Spec field.Int32 // 安全规范0-web1-native2-browser
Name field.String // 名称 Name field.String // 名称
Icon field.String // 图标URL Icon field.String // 图标URL
Status field.Int32 // 状态1-正常0-禁用 Status field.Int32 // 状态1-正常0-禁用
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
GrantPassword field.Bool
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -95,6 +97,7 @@ func (c *client) updateTableName(table string) *client {
c.CreatedAt = field.NewField(table, "created_at") c.CreatedAt = field.NewField(table, "created_at")
c.UpdatedAt = field.NewField(table, "updated_at") c.UpdatedAt = field.NewField(table, "updated_at")
c.DeletedAt = field.NewField(table, "deleted_at") c.DeletedAt = field.NewField(table, "deleted_at")
c.GrantPassword = field.NewBool(table, "grant_password")
c.fillFieldMap() c.fillFieldMap()
@@ -111,7 +114,7 @@ func (c *client) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (c *client) fillFieldMap() { func (c *client) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 14) c.fieldMap = make(map[string]field.Expr, 15)
c.fieldMap["id"] = c.ID c.fieldMap["id"] = c.ID
c.fieldMap["client_id"] = c.ClientID c.fieldMap["client_id"] = c.ClientID
c.fieldMap["client_secret"] = c.ClientSecret c.fieldMap["client_secret"] = c.ClientSecret
@@ -126,6 +129,7 @@ func (c *client) fillFieldMap() {
c.fieldMap["created_at"] = c.CreatedAt c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["deleted_at"] = c.DeletedAt c.fieldMap["deleted_at"] = c.DeletedAt
c.fieldMap["grant_password"] = c.GrantPassword
} }
func (c client) clone(db *gorm.DB) client { func (c client) clone(db *gorm.DB) client {

View File

@@ -48,17 +48,17 @@ type coupon struct {
couponDo couponDo
ALL field.Asterisk ALL field.Asterisk
ExpireAt field.Time ExpireAt field.Time // 过期时间
CreatedAt field.Field CreatedAt field.Field // 创建时间
UpdatedAt field.Field UpdatedAt field.Field // 更新时间
DeletedAt field.Field DeletedAt field.Field // 删除时间
ID field.Int32 ID field.Int32 // 优惠券ID
UserID field.Int32 UserID field.Int32 // 用户ID
Status field.Int32 Status field.Int32 // 优惠券状态0-未使用1-已使用2-已过期
Code field.String Code field.String // 优惠券代码
Remark field.String Remark field.String // 优惠券备注
Amount field.Float64 Amount field.Float64 // 优惠券金额
MinAmount field.Float64 MinAmount field.Float64 // 最低消费金额
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }

View File

@@ -60,13 +60,13 @@ type node struct {
City field.String // 城市 City field.String // 城市
ProxyID field.Int32 // 代理ID ProxyID field.Int32 // 代理ID
ProxyPort field.Int32 // 代理端口 ProxyPort field.Int32 // 代理端口
Status field.Int32 // 节点状态:1-正常,0-离线 Status field.Int32 // 节点状态0-离线1-正常
Rtt field.Int32 // 延迟 Rtt field.Int32 // 延迟
Loss field.Int32 // 丢包率 Loss field.Int32 // 丢包率
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
Isp field.Int32 Isp field.Int32 // 运营商0-其他1-电信2-联通3-移动
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }

View File

@@ -52,9 +52,9 @@ type refund struct {
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
TradeID field.Int32 TradeID field.Int32 // 订单ID
Reason field.String Reason field.String // 退款原因
Status field.Int32 Status field.Int32 // 退款状态0-待处理1-已退款2-已拒绝
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }

View File

@@ -50,14 +50,14 @@ type resource struct {
resourceDo resourceDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 // 套餐ID ID field.Int32 // 套餐ID
UserID field.Int32 // 用户ID UserID field.Int32 // 用户ID
Active field.Bool // 套餐状态 Active field.Bool // 套餐状态
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
ResourceNo field.String ResourceNo field.String // 套餐编号
Type field.Int32 Type field.Int32 // 套餐类型1-动态2-隧道3-独享
Pss resourceHasOnePss Pss resourceHasOnePss
fieldMap map[string]field.Expr fieldMap map[string]field.Expr

View File

@@ -67,10 +67,10 @@ type trade struct {
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
Type field.Int32 Type field.Int32 // 订单类型0-充值余额1-购买产品
CancelAt field.Field CancelAt field.Field // 取消时间
PaidAt field.Field PaidAt field.Field // 支付时间
PayURL field.String PayURL field.String // 支付链接
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }

View File

@@ -50,7 +50,7 @@ type whitelist struct {
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
Remark field.String Remark field.String // 备注
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }

View File

@@ -12,7 +12,7 @@ func ApplyRouters(app *fiber.App) {
// 认证 // 认证
auth := api.Group("/auth") auth := api.Group("/auth")
auth.Post("/verify/sms", auth2.PermitDevice(), handlers.SmsCode) auth.Post("/verify/sms", handlers.SmsCode)
auth.Post("/login/sms", auth2.PermitDevice(), handlers.Login) auth.Post("/login/sms", auth2.PermitDevice(), handlers.Login)
auth.Post("/logout", handlers.Logout) auth.Post("/logout", handlers.Logout)
auth.Post("/token", handlers.Token) auth.Post("/token", handlers.Token)

View File

@@ -85,10 +85,19 @@ func (s *authService) OauthRefreshToken(ctx context.Context, client *models.Clie
return details, nil return details, nil
} }
type GrantType int type OauthGrantType string
const ( const (
GrantTypeAuthorizationCode GrantType = iota OauthGrantTypeAuthorizationCode = OauthGrantType("authorization_code")
GrantTypeClientCredentials OauthGrantTypeClientCredentials = OauthGrantType("client_credentials")
GrantTypeRefreshToken OauthGrantTypeRefreshToken = OauthGrantType("refresh_token")
OauthGrantTypePassword = OauthGrantType("password")
)
type OauthGrantLoginType string
const (
OauthGrantPasswordTypePassword = OauthGrantLoginType("password")
OauthGrantPasswordTypePhoneCode = OauthGrantLoginType("phone_code")
OauthGrantPasswordTypeEmailCode = OauthGrantLoginType("email_code")
) )

View File

@@ -239,6 +239,7 @@ func mergeConfig(defaultCfg SessionConfig, customCfg SessionConfig) SessionConfi
// AuthContext 定义认证信息 // AuthContext 定义认证信息
type AuthContext struct { type AuthContext struct {
Payload Payload `json:"payload"` Payload Payload `json:"payload"`
Agent Agent `json:"agent,omitempty"`
Permissions map[string]struct{} `json:"permissions,omitempty"` Permissions map[string]struct{} `json:"permissions,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"`
} }
@@ -265,6 +266,11 @@ const (
PayloadClientConfidential PayloadClientConfidential
) )
type Agent struct {
Id int32 `json:"id,omitempty"`
Addr string `json:"addr,omitempty"`
}
// AnyPermission 检查认证是否包含指定权限 // AnyPermission 检查认证是否包含指定权限
func (a *AuthContext) AnyPermission(requiredPermission ...string) bool { func (a *AuthContext) AnyPermission(requiredPermission ...string) bool {
if a == nil || a.Permissions == nil { if a == nil || a.Permissions == nil {