feat: tenant-scoped routing and portal navigation
This commit is contained in:
@@ -1,16 +1,19 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"quyun/v2/app/errorx"
|
||||
"quyun/v2/app/services"
|
||||
"quyun/v2/database/models"
|
||||
"quyun/v2/pkg/consts"
|
||||
"quyun/v2/providers/jwt"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.ipao.vip/gen/types"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Middlewares provides reusable Fiber middlewares shared across modules.
|
||||
@@ -49,13 +52,18 @@ func (m *Middlewares) Auth(ctx fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// Set Context
|
||||
ctx.Locals("__ctx_user", user)
|
||||
if claims.TenantID > 0 {
|
||||
tenant, err := services.Tenant.GetModelByID(ctx, claims.TenantID)
|
||||
ctx.Locals(consts.CtxKeyUser, user)
|
||||
|
||||
if tenant := ctx.Locals(consts.CtxKeyTenant); tenant != nil {
|
||||
if model, ok := tenant.(*models.Tenant); ok && claims.TenantID > 0 && model.ID != claims.TenantID {
|
||||
return errorx.ErrForbidden.WithMsg("租户不匹配")
|
||||
}
|
||||
} else if claims.TenantID > 0 {
|
||||
tenantModel, err := services.Tenant.GetModelByID(ctx, claims.TenantID)
|
||||
if err != nil {
|
||||
return errorx.ErrUnauthorized.WithCause(err).WithMsg("TenantNotFound")
|
||||
}
|
||||
ctx.Locals("__ctx_tenant", tenant)
|
||||
ctx.Locals(consts.CtxKeyTenant, tenantModel)
|
||||
}
|
||||
|
||||
return ctx.Next()
|
||||
@@ -84,7 +92,26 @@ func (m *Middlewares) SuperAuth(ctx fiber.Ctx) error {
|
||||
return errorx.ErrForbidden.WithMsg("无权限访问")
|
||||
}
|
||||
|
||||
ctx.Locals("__ctx_user", user)
|
||||
ctx.Locals(consts.CtxKeyUser, user)
|
||||
return ctx.Next()
|
||||
}
|
||||
|
||||
func (m *Middlewares) TenantResolver(ctx fiber.Ctx) error {
|
||||
tenantCode := strings.TrimSpace(ctx.Params("tenantCode"))
|
||||
if tenantCode == "" {
|
||||
return errorx.ErrMissingParameter.WithMsg("缺少租户编码")
|
||||
}
|
||||
|
||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||
tenant, err := q.Where(tbl.Code.Eq(tenantCode)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errorx.ErrRecordNotFound.WithMsg("租户不存在")
|
||||
}
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
ctx.Locals(consts.CtxKeyTenant, tenant)
|
||||
return ctx.Next()
|
||||
}
|
||||
|
||||
@@ -98,7 +125,7 @@ func hasRole(roles types.Array[consts.Role], role consts.Role) bool {
|
||||
}
|
||||
|
||||
func isPublicRoute(ctx fiber.Ctx) bool {
|
||||
path := ctx.Path()
|
||||
path := normalizeTenantPath(ctx.Path())
|
||||
method := ctx.Method()
|
||||
|
||||
if method == fiber.MethodGet {
|
||||
@@ -127,6 +154,13 @@ func isPublicRoute(ctx fiber.Ctx) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if method == fiber.MethodPost {
|
||||
switch path {
|
||||
case "/v1/auth/otp", "/v1/auth/login":
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if method == fiber.MethodPut && strings.HasPrefix(path, "/v1/storage/") {
|
||||
return true
|
||||
}
|
||||
@@ -144,3 +178,19 @@ func isSuperPublicRoute(ctx fiber.Ctx) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func normalizeTenantPath(path string) string {
|
||||
if !strings.HasPrefix(path, "/t/") {
|
||||
return path
|
||||
}
|
||||
rest := strings.TrimPrefix(path, "/t/")
|
||||
slash := strings.Index(rest, "/")
|
||||
if slash == -1 {
|
||||
return path
|
||||
}
|
||||
rest = rest[slash:]
|
||||
if strings.HasPrefix(rest, "/v1") {
|
||||
return rest
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user