feat: Implement public access for tenant content

- Add TenantOptionalAuth middleware to allow access to public content without requiring authentication.
- Introduce ListPublicPublished and PublicDetail methods in the content service to retrieve publicly accessible content.
- Create tenant_public HTTP routes for listing and showing public content, including preview and main asset retrieval.
- Enhance content tests to cover scenarios for public content access and permissions.
- Update specifications to reflect the new public content access features and rules.
This commit is contained in:
2025-12-22 16:29:44 +08:00
parent 266de2f75e
commit 39454458f1
17 changed files with 1010 additions and 17 deletions

View File

@@ -63,6 +63,40 @@ func (f *Middlewares) TenantAuth(c fiber.Ctx) error {
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 {
tenantModel, ok := c.Locals(consts.CtxKeyTenant).(*models.Tenant)
if !ok || tenantModel == nil {