Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fe415de63 | |||
| 8e42fad8aa | |||
| 7a3c47f1d4 | |||
| dfbb3a9acc | |||
| 19fa8b381c | |||
| b7a9682552 | |||
| f638baec64 | |||
| 1262a8dae4 |
@@ -2,7 +2,7 @@ name: Docker
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ["v*"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
@@ -10,14 +10,12 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -40,12 +40,13 @@ http 调用 clients 的初始化函数
|
|||||||
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
||||||
|
|
||||||
慢速请求底层调用埋点监控
|
慢速请求底层调用埋点监控
|
||||||
|
|
||||||
- redis
|
- redis
|
||||||
- gorm
|
- gorm
|
||||||
- 三方接口
|
- 三方接口
|
||||||
|
|
||||||
冷数据迁移方案
|
冷数据迁移方案
|
||||||
|
|
||||||
## 业务逻辑
|
## 业务逻辑
|
||||||
|
|
||||||
### 订单关闭的几种方式
|
### 订单关闭的几种方式
|
||||||
@@ -64,7 +65,8 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
|||||||
|
|
||||||
### 节点分配与存储逻辑
|
### 节点分配与存储逻辑
|
||||||
|
|
||||||
提取:
|
提取:
|
||||||
|
|
||||||
- 检查用户套餐与白名单
|
- 检查用户套餐与白名单
|
||||||
- 选中代理
|
- 选中代理
|
||||||
- 找到当前可用端口最多的代理
|
- 找到当前可用端口最多的代理
|
||||||
@@ -76,6 +78,7 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
|||||||
- 分别提交连接与配置请求
|
- 分别提交连接与配置请求
|
||||||
|
|
||||||
释放:
|
释放:
|
||||||
|
|
||||||
- 根据批次查出所有端口与相关节点
|
- 根据批次查出所有端口与相关节点
|
||||||
- 分别提交断开与关闭请求
|
- 分别提交断开与关闭请求
|
||||||
- 释放端口
|
- 释放端口
|
||||||
|
|||||||
16
publish.ps1
Normal file
16
publish.ps1
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
if (-not $args) {
|
||||||
|
Write-Error "需要指定版本号"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$confrim = Read-Host "构建版本为 [platform:$($args[0])],是否继续?(y/n)"
|
||||||
|
if ($confrim -ne "y") {
|
||||||
|
Write-Host "已取消构建"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
docker build -t 43.226.58.254:53000/lanhu/platform:latest .
|
||||||
|
docker build -t 43.226.58.254:53000/lanhu/platform:$($args[0]) .
|
||||||
|
|
||||||
|
docker push 43.226.58.254:53000/lanhu/platform:latest
|
||||||
|
docker push 43.226.58.254:53000/lanhu/platform:$($args[0])
|
||||||
156
web/auth/account.go
Normal file
156
web/auth/account.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
"platform/pkg/u"
|
||||||
|
"platform/web/core"
|
||||||
|
m "platform/web/models"
|
||||||
|
q "platform/web/queries"
|
||||||
|
s "platform/web/services"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func authClient(clientId string, clientSecrets ...string) (*m.Client, error) {
|
||||||
|
|
||||||
|
// 获取客户端信息
|
||||||
|
client, err := q.Client.
|
||||||
|
Where(
|
||||||
|
q.Client.ClientID.Eq(clientId),
|
||||||
|
q.Client.Status.Eq(1)).
|
||||||
|
Take()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查客户端密钥
|
||||||
|
if client.Spec == m.ClientSpecWeb || client.Spec == m.ClientSpecAPI {
|
||||||
|
if len(clientSecrets) == 0 {
|
||||||
|
return nil, errors.New("客户端密钥错误")
|
||||||
|
}
|
||||||
|
clientSecret := clientSecrets[0]
|
||||||
|
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
|
||||||
|
return nil, errors.New("客户端密钥错误")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo 查询客户端关联权限
|
||||||
|
|
||||||
|
// 组织授权信息(一次性请求)
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func authUser(loginType PwdLoginType, username, password string) (user *m.User, err error) {
|
||||||
|
switch loginType {
|
||||||
|
case PwdLoginByPhone:
|
||||||
|
user, err = authUserBySms(q.Q, username, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
user = &m.User{
|
||||||
|
Phone: username,
|
||||||
|
Username: u.P(username),
|
||||||
|
Status: m.UserStatusEnabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PwdLoginByEmail:
|
||||||
|
user, err = authUserByEmail(q.Q, username, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case PwdLoginByPassword:
|
||||||
|
user, err = authUserByPassword(q.Q, username, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, ErrAuthorizeInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// 账户状态
|
||||||
|
if user.Status == m.UserStatusDisabled {
|
||||||
|
return nil, core.NewBizErr("账号已禁用")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func authUserBySms(tx *q.Query, username, code string) (*m.User, error) {
|
||||||
|
// 验证验证码
|
||||||
|
err := s.Verifier.VerifySms(context.Background(), username, code)
|
||||||
|
if err != nil {
|
||||||
|
return nil, core.NewBizErr("短信认证失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找用户
|
||||||
|
return tx.User.Where(tx.User.Phone.Eq(username)).Take()
|
||||||
|
}
|
||||||
|
|
||||||
|
func authUserByEmail(tx *q.Query, username, code string) (*m.User, error) {
|
||||||
|
return nil, core.NewServErr("邮箱登录不可用")
|
||||||
|
}
|
||||||
|
|
||||||
|
func authUserByPassword(tx *q.Query, username, password string) (*m.User, error) {
|
||||||
|
user, err := tx.User.
|
||||||
|
Where(tx.User.Phone.Eq(username)).
|
||||||
|
Or(tx.User.Email.Eq(username)).
|
||||||
|
Or(tx.User.Username.Eq(username)).
|
||||||
|
Take()
|
||||||
|
if err != nil {
|
||||||
|
slog.Debug("查找用户失败", "error", err)
|
||||||
|
return nil, core.NewBizErr("用户不存在或密码错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
if user.Password == nil || *user.Password == "" {
|
||||||
|
slog.Debug("用户未设置密码", "username", username)
|
||||||
|
return nil, core.NewBizErr("用户不存在或密码错误")
|
||||||
|
}
|
||||||
|
if bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)) != nil {
|
||||||
|
slog.Debug("密码验证失败", "username", username)
|
||||||
|
return nil, core.NewBizErr("用户不存在或密码错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func authAdmin(loginType PwdLoginType, username, password string) (admin *m.Admin, err error) {
|
||||||
|
switch loginType {
|
||||||
|
case PwdLoginByPhone, PwdLoginByEmail:
|
||||||
|
return nil, core.NewServErr("不支持的登录方式:" + string(loginType))
|
||||||
|
case PwdLoginByPassword:
|
||||||
|
admin, err = authAdminByPassword(q.Q, username, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, ErrAuthorizeInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// 账户状态
|
||||||
|
if admin.Status == m.AdminStatusDisabled {
|
||||||
|
return nil, core.NewBizErr("账号已禁用")
|
||||||
|
}
|
||||||
|
|
||||||
|
return admin, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func authAdminByPassword(tx *q.Query, username, password string) (*m.Admin, error) {
|
||||||
|
admin, err := tx.Admin.Where(tx.Admin.Username.Eq(username)).Take()
|
||||||
|
if err != nil {
|
||||||
|
return nil, core.NewBizErr("账号不存在或密码错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
if admin.Password == "" {
|
||||||
|
return nil, core.NewBizErr("账号不存在或密码错误")
|
||||||
|
}
|
||||||
|
if bcrypt.CompareHashAndPassword([]byte(admin.Password), []byte(password)) != nil {
|
||||||
|
return nil, core.NewBizErr("账号不存在或密码错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
return admin, nil
|
||||||
|
}
|
||||||
@@ -6,10 +6,8 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log/slog"
|
|
||||||
"platform/pkg/env"
|
"platform/pkg/env"
|
||||||
"platform/pkg/u"
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
"platform/web/globals/orm"
|
"platform/web/globals/orm"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
@@ -22,67 +20,52 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GrantType string
|
// AuthorizeGet 授权端点
|
||||||
|
func AuthorizeGet(ctx *fiber.Ctx) error {
|
||||||
|
|
||||||
const (
|
// 检查请求
|
||||||
GrantAuthorizationCode = GrantType("authorization_code") // 授权码模式
|
req := new(AuthorizeGetReq)
|
||||||
GrantClientCredentials = GrantType("client_credentials") // 客户端凭证模式
|
if err := g.Validator.ParseQuery(ctx, req); err != nil {
|
||||||
GrantRefreshToken = GrantType("refresh_token") // 刷新令牌模式
|
return err
|
||||||
GrantPassword = GrantType("password") // 密码模式(私有扩展)
|
}
|
||||||
)
|
|
||||||
|
|
||||||
type PasswordGrantType string
|
// 检查客户端
|
||||||
|
client, err := authClient(req.ClientID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
if client.RedirectURI == nil || *client.RedirectURI != req.RedirectURI {
|
||||||
GrantPasswordSecret = PasswordGrantType("password") // 账号密码
|
return errors.New("客户端重定向URI错误")
|
||||||
GrantPasswordPhone = PasswordGrantType("phone_code") // 手机验证码
|
}
|
||||||
GrantPasswordEmail = PasswordGrantType("email_code") // 邮箱验证码
|
|
||||||
)
|
|
||||||
|
|
||||||
type TokenReq struct {
|
// todo 检查 scope
|
||||||
GrantType GrantType `json:"grant_type" form:"grant_type"`
|
|
||||||
ClientID string `json:"client_id" form:"client_id"`
|
// 授权确认页面
|
||||||
ClientSecret string `json:"client_secret" form:"client_secret"`
|
return nil
|
||||||
Scope string `json:"scope" form:"scope"`
|
|
||||||
GrantCodeData
|
|
||||||
GrantClientData
|
|
||||||
GrantRefreshData
|
|
||||||
GrantPasswordData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GrantCodeData struct {
|
type AuthorizeGetReq struct {
|
||||||
Code string `json:"code" form:"code"`
|
ResponseType string `json:"response_type" validate:"eq=code"`
|
||||||
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
|
ClientID string `json:"client_id" validate:"required"`
|
||||||
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
|
RedirectURI string `json:"redirect_uri" validate:"required"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
State string `json:"state"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GrantClientData struct {
|
func AuthorizePost(ctx *fiber.Ctx) error {
|
||||||
|
|
||||||
|
// todo 解析用户授权的范围
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type GrantRefreshData struct {
|
type AuthorizePostReq struct {
|
||||||
RefreshToken string `json:"refresh_token" form:"refresh_token"`
|
Accept bool `json:"accept"`
|
||||||
}
|
Scope string `json:"scope"`
|
||||||
|
|
||||||
type GrantPasswordData struct {
|
|
||||||
LoginType PasswordGrantType `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 {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
RefreshToken string `json:"refresh_token,omitempty"`
|
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
TokenType string `json:"token_type"`
|
|
||||||
Scope string `json:"scope,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TokenErrResp struct {
|
|
||||||
Error string `json:"error"`
|
|
||||||
Description string `json:"error_description,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Token 令牌端点
|
||||||
func Token(c *fiber.Ctx) error {
|
func Token(c *fiber.Ctx) error {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@@ -165,6 +148,75 @@ func Token(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TokenReq struct {
|
||||||
|
GrantType GrantType `json:"grant_type" form:"grant_type"`
|
||||||
|
ClientID string `json:"client_id" form:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret" form:"client_secret"`
|
||||||
|
Scope string `json:"scope" form:"scope"`
|
||||||
|
GrantCodeData
|
||||||
|
GrantClientData
|
||||||
|
GrantRefreshData
|
||||||
|
GrantPasswordData
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantCodeData struct {
|
||||||
|
Code string `json:"code" form:"code"`
|
||||||
|
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
|
||||||
|
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantClientData struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantRefreshData struct {
|
||||||
|
RefreshToken string `json:"refresh_token" form:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantPasswordData struct {
|
||||||
|
LoginType PwdLoginType `json:"login_type" form:"login_type"`
|
||||||
|
LoginPool PwdLoginPool `json:"login_pool" form:"login_pool"`
|
||||||
|
Username string `json:"username" form:"username"`
|
||||||
|
Password string `json:"password" form:"password"`
|
||||||
|
Remember bool `json:"remember" form:"remember"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
GrantAuthorizationCode = GrantType("authorization_code") // 授权码模式
|
||||||
|
GrantClientCredentials = GrantType("client_credentials") // 客户端凭证模式
|
||||||
|
GrantRefreshToken = GrantType("refresh_token") // 刷新令牌模式
|
||||||
|
GrantPassword = GrantType("password") // 密码模式(私有扩展)
|
||||||
|
)
|
||||||
|
|
||||||
|
type PwdLoginType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PwdLoginByPassword = PwdLoginType("password") // 账号密码
|
||||||
|
PwdLoginByPhone = PwdLoginType("phone_code") // 手机验证码
|
||||||
|
PwdLoginByEmail = PwdLoginType("email_code") // 邮箱验证码
|
||||||
|
)
|
||||||
|
|
||||||
|
type PwdLoginPool string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PwdLoginAsUser = PwdLoginPool("user") // 用户池
|
||||||
|
PwdLoginAsAdmin = PwdLoginPool("admin") // 管理员池
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenResp struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token,omitempty"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenErrResp struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
Description string `json:"error_description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m.Session, error) {
|
func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m.Session, error) {
|
||||||
|
|
||||||
// 检查 code 获取用户授权信息
|
// 检查 code 获取用户授权信息
|
||||||
@@ -226,7 +278,7 @@ func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.
|
|||||||
session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
|
session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = SaveSession(session)
|
err = SaveSession(q.Q, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -249,7 +301,7 @@ func authClientCredential(c *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.Tim
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存会话
|
// 保存会话
|
||||||
err := SaveSession(session)
|
err := SaveSession(q.Q, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -261,71 +313,85 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
|
|||||||
ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常
|
ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常
|
||||||
ua := u.X(c.Get(fiber.HeaderUserAgent))
|
ua := u.X(c.Get(fiber.HeaderUserAgent))
|
||||||
|
|
||||||
|
// 分池认证
|
||||||
|
var err error
|
||||||
var user *m.User
|
var user *m.User
|
||||||
err := q.Q.Transaction(func(tx *q.Query) (err error) {
|
var admin *m.Admin
|
||||||
switch req.LoginType {
|
|
||||||
case GrantPasswordPhone:
|
|
||||||
user, err = authUserBySms(tx, req.Username, req.Password)
|
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if user == nil {
|
|
||||||
user = &m.User{
|
|
||||||
Phone: req.Username,
|
|
||||||
Username: u.P(req.Username),
|
|
||||||
Status: m.UserStatusEnabled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case GrantPasswordEmail:
|
|
||||||
user, err = authUserByEmail(tx, req.Username, req.Password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case GrantPasswordSecret:
|
|
||||||
user, err = authUserByPassword(tx, req.Username, req.Password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ErrAuthorizeInvalidRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// 账户状态
|
pool := req.LoginPool
|
||||||
if user.Status == m.UserStatusDisabled {
|
if pool == "" {
|
||||||
slog.Debug("账户状态异常", "username", req.Username, "status", user.Status)
|
pool = PwdLoginAsUser
|
||||||
return core.NewBizErr("账号无法登录")
|
}
|
||||||
|
switch pool {
|
||||||
|
case PwdLoginAsUser:
|
||||||
|
user, err = authUser(req.LoginType, req.Username, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
if req.LoginType != PwdLoginByPhone || !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手机号首次登录的自动创建用户
|
||||||
|
user = &m.User{
|
||||||
|
Phone: req.Username,
|
||||||
|
Username: u.P(req.Username),
|
||||||
|
Status: m.UserStatusEnabled,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户的登录时间
|
// 更新用户的登录时间
|
||||||
user.LastLogin = u.P(time.Now())
|
user.LastLogin = u.P(time.Now())
|
||||||
user.LastLoginIP = ip
|
user.LastLoginIP = ip
|
||||||
user.LastLoginUA = ua
|
user.LastLoginUA = ua
|
||||||
if err := tx.User.Save(user); err != nil {
|
|
||||||
return err
|
case PwdLoginAsAdmin:
|
||||||
|
admin, err = authAdmin(req.LoginType, req.Username, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// 更新管理员登录时间
|
||||||
})
|
admin.LastLogin = u.P(time.Now())
|
||||||
if err != nil {
|
admin.LastLoginIP = ip
|
||||||
return nil, err
|
admin.LastLoginUA = ua
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, ErrAuthorizeInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成会话
|
// 生成会话
|
||||||
session := &m.Session{
|
session := &m.Session{
|
||||||
IP: ip,
|
IP: ip,
|
||||||
UA: ua,
|
UA: ua,
|
||||||
UserID: &user.ID,
|
|
||||||
ClientID: &auth.Client.ID,
|
ClientID: &auth.Client.ID,
|
||||||
Scopes: u.X(req.Scope),
|
Scopes: u.X(req.Scope),
|
||||||
AccessToken: uuid.NewString(),
|
AccessToken: uuid.NewString(),
|
||||||
AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
|
AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Remember {
|
if req.Remember {
|
||||||
session.RefreshToken = u.P(uuid.NewString())
|
session.RefreshToken = u.P(uuid.NewString())
|
||||||
session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
|
session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = SaveSession(session)
|
// 保存用户更新和会话
|
||||||
|
err = q.Q.Transaction(func(tx *q.Query) error {
|
||||||
|
if user != nil {
|
||||||
|
if err := tx.User.Save(user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session.UserID = &user.ID
|
||||||
|
}
|
||||||
|
if admin != nil {
|
||||||
|
if err := tx.Admin.Save(admin); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session.AdminID = &admin.ID
|
||||||
|
}
|
||||||
|
if err := SaveSession(tx, session); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -353,7 +419,7 @@ func authRefreshToken(_ *fiber.Ctx, _ *AuthCtx, req *TokenReq, now time.Time) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存令牌
|
// 保存令牌
|
||||||
err = SaveSession(session)
|
err = SaveSession(q.Q, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -394,12 +460,117 @@ func sendError(c *fiber.Ctx, err error, description ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Revoke() error {
|
// Revoke 令牌撤销端点
|
||||||
|
func Revoke(ctx *fiber.Ctx) error {
|
||||||
|
_, err := GetAuthCtx(ctx).PermitUser()
|
||||||
|
if err != nil {
|
||||||
|
// 用户未登录
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(RevokeReq)
|
||||||
|
if err := ctx.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除会话
|
||||||
|
err = RemoveSession(ctx.Context(), req.AccessToken, req.RefreshToken)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Introspect() error {
|
type RevokeReq struct {
|
||||||
return nil
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Introspect 令牌检查端点
|
||||||
|
func Introspect(ctx *fiber.Ctx) error {
|
||||||
|
authCtx := GetAuthCtx(ctx)
|
||||||
|
|
||||||
|
// 尝试验证用户权限
|
||||||
|
if _, err := authCtx.PermitUser(); err == nil {
|
||||||
|
return introspectUser(ctx, authCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试验证管理员权限
|
||||||
|
if _, err := authCtx.PermitAdmin(); err == nil {
|
||||||
|
return introspectAdmin(ctx, authCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrAuthenticateForbidden
|
||||||
|
}
|
||||||
|
|
||||||
|
// introspectUser 获取并返回用户信息
|
||||||
|
func introspectUser(ctx *fiber.Ctx, authCtx *AuthCtx) error {
|
||||||
|
// 获取用户信息
|
||||||
|
profile, err := q.User.
|
||||||
|
Where(q.User.ID.Eq(authCtx.User.ID)).
|
||||||
|
Omit(q.User.DeletedAt).
|
||||||
|
Take()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户是否设置了密码
|
||||||
|
hasPassword := false
|
||||||
|
if profile.Password != nil && *profile.Password != "" {
|
||||||
|
hasPassword = true
|
||||||
|
profile.Password = nil // 不返回密码
|
||||||
|
}
|
||||||
|
|
||||||
|
// 掩码敏感信息
|
||||||
|
if profile.Phone != "" {
|
||||||
|
profile.Phone = maskPhone(profile.Phone)
|
||||||
|
}
|
||||||
|
if profile.IDNo != nil && *profile.IDNo != "" {
|
||||||
|
profile.IDNo = u.P(maskIdNo(*profile.IDNo))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(struct {
|
||||||
|
m.User
|
||||||
|
HasPassword bool `json:"has_password"` // 是否设置了密码
|
||||||
|
}{*profile, hasPassword})
|
||||||
|
}
|
||||||
|
|
||||||
|
// introspectAdmin 获取并返回管理员信息
|
||||||
|
func introspectAdmin(ctx *fiber.Ctx, authCtx *AuthCtx) error {
|
||||||
|
// 获取管理员信息
|
||||||
|
profile, err := q.Admin.
|
||||||
|
Where(q.Admin.ID.Eq(authCtx.Admin.ID)).
|
||||||
|
Omit(q.Admin.DeletedAt).
|
||||||
|
Take()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不返回密码
|
||||||
|
profile.Password = ""
|
||||||
|
|
||||||
|
// 掩码敏感信息
|
||||||
|
if profile.Phone != nil && *profile.Phone != "" {
|
||||||
|
profile.Phone = u.P(maskPhone(*profile.Phone))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskPhone(phone string) string {
|
||||||
|
if len(phone) < 11 {
|
||||||
|
return phone
|
||||||
|
}
|
||||||
|
return phone[:3] + "****" + phone[7:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskIdNo(idNo string) string {
|
||||||
|
if len(idNo) < 18 {
|
||||||
|
return idNo
|
||||||
|
}
|
||||||
|
return idNo[:3] + "*********" + idNo[14:]
|
||||||
}
|
}
|
||||||
|
|
||||||
type CodeContext struct {
|
type CodeContext struct {
|
||||||
@@ -6,15 +6,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"platform/web/core"
|
|
||||||
m "platform/web/models"
|
|
||||||
q "platform/web/queries"
|
|
||||||
s "platform/web/services"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Authenticate() fiber.Handler {
|
func Authenticate() fiber.Handler {
|
||||||
@@ -123,67 +118,3 @@ func authBasic(_ context.Context, token string) (*AuthCtx, error) {
|
|||||||
Scopes: []string{},
|
Scopes: []string{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func authClient(clientId, clientSecret string) (*m.Client, error) {
|
|
||||||
|
|
||||||
// 获取客户端信息
|
|
||||||
client, err := q.Client.
|
|
||||||
Where(
|
|
||||||
q.Client.ClientID.Eq(clientId),
|
|
||||||
q.Client.Status.Eq(1)).
|
|
||||||
Take()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查客户端密钥
|
|
||||||
if client.Spec == m.ClientSpecWeb || client.Spec == m.ClientSpecAPI {
|
|
||||||
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
|
|
||||||
return nil, errors.New("客户端密钥错误")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo 查询客户端关联权限
|
|
||||||
|
|
||||||
// 组织授权信息(一次性请求)
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func authUserBySms(tx *q.Query, username, code string) (*m.User, error) {
|
|
||||||
// 验证验证码
|
|
||||||
err := s.Verifier.VerifySms(context.Background(), username, code)
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewBizErr("短信认证失败:%w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找用户
|
|
||||||
return tx.User.Where(tx.User.Phone.Eq(username)).Take()
|
|
||||||
}
|
|
||||||
|
|
||||||
func authUserByEmail(tx *q.Query, username, code string) (*m.User, error) {
|
|
||||||
return nil, core.NewServErr("邮箱登录不可用")
|
|
||||||
}
|
|
||||||
|
|
||||||
func authUserByPassword(tx *q.Query, username, password string) (*m.User, error) {
|
|
||||||
user, err := tx.User.
|
|
||||||
Where(tx.User.Phone.Eq(username)).
|
|
||||||
Or(tx.User.Email.Eq(username)).
|
|
||||||
Or(tx.User.Username.Eq(username)).
|
|
||||||
Take()
|
|
||||||
if err != nil {
|
|
||||||
slog.Debug("查找用户失败", "error", err)
|
|
||||||
return nil, core.NewBizErr("用户不存在或密码错误")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证密码
|
|
||||||
if user.Password == nil || *user.Password == "" {
|
|
||||||
slog.Debug("用户未设置密码", "username", username)
|
|
||||||
return nil, core.NewBizErr("用户不存在或密码错误")
|
|
||||||
}
|
|
||||||
if bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)) != nil {
|
|
||||||
slog.Debug("密码验证失败", "username", username)
|
|
||||||
return nil, core.NewBizErr("用户不存在或密码错误")
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,8 @@ func FindSessionByRefresh(refreshToken string, now time.Time) (*m.Session, error
|
|||||||
).First()
|
).First()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveSession(session *m.Session) error {
|
func SaveSession(tx *q.Query, session *m.Session) error {
|
||||||
return q.Session.Save(session)
|
return tx.Session.Save(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveSession(ctx context.Context, accessToken string, refreshToken string) error {
|
func RemoveSession(ctx context.Context, accessToken string, refreshToken string) error {
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"platform/pkg/u"
|
|
||||||
auth2 "platform/web/auth"
|
|
||||||
m "platform/web/models"
|
|
||||||
q "platform/web/queries"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// region /revoke
|
|
||||||
|
|
||||||
type RevokeReq struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func Revoke(c *fiber.Ctx) error {
|
|
||||||
_, err := auth2.GetAuthCtx(c).PermitUser()
|
|
||||||
if err != nil {
|
|
||||||
// 用户未登录
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析请求参数
|
|
||||||
req := new(RevokeReq)
|
|
||||||
if err := c.BodyParser(req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除会话
|
|
||||||
err = auth2.RemoveSession(c.Context(), req.AccessToken, req.RefreshToken)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region /profile
|
|
||||||
|
|
||||||
type IntrospectResp struct {
|
|
||||||
m.User
|
|
||||||
HasPassword bool `json:"has_password"` // 是否设置了密码
|
|
||||||
}
|
|
||||||
|
|
||||||
func Introspect(c *fiber.Ctx) error {
|
|
||||||
// 验证权限
|
|
||||||
authCtx, err := auth2.GetAuthCtx(c).PermitUser()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取用户信息
|
|
||||||
profile, err := q.User.
|
|
||||||
Where(q.User.ID.Eq(authCtx.User.ID)).
|
|
||||||
Omit(q.User.DeletedAt).
|
|
||||||
Take()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查用户是否设置了密码
|
|
||||||
hasPassword := false
|
|
||||||
if profile.Password != nil && *profile.Password != "" {
|
|
||||||
hasPassword = true
|
|
||||||
profile.Password = nil // 不返回密码
|
|
||||||
}
|
|
||||||
|
|
||||||
// 掩码敏感信息
|
|
||||||
if profile.Phone != "" {
|
|
||||||
profile.Phone = maskPhone(profile.Phone)
|
|
||||||
}
|
|
||||||
if profile.IDNo != nil && *profile.IDNo != "" {
|
|
||||||
profile.IDNo = u.P(maskIdNo(*profile.IDNo))
|
|
||||||
}
|
|
||||||
return c.JSON(IntrospectResp{*profile, hasPassword})
|
|
||||||
}
|
|
||||||
|
|
||||||
func maskPhone(phone string) string {
|
|
||||||
if len(phone) < 11 {
|
|
||||||
return phone
|
|
||||||
}
|
|
||||||
return phone[:3] + "****" + phone[7:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func maskIdNo(idNo string) string {
|
|
||||||
if len(idNo) < 18 {
|
|
||||||
return idNo
|
|
||||||
}
|
|
||||||
return idNo[:3] + "*********" + idNo[14:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
@@ -55,3 +55,25 @@ type PageResourceBatchReq struct {
|
|||||||
TimeStart *time.Time `json:"time_start"`
|
TimeStart *time.Time `json:"time_start"`
|
||||||
TimeEnd *time.Time `json:"time_end"`
|
TimeEnd *time.Time `json:"time_end"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageBatchByAdmin 分页查询所有提取记录
|
||||||
|
func PageBatchByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(struct{ core.PageReq })
|
||||||
|
if err = g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := q.LogsUserUsage.FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,20 +3,40 @@ package handlers
|
|||||||
import (
|
import (
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
|
g "platform/web/globals"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// region ListBill
|
// PageBillByAdmin 分页查询全部账单
|
||||||
|
func PageBillByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
type ListBillReq struct {
|
// 解析请求参数
|
||||||
core.PageReq
|
req := new(core.PageReq)
|
||||||
BillNo *string `json:"bill_no"`
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
Type *int `json:"type"`
|
return err
|
||||||
CreateAfter *time.Time `json:"create_after"`
|
}
|
||||||
CreateBefore *time.Time `json:"create_before"`
|
|
||||||
|
// 查询用户列表
|
||||||
|
list, total, err := q.Bill.FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListBill 获取账单列表
|
// ListBill 获取账单列表
|
||||||
@@ -79,4 +99,10 @@ func ListBill(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
type ListBillReq struct {
|
||||||
|
core.PageReq
|
||||||
|
BillNo *string `json:"bill_no"`
|
||||||
|
Type *int `json:"type"`
|
||||||
|
CreateAfter *time.Time `json:"create_after"`
|
||||||
|
CreateBefore *time.Time `json:"create_before"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,15 +14,36 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// region ListChannels
|
// PageChannelsByAdmin 分页查询所有通道
|
||||||
|
func PageChannelsByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
type ListChannelsReq struct {
|
// 解析请求参数
|
||||||
core.PageReq
|
req := new(core.PageReq)
|
||||||
AuthType s.ChannelAuthType `json:"auth_type"`
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
ExpireAfter *time.Time `json:"expire_after"`
|
return err
|
||||||
ExpireBefore *time.Time `json:"expire_before"`
|
}
|
||||||
|
|
||||||
|
// 查询通道列表
|
||||||
|
list, total, err := q.Channel.FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分页查询当前用户通道
|
||||||
func ListChannels(c *fiber.Ctx) error {
|
func ListChannels(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authContext, err := auth.GetAuthCtx(c).PermitUser()
|
authContext, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -86,28 +107,14 @@ func ListChannels(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
type ListChannelsReq struct {
|
||||||
|
core.PageReq
|
||||||
// region CreateChannel
|
AuthType s.ChannelAuthType `json:"auth_type"`
|
||||||
|
ExpireAfter *time.Time `json:"expire_after"`
|
||||||
type CreateChannelReq struct {
|
ExpireBefore *time.Time `json:"expire_before"`
|
||||||
ResourceId int32 `json:"resource_id" validate:"required"`
|
|
||||||
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
|
|
||||||
Protocol int `json:"protocol" validate:"required"`
|
|
||||||
Count int `json:"count" validate:"required"`
|
|
||||||
Prov *string `json:"prov"`
|
|
||||||
City *string `json:"city"`
|
|
||||||
Isp *int `json:"isp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateChannelRespItem struct {
|
|
||||||
Proto int `json:"-"`
|
|
||||||
Host string `json:"host"`
|
|
||||||
Port uint16 `json:"port"`
|
|
||||||
Username *string `json:"username,omitempty"`
|
|
||||||
Password *string `json:"password,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建新通道
|
||||||
func CreateChannel(c *fiber.Ctx) error {
|
func CreateChannel(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// 解析参数
|
// 解析参数
|
||||||
@@ -154,16 +161,25 @@ func CreateChannel(c *fiber.Ctx) error {
|
|||||||
return c.JSON(resp)
|
return c.JSON(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateChannelResultType string
|
type CreateChannelReq struct {
|
||||||
|
ResourceId int32 `json:"resource_id" validate:"required"`
|
||||||
// endregion
|
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
|
||||||
|
Protocol int `json:"protocol" validate:"required"`
|
||||||
// region RemoveChannels
|
Count int `json:"count" validate:"required"`
|
||||||
|
Prov *string `json:"prov"`
|
||||||
type RemoveChannelsReq struct {
|
City *string `json:"city"`
|
||||||
Batch string `json:"batch" validate:"required"`
|
Isp *int `json:"isp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateChannelRespItem struct {
|
||||||
|
Proto int `json:"-"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port uint16 `json:"port"`
|
||||||
|
Username *string `json:"username,omitempty"`
|
||||||
|
Password *string `json:"password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveChannels 删除通道
|
||||||
func RemoveChannels(c *fiber.Ctx) error {
|
func RemoveChannels(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
|
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
|
||||||
@@ -186,4 +202,6 @@ func RemoveChannels(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
type RemoveChannelsReq struct {
|
||||||
|
Batch string `json:"batch" validate:"required"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListResourceShort 分页短效套餐
|
// PageResourceShort 分页查询当前用户短效套餐
|
||||||
func ListResourceShort(c *fiber.Ctx) error {
|
func PageResourceShort(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -24,7 +24,7 @@ func ListResourceShort(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析请求参数
|
// 解析请求参数
|
||||||
req := new(ListResourceShortReq)
|
req := new(PageResourceShortReq)
|
||||||
if err := c.BodyParser(req); err != nil {
|
if err := c.BodyParser(req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ func ListResourceShort(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListResourceShortReq struct {
|
type PageResourceShortReq struct {
|
||||||
core.PageReq
|
core.PageReq
|
||||||
ResourceNo *string `json:"resource_no"`
|
ResourceNo *string `json:"resource_no"`
|
||||||
Active *bool `json:"active"`
|
Active *bool `json:"active"`
|
||||||
@@ -97,8 +97,8 @@ type ListResourceShortReq struct {
|
|||||||
ExpireBefore *time.Time `json:"expire_before"`
|
ExpireBefore *time.Time `json:"expire_before"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListResourceLong 分页长效套餐
|
// PageResourceLong 分页查询当前用户长效套餐
|
||||||
func ListResourceLong(c *fiber.Ctx) error {
|
func PageResourceLong(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -106,7 +106,7 @@ func ListResourceLong(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析请求参数
|
// 解析请求参数
|
||||||
req := new(ListResourceLongReq)
|
req := new(PageResourceLongReq)
|
||||||
if err := c.BodyParser(req); err != nil {
|
if err := c.BodyParser(req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ func ListResourceLong(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListResourceLongReq struct {
|
type PageResourceLongReq struct {
|
||||||
core.PageReq
|
core.PageReq
|
||||||
ResourceNo *string `json:"resource_no"`
|
ResourceNo *string `json:"resource_no"`
|
||||||
Active *bool `json:"active"`
|
Active *bool `json:"active"`
|
||||||
@@ -179,6 +179,56 @@ type ListResourceLongReq struct {
|
|||||||
ExpireBefore *time.Time `json:"expire_before"`
|
ExpireBefore *time.Time `json:"expire_before"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageResourceShortByAdmin 分页查询全部短效套餐
|
||||||
|
func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(struct{ core.PageReq })
|
||||||
|
if err = g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := q.Resource.
|
||||||
|
LeftJoin(q.ResourceShort, q.ResourceShort.ResourceID.EqCol(q.Resource.ID)).
|
||||||
|
Where(q.Resource.Type.Eq(int(m.ResourceTypeShort))).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageResourceLongByAdmin 分页查询全部短效套餐
|
||||||
|
func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(struct{ core.PageReq })
|
||||||
|
if err = g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := q.Resource.
|
||||||
|
LeftJoin(q.ResourceLong, q.ResourceLong.ResourceID.EqCol(q.Resource.ID)).
|
||||||
|
Where(q.Resource.Type.Eq(int(m.ResourceTypeLong))).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// AllActiveResource 所有可用套餐
|
// AllActiveResource 所有可用套餐
|
||||||
func AllActiveResource(c *fiber.Ctx) error {
|
func AllActiveResource(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
|
q "platform/web/queries"
|
||||||
s "platform/web/services"
|
s "platform/web/services"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -16,18 +17,36 @@ import (
|
|||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TradeCreateReq struct {
|
// PageTradeByAdmin 分页查询所有订单
|
||||||
s.CreateTradeData
|
func PageTradeByAdmin(c *fiber.Ctx) error {
|
||||||
Type m.TradeType `json:"type" validate:"required"`
|
// 检查权限
|
||||||
Resource *s.CreateResourceData `json:"resource,omitempty"`
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"`
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
type TradeCreateResp struct {
|
|
||||||
PayUrl string `json:"pay_url"`
|
// 解析请求参数
|
||||||
TradeNo string `json:"trade_no"`
|
req := new(core.PageReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户列表
|
||||||
|
list, total, err := q.Trade.FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建订单
|
||||||
func TradeCreate(c *fiber.Ctx) error {
|
func TradeCreate(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -67,10 +86,19 @@ func TradeCreate(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type TradeCompleteReq struct {
|
type TradeCreateReq struct {
|
||||||
s.ModifyTradeData
|
s.CreateTradeData
|
||||||
|
Type m.TradeType `json:"type" validate:"required"`
|
||||||
|
Resource *s.CreateResourceData `json:"resource,omitempty"`
|
||||||
|
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TradeCreateResp struct {
|
||||||
|
PayUrl string `json:"pay_url"`
|
||||||
|
TradeNo string `json:"trade_no"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完成订单
|
||||||
func TradeComplete(c *fiber.Ctx) error {
|
func TradeComplete(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
_, err := auth.GetAuthCtx(c).PermitUser()
|
_, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -93,10 +121,11 @@ func TradeComplete(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TradeCancelReq struct {
|
type TradeCompleteReq struct {
|
||||||
s.ModifyTradeData
|
s.ModifyTradeData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消订单
|
||||||
func TradeCancel(c *fiber.Ctx) error {
|
func TradeCancel(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
_, err := auth.GetAuthCtx(c).PermitUser()
|
_, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -120,10 +149,11 @@ func TradeCancel(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TradeCheckReq struct {
|
type TradeCancelReq struct {
|
||||||
s.ModifyTradeData
|
s.ModifyTradeData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查订单
|
||||||
func TradeCheck(c *fiber.Ctx) error {
|
func TradeCheck(c *fiber.Ctx) error {
|
||||||
// 解析请求参数
|
// 解析请求参数
|
||||||
req := new(TradeCheckReq)
|
req := new(TradeCheckReq)
|
||||||
@@ -170,3 +200,7 @@ func TradeCheck(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TradeCheckReq struct {
|
||||||
|
s.ModifyTradeData
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
|
"platform/web/core"
|
||||||
|
g "platform/web/globals"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
s "platform/web/services"
|
s "platform/web/services"
|
||||||
@@ -10,15 +12,81 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// region /update
|
// 分页获取用户
|
||||||
|
func PageUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateUserReq struct {
|
// 解析请求参数
|
||||||
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
req := new(core.PageReq)
|
||||||
Email string `json:"email" validate:"omitempty,email"`
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
|
return err
|
||||||
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
|
}
|
||||||
|
|
||||||
|
// 查询用户列表
|
||||||
|
users, total, err := q.User.
|
||||||
|
Preload(q.User.Admin).
|
||||||
|
Omit(q.User.Password).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Admin != nil {
|
||||||
|
user.Admin = &m.Admin{
|
||||||
|
Name: user.Admin.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
List: users,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 绑定管理员
|
||||||
|
func BindAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
authCtx, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(struct {
|
||||||
|
UserID int `json:"user_id" validate:"required"`
|
||||||
|
})
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
result, err := q.User.Where(
|
||||||
|
q.User.ID.Eq(int32(req.UserID)),
|
||||||
|
q.User.AdminID.IsNull(),
|
||||||
|
).UpdateColumnSimple(
|
||||||
|
q.User.AdminID.Value(authCtx.Admin.ID),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("用户已绑定管理员")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户
|
||||||
func UpdateUser(c *fiber.Ctx) error {
|
func UpdateUser(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -49,15 +117,14 @@ func UpdateUser(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
type UpdateUserReq struct {
|
||||||
|
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
||||||
// region /update/account
|
Email string `json:"email" validate:"omitempty,email"`
|
||||||
|
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
|
||||||
type UpdateAccountReq struct {
|
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
|
||||||
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
|
||||||
Password string `json:"password" validate:"omitempty,min=6,max=20"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新账号信息
|
||||||
func UpdateAccount(c *fiber.Ctx) error {
|
func UpdateAccount(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -86,16 +153,12 @@ func UpdateAccount(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
type UpdateAccountReq struct {
|
||||||
|
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
||||||
// region /update/password
|
Password string `json:"password" validate:"omitempty,min=6,max=20"`
|
||||||
|
|
||||||
type UpdatePasswordReq struct {
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
Code string `json:"code"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新账号密码
|
||||||
func UpdatePassword(c *fiber.Ctx) error {
|
func UpdatePassword(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
@@ -109,8 +172,13 @@ func UpdatePassword(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证手机号
|
||||||
|
if req.Phone != authCtx.User.Phone {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "手机号码不正确")
|
||||||
|
}
|
||||||
|
|
||||||
// 验证手机令牌
|
// 验证手机令牌
|
||||||
if req.Phone == "" || req.Code == "" {
|
if req.Code == "" {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "手机号码和验证码不能为空")
|
return fiber.NewError(fiber.StatusBadRequest, "手机号码和验证码不能为空")
|
||||||
}
|
}
|
||||||
err = s.Verifier.VerifySms(c.Context(), req.Phone, req.Code)
|
err = s.Verifier.VerifySms(c.Context(), req.Phone, req.Code)
|
||||||
@@ -135,4 +203,8 @@ func UpdatePassword(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
type UpdatePasswordReq struct {
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gofiber/contrib/otelfiber/v2"
|
"github.com/gofiber/contrib/otelfiber/v2"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||||
@@ -19,6 +20,14 @@ func ApplyMiddlewares(app *fiber.App) {
|
|||||||
EnableStackTrace: true,
|
EnableStackTrace: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// cors
|
||||||
|
app.Use(cors.New(cors.Config{
|
||||||
|
AllowCredentials: true,
|
||||||
|
AllowOriginsFunc: func(origin string) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
app.Use(logger.New(logger.Config{
|
app.Use(logger.New(logger.Config{
|
||||||
Next: func(c *fiber.Ctx) bool {
|
Next: func(c *fiber.Ctx) bool {
|
||||||
|
|||||||
@@ -10,12 +10,30 @@ import (
|
|||||||
|
|
||||||
func ApplyRouters(app *fiber.App) {
|
func ApplyRouters(app *fiber.App) {
|
||||||
api := app.Group("/api")
|
api := app.Group("/api")
|
||||||
|
userRouter(api)
|
||||||
|
adminRouter(api)
|
||||||
|
|
||||||
|
// 回调
|
||||||
|
callbacks := app.Group("/callback")
|
||||||
|
callbacks.Get("/identify", handlers.IdentifyCallbackNew)
|
||||||
|
|
||||||
|
// 临时
|
||||||
|
if env.RunMode == env.RunModeDev {
|
||||||
|
debug := app.Group("/debug")
|
||||||
|
debug.Get("/sms/:phone", handlers.DebugGetSmsCode)
|
||||||
|
debug.Get("/proxy/register", handlers.DebugRegisterProxyBaiYin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户接口路由
|
||||||
|
func userRouter(api fiber.Router) {
|
||||||
// 认证
|
// 认证
|
||||||
auth := api.Group("/auth")
|
auth := api.Group("/auth")
|
||||||
|
auth.Get("/authorize", auth2.AuthorizeGet)
|
||||||
|
auth.Post("/authorize", auth2.AuthorizePost)
|
||||||
auth.Post("/token", auth2.Token)
|
auth.Post("/token", auth2.Token)
|
||||||
auth.Post("/revoke", handlers.Revoke)
|
auth.Post("/revoke", auth2.Revoke)
|
||||||
auth.Post("/introspect", handlers.Introspect)
|
auth.Post("/introspect", auth2.Introspect)
|
||||||
auth.Post("/verify/sms", handlers.SmsCode)
|
auth.Post("/verify/sms", handlers.SmsCode)
|
||||||
|
|
||||||
// 用户
|
// 用户
|
||||||
@@ -35,8 +53,8 @@ func ApplyRouters(app *fiber.App) {
|
|||||||
// 套餐
|
// 套餐
|
||||||
resource := api.Group("/resource")
|
resource := api.Group("/resource")
|
||||||
resource.Post("/all", handlers.AllActiveResource)
|
resource.Post("/all", handlers.AllActiveResource)
|
||||||
resource.Post("/list/short", handlers.ListResourceShort)
|
resource.Post("/list/short", handlers.PageResourceShort)
|
||||||
resource.Post("/list/long", handlers.ListResourceLong)
|
resource.Post("/list/long", handlers.PageResourceLong)
|
||||||
resource.Post("/create", handlers.CreateResource)
|
resource.Post("/create", handlers.CreateResource)
|
||||||
resource.Post("/price", handlers.ResourcePrice)
|
resource.Post("/price", handlers.ResourcePrice)
|
||||||
resource.Post("/statistics/free", handlers.StatisticResourceFree)
|
resource.Post("/statistics/free", handlers.StatisticResourceFree)
|
||||||
@@ -72,7 +90,7 @@ func ApplyRouters(app *fiber.App) {
|
|||||||
proxy.Post("/online", handlers.ProxyReportOnline)
|
proxy.Post("/online", handlers.ProxyReportOnline)
|
||||||
proxy.Post("/offline", handlers.ProxyReportOffline)
|
proxy.Post("/offline", handlers.ProxyReportOffline)
|
||||||
proxy.Post("/update", handlers.ProxyReportUpdate)
|
proxy.Post("/update", handlers.ProxyReportUpdate)
|
||||||
proxy.Post("/register", handlers.ProxyRegisterBaiYin)
|
proxy.Post("/register/baidyin", handlers.ProxyRegisterBaiYin)
|
||||||
|
|
||||||
// 节点
|
// 节点
|
||||||
edge := api.Group("/edge")
|
edge := api.Group("/edge")
|
||||||
@@ -82,15 +100,35 @@ func ApplyRouters(app *fiber.App) {
|
|||||||
// 前台
|
// 前台
|
||||||
inquiry := api.Group("/inquiry")
|
inquiry := api.Group("/inquiry")
|
||||||
inquiry.Post("/create", handlers.CreateInquiry)
|
inquiry.Post("/create", handlers.CreateInquiry)
|
||||||
|
}
|
||||||
// 回调
|
|
||||||
callbacks := app.Group("/callback")
|
// 管理员接口路由
|
||||||
callbacks.Get("/identify", handlers.IdentifyCallbackNew)
|
func adminRouter(api fiber.Router) {
|
||||||
|
api = api.Group("/admin")
|
||||||
// 临时
|
|
||||||
if env.RunMode == env.RunModeDev {
|
// user 用户
|
||||||
debug := app.Group("/debug")
|
var user = api.Group("/user")
|
||||||
debug.Get("/sms/:phone", handlers.DebugGetSmsCode)
|
user.Post("/page", handlers.PageUserByAdmin)
|
||||||
debug.Get("/proxy/register", handlers.DebugRegisterProxyBaiYin)
|
user.Post("/bind", handlers.BindAdmin)
|
||||||
}
|
|
||||||
|
// resource 套餐
|
||||||
|
var resource = api.Group("/resource")
|
||||||
|
resource.Post("/short/page", handlers.PageResourceShortByAdmin)
|
||||||
|
resource.Post("/long/page", handlers.PageResourceLongByAdmin)
|
||||||
|
|
||||||
|
// batch 批次
|
||||||
|
var usage = api.Group("batch")
|
||||||
|
usage.Post("/page", handlers.PageBatchByAdmin)
|
||||||
|
|
||||||
|
// channel 通道
|
||||||
|
var channel = api.Group("/channel")
|
||||||
|
channel.Post("/page", handlers.PageChannelsByAdmin)
|
||||||
|
|
||||||
|
// trade 交易
|
||||||
|
var trade = api.Group("trade")
|
||||||
|
trade.Post("/page", handlers.PageTradeByAdmin)
|
||||||
|
|
||||||
|
// bill 账单
|
||||||
|
var bill = api.Group("/bill")
|
||||||
|
bill.Post("/page", handlers.PageBillByAdmin)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user