认证授权主要流程实现

This commit is contained in:
2025-03-18 17:57:07 +08:00
parent 19530d9d40
commit 6ddf1118a5
37 changed files with 2209 additions and 180 deletions

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
}

View File

@@ -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"` // 更新时间

View File

@@ -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
View 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-web1-native2-browser" json:"spec"` // 安全规范0-web1-native2-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
}

View 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
}

View File

@@ -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

View File

@@ -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"` // 删除时间

View File

@@ -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

View File

@@ -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
View 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-web1-native2-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
}

View 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
}

View File

@@ -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),

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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
}

View File

@@ -1,5 +0,0 @@
package utils
type ErrResp struct {
Cause string `json:"cause"`
}

View File

@@ -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))
}
}