reset backend

This commit is contained in:
2025-12-27 19:49:11 +08:00
parent 77ac9d00b3
commit 503b15aab7
129 changed files with 1134 additions and 18084 deletions

View File

@@ -1,9 +0,0 @@
package middlewares
import (
"github.com/gofiber/fiber/v3"
)
func (f *Middlewares) DebugMode(c fiber.Ctx) error {
return c.Next()
}

View File

@@ -1,68 +0,0 @@
package middlewares
import (
"strings"
"quyun/v2/app/errorx"
"quyun/v2/app/services"
"quyun/v2/pkg/consts"
"quyun/v2/providers/jwt"
"github.com/gofiber/fiber/v3"
)
func shouldSkipSuperJWTAuth(path string) bool {
// 登录接口允许匿名访问。
return strings.Contains(path, "/super/v1/auth/login")
}
// SuperAuth 平台侧超级管理员鉴权:
// - 校验 JWT 并写入 claims
// - 加载用户并校验包含 super_admin 角色
func (f *Middlewares) SuperAuth(c fiber.Ctx) error {
if shouldSkipSuperJWTAuth(c.Path()) {
f.log.Debug("middlewares.super.auth.skipped")
return c.Next()
}
authHeader := c.Get(jwt.HttpHeader)
if authHeader == "" {
f.log.Info("middlewares.super.auth.missing_token")
return errorx.ErrTokenMissing
}
claims, err := f.jwt.Parse(authHeader)
if err != nil {
f.log.WithError(err).Warn("middlewares.super.auth.invalid_token")
switch err {
case jwt.TokenExpired:
return errorx.ErrTokenExpired
case jwt.TokenMalformed, jwt.TokenNotValidYet, jwt.TokenInvalid:
return errorx.ErrTokenInvalid
default:
return errorx.ErrTokenInvalid
}
}
if claims.UserID == 0 {
f.log.Warn("middlewares.super.auth.missing_user_id")
return errorx.ErrTokenInvalid
}
userModel, err := services.User.FindByID(c, claims.UserID)
if err != nil {
f.log.WithField("user_id", claims.UserID).WithError(err).Warn("middlewares.super.auth.load_user_failed")
return err
}
if !userModel.Roles.Contains(consts.RoleSuperAdmin) {
f.log.WithField("user_id", claims.UserID).Warn("middlewares.super.auth.denied")
return errorx.ErrPermissionDenied.WithMsg("需要超级管理员权限")
}
f.log.WithFields(map[string]any{
"user_id": claims.UserID,
}).Info("middlewares.super.auth.ok")
c.Locals(consts.CtxKeyClaims, claims)
c.Locals(consts.CtxKeyUser, userModel)
return c.Next()
}

View File

