feature/member #1
15
Makefile
15
Makefile
|
@ -1,10 +1,19 @@
|
|||
#goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir .
|
||||
#goctl api go -api ./generate/api/gateway.api -dir . -style go_zero
|
||||
|
||||
GOFMT ?= gofmt "-s"
|
||||
GOFMT ?= gofmt
|
||||
GOFILES := $(shell find . -name "*.go")
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: # 格式優化
|
||||
$(GOFMT) -w $(GOFILES)
|
||||
goimports -w ./
|
||||
$(GOFMT) -s -w $(GOFILES)
|
||||
goimports -w ./
|
||||
|
||||
|
||||
.PHONY: gen-doc
|
||||
gen-doc: # 格式優化
|
||||
goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir .
|
||||
|
||||
.PHONY: gen-api
|
||||
gen-api: # 格式優化
|
||||
goctl api go -api ./generate/api/gateway.api -dir . -style go_zero
|
||||
|
|
|
@ -17,6 +17,7 @@ PermissionRpc:
|
|||
- 127.0.0.1:2379
|
||||
Key: permission.rpc
|
||||
Token:
|
||||
Secret: kupiHowBonBon
|
||||
Expired: 300s
|
||||
|
||||
RedisCluster:
|
||||
|
|
200
gateway.json
200
gateway.json
|
@ -42,6 +42,24 @@
|
|||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "device_id",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "ip_address",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "brewser",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
|
@ -112,7 +130,7 @@
|
|||
"post": {
|
||||
"summary": "發送忘記密碼驗證",
|
||||
"description": "發送忘記密碼驗證(三分鐘內只能發一次信)",
|
||||
"operationId": "ForgetPassworCode",
|
||||
"operationId": "ForgetPasswordCode",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
|
@ -179,12 +197,6 @@
|
|||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "uid",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"in": "header",
|
||||
|
@ -223,6 +235,24 @@
|
|||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "device_id",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "ip_address",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "brewser",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
|
@ -269,16 +299,74 @@
|
|||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "uid",
|
||||
"name": "token",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"gateway/member"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/member/refresh_access_token": {
|
||||
"put": {
|
||||
"summary": "更新Token",
|
||||
"description": "用 RefreshToken 換取 AccessToken",
|
||||
"operationId": "RefreshAccessToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/LoginResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "輸入的參數錯誤",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/BaseResponse"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "無效的驗證碼",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/BaseResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "伺服器出錯",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/BaseResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "device_id",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"name": "ip_address",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "brewser",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/UpdateTokenReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
|
@ -418,13 +506,14 @@
|
|||
"description": "平台名稱 digimon, google, twitter"
|
||||
},
|
||||
"account_type": {
|
||||
"type": "string",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"enum": [
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"description": "帳號類型 1 Email 2. 台灣手機 3. 任意"
|
||||
"description": "帳號類型 1 手機 2 信箱 3 自定義帳號"
|
||||
}
|
||||
},
|
||||
"title": "CreateAccountRequest",
|
||||
|
@ -461,13 +550,14 @@
|
|||
"description": "帳號名稱"
|
||||
},
|
||||
"account_type": {
|
||||
"type": "string",
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"enum": [
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"description": "帳號類型 1 Email 2. 台灣手機 3. 任意"
|
||||
"description": "帳號類型 1 手機 2 信箱 3 自定義帳號"
|
||||
}
|
||||
},
|
||||
"title": "ForgetPasswordCodeReq",
|
||||
|
@ -476,6 +566,11 @@
|
|||
"account_type"
|
||||
]
|
||||
},
|
||||
"GetMemberHeader": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"title": "GetMemberHeader"
|
||||
},
|
||||
"Header": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
|
@ -484,6 +579,10 @@
|
|||
"LoginItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"description": "Account"
|
||||
},
|
||||
"access_token": {
|
||||
"type": "string",
|
||||
"description": "訪問令牌 預設 5 分鐘過期"
|
||||
|
@ -499,6 +598,7 @@
|
|||
},
|
||||
"title": "LoginItem",
|
||||
"required": [
|
||||
"uid",
|
||||
"access_token",
|
||||
"refresh_token",
|
||||
"token_type"
|
||||
|
@ -525,13 +625,14 @@
|
|||
"description": "平台名稱 digimon, google, twitter"
|
||||
},
|
||||
"account_type": {
|
||||
"type": "string",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"enum": [
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"description": "帳號類型 1 Email 2. 台灣手機 3. 任意"
|
||||
"description": "帳號類型 1 手機 2 信箱 3 自定義帳號"
|
||||
}
|
||||
},
|
||||
"title": "LoginReq",
|
||||
|
@ -559,6 +660,11 @@
|
|||
"data"
|
||||
]
|
||||
},
|
||||
"MemberLoginHeader": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"title": "MemberLoginHeader"
|
||||
},
|
||||
"Status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -614,10 +720,70 @@
|
|||
"token_check"
|
||||
]
|
||||
},
|
||||
"UpdateTokenReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"description": "uid"
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"description": "refresh token"
|
||||
}
|
||||
},
|
||||
"title": "UpdateTokenReq",
|
||||
"required": [
|
||||
"uid",
|
||||
"token"
|
||||
]
|
||||
},
|
||||
"UserInfo": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"title": "UserInfo"
|
||||
"properties": {
|
||||
"uid": {
|
||||
"type": "string"
|
||||
},
|
||||
"verify_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"alarm_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"currency": {
|
||||
"type": "string"
|
||||
},
|
||||
"avatar": {
|
||||
"type": "string"
|
||||
},
|
||||
"curreate_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"update_time": {
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "UserInfo",
|
||||
"required": [
|
||||
"uid",
|
||||
"verify_type",
|
||||
"alarm_type",
|
||||
"status",
|
||||
"language",
|
||||
"currency",
|
||||
"avatar",
|
||||
"curreate_time",
|
||||
"update_time"
|
||||
]
|
||||
},
|
||||
"UserInfoResp": {
|
||||
"type": "object",
|
||||
|
|
|
@ -166,12 +166,28 @@ type Header {
|
|||
Token string `header:"token"`
|
||||
}
|
||||
|
||||
type GetMemberHeader {
|
||||
Token string `header:"token"`
|
||||
}
|
||||
|
||||
type UserInfoResp {
|
||||
Status Status `json:"status"` // 狀態
|
||||
Data UserInfo `json:"data"`
|
||||
}
|
||||
|
||||
type UserInfo {}
|
||||
type UserInfo {
|
||||
UID string `json:"uid"`
|
||||
VerifyType string `json:"verify_type"`
|
||||
AlarmType string `json:"alarm_type"`
|
||||
Status string `json:"status"`
|
||||
Language string `json:"language"`
|
||||
Currency string `json:"currency"`
|
||||
Avatar string `json:"avatar"`
|
||||
CreateTime string `json:"curreate_time"`
|
||||
UpdateTime string `json:"update_time"`
|
||||
NickName *string `json:"nick_name,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@server(
|
||||
group: member
|
||||
|
@ -189,7 +205,7 @@ service gateway {
|
|||
summary: "會員登出"
|
||||
)
|
||||
@handler Logout
|
||||
get /member/logout (Header) returns (BaseResponse)
|
||||
get /member/logout (GetMemberHeader) returns (BaseResponse)
|
||||
|
||||
/* @respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
|
||||
/* @respdoc-403 (BaseResponse) // 無效的Token */
|
||||
|
@ -198,5 +214,5 @@ service gateway {
|
|||
summary: "取得會員資訊"
|
||||
)
|
||||
@handler Info
|
||||
get /member/info (Header) returns (UserInfoResp)
|
||||
get /member/info (GetMemberHeader) returns (UserInfoResp)
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.22.3
|
|||
|
||||
require (
|
||||
code.30cm.net/digimon/library-go/errors v1.0.1
|
||||
code.30cm.net/digimon/library-go/jwt v1.0.0
|
||||
code.30cm.net/digimon/proto-all v0.0.0-20240826070029-4a87e93fd2cf
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/matcornic/hermes/v2 v2.1.0
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
type Config struct {
|
||||
rest.RestConf
|
||||
Token struct {
|
||||
Secret string
|
||||
Expired time.Duration
|
||||
}
|
||||
// Redis Cluster
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package domain
|
||||
|
||||
type ContextKey string
|
||||
|
||||
func (c ContextKey) ToString() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
const (
|
||||
RoleCode ContextKey = "role"
|
||||
DeviceIDCode ContextKey = "device_id"
|
||||
ScopeCode ContextKey = "scope"
|
||||
UidCode ContextKey = "uid"
|
||||
)
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
func InfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.Header
|
||||
var req types.GetMemberHeader
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.Header
|
||||
var req types.GetMemberHeader
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"app-cloudep-portal-api-gateway/internal/svc"
|
||||
"app-cloudep-portal-api-gateway/internal/types"
|
||||
ers "code.30cm.net/digimon/library-go/errors"
|
||||
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
ers "code.30cm.net/digimon/library-go/errors"
|
||||
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,10 +4,13 @@ import (
|
|||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"app-cloudep-portal-api-gateway/internal/svc"
|
||||
"app-cloudep-portal-api-gateway/internal/types"
|
||||
ers "code.30cm.net/digimon/library-go/errors"
|
||||
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
notificationRpc "code.30cm.net/digimon/proto-all/pkg/notification"
|
||||
|
||||
ers "code.30cm.net/digimon/library-go/errors"
|
||||
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
|
||||
"github.com/matcornic/hermes/v2"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
|
@ -62,28 +65,27 @@ func (l *ForgetPasswordCodeLogic) ForgetPasswordCode(req *types.ForgetPasswordCo
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(info)
|
||||
|
||||
// // 準備驗證碼郵件
|
||||
// nickName := info.Data.Uid
|
||||
// if info.Data.NickName != nil {
|
||||
// nickName = *info.Data.NickName
|
||||
// }
|
||||
// mailContent, title, err := ForgerZHTW(nickName, code.Data.VerifyCode)
|
||||
// if err != nil {
|
||||
// return nil, ers.InvalidFormat("failed to generate mail content: ", err.Error())
|
||||
// }
|
||||
// 準備驗證碼郵件
|
||||
nickName := info.Data.Uid
|
||||
if info.Data.NickName != nil {
|
||||
nickName = *info.Data.NickName
|
||||
}
|
||||
mailContent, title, err := ForgerZHTW(nickName, code.Data.VerifyCode)
|
||||
if err != nil {
|
||||
return nil, ers.InvalidFormat("failed to generate mail content: ", err.Error())
|
||||
}
|
||||
|
||||
// // 發送郵件
|
||||
// _, err = l.svcCtx.NotificationRpc.SendMail(l.ctx, ¬ificationRpc.SendMailReq{
|
||||
// Body: mailContent,
|
||||
// Subject: title,
|
||||
// To: req.Account,
|
||||
// From: l.svcCtx.Config.MailSender,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// 發送郵件
|
||||
_, err = l.svcCtx.NotificationRpc.SendMail(l.ctx, ¬ificationRpc.SendMailReq{
|
||||
Body: mailContent,
|
||||
Subject: title,
|
||||
To: req.Account,
|
||||
From: l.svcCtx.Config.MailSender,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 設置 Redis 鍵,並設置 3 分鐘的過期時間
|
||||
err = l.svcCtx.Redis.Set(rk, code.Data.VerifyCode)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package member
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"app-cloudep-portal-api-gateway/internal/payload"
|
||||
"app-cloudep-portal-api-gateway/internal/svc"
|
||||
"app-cloudep-portal-api-gateway/internal/types"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
|
@ -23,8 +26,30 @@ func NewInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InfoLogic {
|
|||
}
|
||||
}
|
||||
|
||||
func (l *InfoLogic) Info(req *types.Header) (resp *types.UserInfoResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
func (l *InfoLogic) Info(_ *types.GetMemberHeader) (resp *types.UserInfoResp, err error) {
|
||||
info, err := l.svcCtx.AccountRpc.GetUserInfo(l.ctx, &accountRpc.GetUserInfoReq{
|
||||
Uid: payload.UID(l.ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
return &types.UserInfoResp{
|
||||
Status: types.Status{
|
||||
Code: domain.SuccessCode,
|
||||
Message: domain.SuccessMsg,
|
||||
},
|
||||
Data: types.UserInfo{
|
||||
UID: info.Data.Uid,
|
||||
VerifyType: info.Data.VerifyType.String(),
|
||||
AlarmType: info.Data.AlarmType.String(),
|
||||
Status: info.Data.Status.String(),
|
||||
Language: info.Data.Language,
|
||||
Currency: info.Data.Currency,
|
||||
Avatar: info.Data.Avatar,
|
||||
CreateTime: time.Unix(info.Data.CreateTime, 0).UTC().Format(time.RFC3339),
|
||||
UpdateTime: time.Unix(info.Data.UpdateTime, 0).UTC().Format(time.RFC3339),
|
||||
NickName: info.Data.NickName,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package member
|
||||
|
||||
import (
|
||||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"context"
|
||||
|
||||
"code.30cm.net/digimon/proto-all/pkg/permission"
|
||||
|
||||
"app-cloudep-portal-api-gateway/internal/svc"
|
||||
"app-cloudep-portal-api-gateway/internal/types"
|
||||
|
||||
|
@ -23,8 +26,18 @@ func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogi
|
|||
}
|
||||
}
|
||||
|
||||
func (l *LogoutLogic) Logout(req *types.Header) (resp *types.BaseResponse, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
func (l *LogoutLogic) Logout(req *types.GetMemberHeader) (resp *types.BaseResponse, err error) {
|
||||
_, err = l.svcCtx.TokenRpc.CancelToken(l.ctx, &permission.CancelTokenReq{
|
||||
Token: req.Token,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
return &types.BaseResponse{
|
||||
Status: types.Status{
|
||||
Code: domain.SuccessCode,
|
||||
Message: domain.SuccessMsg,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package member
|
|||
|
||||
import (
|
||||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
ers "code.30cm.net/digimon/library-go/errors"
|
||||
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
|
||||
permissionRpc "code.30cm.net/digimon/proto-all/pkg/permission"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"app-cloudep-portal-api-gateway/internal/svc"
|
||||
"app-cloudep-portal-api-gateway/internal/types"
|
||||
|
|
|
@ -1,19 +1,79 @@
|
|||
package middleware
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"app-cloudep-portal-api-gateway/internal/types"
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
ers "code.30cm.net/digimon/library-go/errors"
|
||||
token "code.30cm.net/digimon/library-go/jwt"
|
||||
permissionRpc "code.30cm.net/digimon/proto-all/pkg/permission"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
type AuthMiddlewareParam struct {
|
||||
TokenSec string
|
||||
TokenClient permissionRpc.TokenServiceClient
|
||||
}
|
||||
|
||||
type AuthMiddleware struct {
|
||||
tokenSec string
|
||||
tokenClient permissionRpc.TokenServiceClient
|
||||
}
|
||||
|
||||
func NewAuthMiddleware() *AuthMiddleware {
|
||||
return &AuthMiddleware{}
|
||||
}
|
||||
|
||||
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO generate middleware implement function, delete after code implementation
|
||||
|
||||
// Passthrough to next handler if need
|
||||
next(w, r)
|
||||
func NewAuthMiddleware(param AuthMiddlewareParam) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
tokenSec: param.TokenSec,
|
||||
tokenClient: param.TokenClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 處理 Auth Middleware
|
||||
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// 解析 Header
|
||||
header := types.GetMemberHeader{}
|
||||
if err := httpx.ParseHeaders(r, &header); err != nil {
|
||||
m.writeErrorResponse(w, r, http.StatusBadRequest, "Failed to parse headers", int64(ers.InvalidFormat().FullCode()))
|
||||
return
|
||||
}
|
||||
|
||||
// 驗證 Token
|
||||
claim, err := token.ParseClaims(header.Token, m.tokenSec, true)
|
||||
if err != nil {
|
||||
// 是否需要紀錄錯誤,是不是只要紀錄除了驗證失敗或過期之外的真錯誤
|
||||
m.writeErrorResponse(w, r, http.StatusForbidden, err.Error(), int64(ers.Forbidden().FullCode()))
|
||||
return
|
||||
}
|
||||
|
||||
// 驗證 Token 是否在黑名單中
|
||||
if _, err := m.tokenClient.ValidationToken(r.Context(), &permissionRpc.ValidationTokenReq{Token: header.Token}); err != nil {
|
||||
m.writeErrorResponse(w, r, http.StatusForbidden, err.Error(), int64(ers.Forbidden().FullCode()))
|
||||
return
|
||||
}
|
||||
|
||||
// 設置 context 並傳遞給下一個處理器
|
||||
ctx := SetContext(r, claim)
|
||||
next(w, r.WithContext(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
func SetContext(r *http.Request, claim token.DataClaims) context.Context {
|
||||
ctx := context.WithValue(r.Context(), domain.RoleCode, claim.Role())
|
||||
ctx = context.WithValue(ctx, domain.UidCode, claim.UID())
|
||||
ctx = context.WithValue(ctx, domain.DeviceIDCode, claim.DeviceID())
|
||||
ctx = context.WithValue(ctx, domain.ScopeCode, claim.Get(domain.ScopeCode.ToString()))
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// writeErrorResponse 用於處理錯誤回應
|
||||
func (m *AuthMiddleware) writeErrorResponse(w http.ResponseWriter, r *http.Request, statusCode int, message string, code int64) {
|
||||
httpx.WriteJsonCtx(r.Context(), w, statusCode, types.BaseResponse{
|
||||
Status: types.Status{
|
||||
Code: code,
|
||||
Message: message,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package payload
|
||||
|
||||
import (
|
||||
"app-cloudep-portal-api-gateway/internal/domain"
|
||||
"context"
|
||||
)
|
||||
|
||||
func UID(ctx context.Context) string {
|
||||
return ctx.Value(domain.UidCode).(string)
|
||||
}
|
||||
|
||||
func Scope(ctx context.Context) string {
|
||||
return ctx.Value(domain.ScopeCode).(string)
|
||||
}
|
||||
|
||||
func Role(ctx context.Context) string {
|
||||
return ctx.Value(domain.RoleCode).(string)
|
||||
}
|
||||
|
||||
func DeviceID(ctx context.Context) string {
|
||||
return ctx.Value(domain.DeviceIDCode).(string)
|
||||
}
|
|
@ -32,11 +32,15 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
tc := permissionRpc.NewTokenServiceClient(zrpc.MustNewClient(c.PermissionRpc).Conn())
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
AuthMiddleware: middleware.NewAuthMiddleware(),
|
||||
Config: c,
|
||||
AuthMiddleware: middleware.NewAuthMiddleware(middleware.AuthMiddlewareParam{
|
||||
TokenSec: c.Token.Secret,
|
||||
TokenClient: tc,
|
||||
}),
|
||||
AccountRpc: accountRpc.NewAccountClient(zrpc.MustNewClient(c.AccountRpc).Conn()),
|
||||
TokenRpc: permissionRpc.NewTokenServiceClient(zrpc.MustNewClient(c.PermissionRpc).Conn()),
|
||||
TokenRpc: tc,
|
||||
NotificationRpc: notificationRpc.NewSenderServiceClient(zrpc.MustNewClient(c.NotificationRpc).Conn()),
|
||||
Redis: *newRedis,
|
||||
}
|
||||
|
|
|
@ -87,10 +87,24 @@ type Header struct {
|
|||
Token string `header:"token"`
|
||||
}
|
||||
|
||||
type GetMemberHeader struct {
|
||||
Token string `header:"token"`
|
||||
}
|
||||
|
||||
type UserInfoResp struct {
|
||||
Status Status `json:"status"` // 狀態
|
||||
Data UserInfo `json:"data"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
UID string `json:"uid"`
|
||||
VerifyType string `json:"verify_type"`
|
||||
AlarmType string `json:"alarm_type"`
|
||||
Status string `json:"status"`
|
||||
Language string `json:"language"`
|
||||
Currency string `json:"currency"`
|
||||
Avatar string `json:"avatar"`
|
||||
CreateTime string `json:"curreate_time"`
|
||||
UpdateTime string `json:"update_time"`
|
||||
NickName *string `json:"nick_name,omitempty"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue