feat: 新增设备登录功能,包括API接口、RPC服务、逻辑处理和相关数据类型及错误码。
Build docker and publish / prepare (20.15.1) (push) Successful in 11s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m32s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 8m6s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m26s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 8m23s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 4m1s
Build docker and publish / deploy (push) Successful in 45s
Build docker and publish / notify (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 3m55s
Build docker and publish / prepare (20.15.1) (push) Successful in 11s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m32s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 8m6s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m26s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 8m23s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 4m1s
Build docker and publish / deploy (push) Successful in 45s
Build docker and publish / notify (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 3m55s
This commit is contained in:
@@ -19,6 +19,14 @@ type (
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
DeviceLoginReq {
|
||||
Identifier string `json:"identifier" validate:"required"`
|
||||
UserAgent string `json:"user_agent" validate:"required"`
|
||||
ShortCode string `json:"short_code,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
}
|
||||
|
||||
UserRegisterReq {
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
@@ -58,3 +66,14 @@ service ppanel-api {
|
||||
post /reset (ResetPasswordReq) returns (LoginResp)
|
||||
|
||||
}
|
||||
|
||||
@server (
|
||||
prefix: /api/v1/auth
|
||||
group: auth
|
||||
middleware: DecryptMiddleware
|
||||
)
|
||||
service ppanel-api {
|
||||
@doc "设备登录"
|
||||
@handler DeviceLoginHandler
|
||||
post /login/device (DeviceLoginReq) returns (LoginResp)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
// goctl 1.9.2
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/auth"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
// 设备登录
|
||||
func DeviceLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DeviceLoginReq
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := auth.NewDeviceLoginLogic(r.Context(), svcCtx)
|
||||
resp, err := l.DeviceLogin(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,21 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
rest.WithPrefix("/api/v1/auth"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.DecryptMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
// 设备登录
|
||||
Method: http.MethodPost,
|
||||
Path: "/login/device",
|
||||
Handler: auth.DeviceLoginHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithPrefix("/api/v1/auth"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
// goctl 1.9.2
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/jwtx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type DeviceLoginLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// 设备登录
|
||||
func NewDeviceLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceLoginLogic {
|
||||
return &DeviceLoginLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginReq) (resp *types.LoginResp, err error) {
|
||||
// 1. 调用 Core RPC 设备登录
|
||||
rpcResp, err := l.svcCtx.CoreRpc.DeviceLogin(l.ctx, &core.DeviceLoginReq{
|
||||
Identifier: req.Identifier,
|
||||
UserAgent: req.UserAgent,
|
||||
Ip: req.IP,
|
||||
ShortCode: req.ShortCode,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2. 检查用户是否禁用
|
||||
if rpcResp.IsDisabled {
|
||||
return nil, xerr.NewErrCode(xerr.UserDisabled)
|
||||
}
|
||||
|
||||
// 3. 生成 session ID
|
||||
sessionId := uuid.New().String()
|
||||
|
||||
// 4. 生成 JWT Token
|
||||
token, err := jwtx.GenerateToken(
|
||||
l.svcCtx.Config.JwtAuth.AccessSecret,
|
||||
rpcResp.UserId,
|
||||
"device",
|
||||
rpcResp.IsAdmin,
|
||||
l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
)
|
||||
if err != nil {
|
||||
l.Errorf("GenerateToken error: %v", err)
|
||||
return nil, xerr.NewErrCode(xerr.ServerError)
|
||||
}
|
||||
|
||||
// 5. 写入 Redis session
|
||||
expireDuration := time.Duration(l.svcCtx.Config.JwtAuth.AccessExpire) * time.Second
|
||||
|
||||
// session:{sessionId} → userId
|
||||
sessionKey := fmt.Sprintf("session:%s", sessionId)
|
||||
if err := l.svcCtx.Redis.SetexCtx(l.ctx, sessionKey, fmt.Sprintf("%d", rpcResp.UserId), int(expireDuration.Seconds())); err != nil {
|
||||
l.Errorf("Redis set session error: %v", err)
|
||||
}
|
||||
|
||||
// device:{identifier} → sessionId
|
||||
deviceKey := fmt.Sprintf("device:%s", req.Identifier)
|
||||
if err := l.svcCtx.Redis.SetexCtx(l.ctx, deviceKey, sessionId, int(expireDuration.Seconds())); err != nil {
|
||||
l.Errorf("Redis set device session error: %v", err)
|
||||
}
|
||||
|
||||
return &types.LoginResp{
|
||||
Token: token,
|
||||
}, nil
|
||||
}
|
||||
@@ -9,14 +9,16 @@ import (
|
||||
coreClient "github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreClient"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
CoreRpc coreClient.Core
|
||||
Redis *redis.Redis
|
||||
SignatureMiddleware *middleware.SignatureMiddleware
|
||||
DecryptMiddleware *middleware.DecryptMiddleware
|
||||
DecryptMiddleware rest.Middleware
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
@@ -26,7 +28,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
CoreRpc: coreClient.NewCore(zrpc.MustNewClient(c.CoreRpc)),
|
||||
Redis: rds,
|
||||
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
|
||||
DecryptMiddleware: middleware.NewDecryptMiddleware(c),
|
||||
DecryptMiddleware: middleware.NewDecryptMiddleware(c).Handle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,14 @@ type CreateTicketReq struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type DeviceLoginReq struct {
|
||||
Identifier string `json:"identifier" validate:"required"`
|
||||
UserAgent string `json:"user_agent" validate:"required"`
|
||||
ShortCode string `json:"short_code,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
}
|
||||
|
||||
type DocumentDetailReq struct {
|
||||
Id int64 `path:"id"`
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ func main() {
|
||||
|
||||
ctx := svc.NewServiceContext(c)
|
||||
server.Use(ctx.SignatureMiddleware.Handle)
|
||||
server.Use(ctx.DecryptMiddleware.Handle)
|
||||
handler.RegisterHandlers(server, ctx)
|
||||
|
||||
// Registe global http error handler
|
||||
|
||||
Reference in New Issue
Block a user