@@ -1,176 +0,0 @@
package middlewares
import (
"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"
)
func shouldSkipTenantJWTAuth(path string) bool {
// Public read endpoints allow anonymous access (optional JWT).
if strings.Contains(path, "/v1/public/") {
return true
}
// Media play is token-based, no JWT required.
if strings.Contains(path, "/v1/media/play") {
return true
}
return false
}
func shouldSkipTenantRequireMember(path string) bool {
// Public read endpoints allow anonymous access.
if strings.Contains(path, "/v1/public/") {
return true
}
// Join endpoints require JWT but not tenant membership.
if strings.Contains(path, "/v1/join/") {
return true
}
// Media play is token-based, no JWT required.
if strings.Contains(path, "/v1/media/play") {
return true
}
return false
}
func (f *Middlewares) TenantResolve(c fiber.Ctx) error {
tenantCode := c.Params("tenantCode")
if tenantCode == "" {
return errorx.ErrMissingParameter.WithMsg("缺少 tenantCode")
}
tenantModel, err := services.Tenant.FindByCode(c, tenantCode)
if err != nil {
f.log.WithField("tenant_code", tenantCode).WithError(err).Warn("middlewares.tenant.resolve.failed")
return err
}
f.log.WithFields(map[string]any{
"tenant_id": tenantModel.ID,
"tenant_code": tenantCode,
}).Info("middlewares.tenant.resolve.ok")
c.Locals(consts.CtxKeyTenant, tenantModel)
return c.Next()
}
func (f *Middlewares) TenantAuth(c fiber.Ctx) error {
if shouldSkipTenantJWTAuth(c.Path()) {
f.log.Debug("middlewares.tenant.auth.skipped")
return c.Next()
}
authHeader := c.Get(jwt.HttpHeader)
if authHeader == "" {
f.log.Info("middlewares.tenant.auth.missing_token")
return errorx.ErrTokenMissing
}
claims, err := f.jwt.Parse(authHeader)
if err != nil {
f.log.WithError(err).Warn("middlewares.tenant.auth.invalid_token")
switch err {
case jwt.TokenExpired:
return errorx.ErrTokenExpired
case jwt.TokenMalformed, jwt.TokenNotValidYet, jwt.TokenInvalid:
return errorx.ErrTokenInvalid
default:
return errorx.ErrTokenInvalid
}
}
if claims.UserID == 0 {
f.log.Warn("middlewares.tenant.auth.missing_user_id")
return errorx.ErrTokenInvalid
}
f.log.WithFields(map[string]any{
"user_id": claims.UserID,
}).Info("middlewares.tenant.auth.ok")
c.Locals(consts.CtxKeyClaims, claims)
return c.Next()
}
// TenantOptionalAuth 在 token 存在时解析并写入 claims但允许无 token 的请求继续。
// 用于“公开只读”类接口:可匿名访问,但若携带 token 则可以得到更准确的 has_access 等判断。
func (f *Middlewares) TenantOptionalAuth(c fiber.Ctx) error {
authHeader := c.Get(jwt.HttpHeader)
if authHeader == "" {
f.log.Debug("middlewares.tenant.optional_auth.no_token")
return c.Next()
}
claims, err := f.jwt.Parse(authHeader)
if err != nil {
f.log.WithError(err).Warn("middlewares.tenant.optional_auth.invalid_token")
switch err {
case jwt.TokenExpired:
return errorx.ErrTokenExpired
case jwt.TokenMalformed, jwt.TokenNotValidYet, jwt.TokenInvalid:
return errorx.ErrTokenInvalid
default:
return errorx.ErrTokenInvalid
}
}
if claims.UserID == 0 {
f.log.Warn("middlewares.tenant.optional_auth.missing_user_id")
return errorx.ErrTokenInvalid
}
f.log.WithFields(map[string]any{
"user_id": claims.UserID,
}).Debug("middlewares.tenant.optional_auth.ok")
c.Locals(consts.CtxKeyClaims, claims)
return c.Next()
}
func (f *Middlewares) TenantRequireMember(c fiber.Ctx) error {
if shouldSkipTenantRequireMember(c.Path()) {
f.log.Debug("middlewares.tenant.require_member.skipped")
return c.Next()
}
tenantModel, ok := c.Locals(consts.CtxKeyTenant).(*models.Tenant)
if !ok || tenantModel == nil {
f.log.Error("middlewares.tenant.require_member.missing_tenant_context")
return errorx.ErrInternalError.WithMsg("tenant context missing")
}
claims, ok := c.Locals(consts.CtxKeyClaims).(*jwt.Claims)
if !ok || claims == nil {
f.log.Error("middlewares.tenant.require_member.missing_claims_context")
return errorx.ErrInternalError.WithMsg("claims context missing")
}
tenantUser, err := services.Tenant.FindTenantUser(c, tenantModel.ID, claims.UserID)
if err != nil {
f.log.WithFields(map[string]any{
"tenant_id": tenantModel.ID,
"user_id": claims.UserID,
}).WithError(err).Warn("middlewares.tenant.require_member.denied")
return errorx.ErrPermissionDenied.WithMsg("不属于该租户")
}
userModel, err := services.User.FindByID(c, claims.UserID)
if err != nil {
f.log.WithField("user_id", claims.UserID).WithError(err).Warn("middlewares.tenant.require_member.load_user_failed")
return err
}
f.log.WithFields(map[string]any{
"tenant_id": tenantModel.ID,
"user_id": claims.UserID,
}).Info("middlewares.tenant.require_member.ok")
c.Locals(consts.CtxKeyTenantUser, tenantUser)
c.Locals(consts.CtxKeyUser, userModel)
return c.Next()
}

View File

@@ -1,64 +0,0 @@
package middlewares
import (
"strings"
"quyun/v2/app/errorx"
"quyun/v2/pkg/consts"
"quyun/v2/providers/jwt"
"github.com/gofiber/fiber/v3"
)
func shouldSkipUserJWTAuth(path, method string) bool {
// 仅对明确的公开接口放行,避免误伤其它路径。
if method != fiber.MethodPost {
return false
}
p := strings.TrimSuffix(path, "/")
switch p {
case "/v1/auth/login", "/v1/auth/register", "/v1/auth/password/reset/sms", "/v1/auth/password/reset/verify", "/v1/auth/password/reset":
return true
default:
return false
}
}
// UserAuth 为平台通用(非租户域)接口提供 JWT 校验,并写入 claims 到 ctx locals。
func (f *Middlewares) UserAuth(c fiber.Ctx) error {
if shouldSkipUserJWTAuth(c.Path(), c.Method()) {
f.log.Debug("middlewares.user.auth.skipped")
return c.Next()
}
authHeader := c.Get(jwt.HttpHeader)
if authHeader == "" {
f.log.Info("middlewares.user.auth.missing_token")
return errorx.ErrTokenMissing
}
claims, err := f.jwt.Parse(authHeader)
if err != nil {
f.log.WithError(err).Warn("middlewares.user.auth.invalid_token")
switch err {
case jwt.TokenExpired:
return errorx.ErrTokenExpired
case jwt.TokenMalformed, jwt.TokenNotValidYet, jwt.TokenInvalid:
return errorx.ErrTokenInvalid
default:
return errorx.ErrTokenInvalid
}
}
if claims.UserID == 0 {
f.log.Warn("middlewares.user.auth.missing_user_id")
return errorx.ErrTokenInvalid
}
f.log.WithFields(map[string]any{
"user_id": claims.UserID,
}).Info("middlewares.user.auth.ok")
c.Locals(consts.CtxKeyClaims, claims)
return c.Next()
}