fix: resolve all workspace removal breakage - stub permissions, quota, ports, adapters
- Remove Workspace from domain/__init__.py exports - Stub domain/permissions.py (all checks pass) - Stub domain/quota.py (all checks pass) - Create missing port stubs: workspace_member_repository, workspace_repository, workspace_invitation_repository - Create missing adapter stubs: workspace_repository, workspace_invitation_repository - Create routes/permissions.py stub (require_workspace_member no-op) - Fix .env Redis URL encoding on production server
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# Compatibility module - workspace concept has been removed
|
||||
# This is a stub to maintain backward compatibility
|
||||
|
||||
def require_workspace_member(workspace_id, authenticated_user, workspace_member_repository):
|
||||
"""Stub function for backward compatibility.
|
||||
Workspace concept has been removed.
|
||||
"""
|
||||
pass
|
||||
@@ -68,8 +68,7 @@ const Header: React.FC = () => {
|
||||
|
||||
<nav className="xx-nav-links">
|
||||
{navItems.map((item) => {
|
||||
const active = item.path === '/' ? location.pathname === '/' : false
|
||||
: location.pathname.startsWith(item.path);
|
||||
const active = (item.path === '/' && location.pathname === '/') || location.pathname.startsWith(item.path);
|
||||
return (
|
||||
<button
|
||||
key={item.key}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import os
|
||||
from celery import Celery
|
||||
|
||||
|
||||
def create_celery_app() -> Celery:
|
||||
app = Celery("xiaoxia_saas_worker")
|
||||
app.conf.broker_url = "redis://redis:6379/0"
|
||||
app.conf.result_backend = "redis://redis:6379/1"
|
||||
app.conf.broker_url = os.getenv("CELERY_BROKER_URL", "redis://redis:6379/0")
|
||||
app.conf.result_backend = os.getenv("CELERY_RESULT_BACKEND", "redis://redis:6379/1")
|
||||
return app
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ WORKDIR /app
|
||||
COPY requirements.txt /tmp/requirements.txt
|
||||
|
||||
# 安装 Python 包
|
||||
RUN pip install --no-cache-dir -r /tmp/requirements.txt
|
||||
RUN pip install --no-cache-dir -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r /tmp/requirements.txt
|
||||
|
||||
# 复制应用代码
|
||||
COPY apps/worker/ /app/apps/worker/
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Stub adapter for workspace invitation repository - workspace concept removed."""
|
||||
|
||||
class SQLAlchemyWorkspaceInvitationRepository:
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Stub adapter for workspace repository - workspace concept removed."""
|
||||
|
||||
class SQLAlchemyWorkspaceRepository:
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
@@ -15,7 +15,6 @@ from .entities import (
|
||||
IngestJobStatus,
|
||||
Project,
|
||||
User,
|
||||
Workspace,
|
||||
)
|
||||
from .generated_video import GeneratedVideo
|
||||
from .generation_task import GenerationTask, GenerationTaskStatus
|
||||
@@ -42,5 +41,4 @@ __all__ = [
|
||||
"TaskPriority",
|
||||
"TaskStatus",
|
||||
"User",
|
||||
"Workspace",
|
||||
]
|
||||
|
||||
+32
-261
@@ -1,303 +1,74 @@
|
||||
"""
|
||||
权限验证辅助函数
|
||||
用于检查用户在工作空间中的权限
|
||||
Permissions module - stub implementation.
|
||||
Workspace concept has been removed. All permission checks pass by default.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from packages.domain.entities import WorkspaceMemberRole
|
||||
|
||||
class WorkspaceMemberRole:
|
||||
"""Stub enum - workspace concept removed."""
|
||||
OWNER = "owner"
|
||||
ADMIN = "admin"
|
||||
MEMBER = "member"
|
||||
VIEWER = "viewer"
|
||||
|
||||
|
||||
class PermissionChecker:
|
||||
"""权限检查器"""
|
||||
"""Stub permission checker - all checks pass since workspace is removed."""
|
||||
|
||||
def __init__(self, workspace_member_repository):
|
||||
def __init__(self, workspace_member_repository=None):
|
||||
self.workspace_member_repository = workspace_member_repository
|
||||
|
||||
def check_workspace_access(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
检查用户是否可以访问工作空间
|
||||
def check_workspace_access(self, workspace_id, user_id):
|
||||
return True, "owner"
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
def check_is_owner(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
Returns:
|
||||
(是否有权限, 用户角色)
|
||||
"""
|
||||
member = self.workspace_member_repository.find_by_workspace_and_user(
|
||||
workspace_id,
|
||||
user_id,
|
||||
)
|
||||
def check_is_admin_or_owner(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
if not member:
|
||||
return False, None
|
||||
def check_can_manage_members(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
return True, member.role
|
||||
def check_can_edit_workspace(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
def check_is_owner(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否是工作空间 Owner
|
||||
def check_can_create_project(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
def check_can_edit_project(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
Returns:
|
||||
是否是 Owner
|
||||
"""
|
||||
member = self.workspace_member_repository.find_by_workspace_and_user(
|
||||
workspace_id,
|
||||
user_id,
|
||||
)
|
||||
def check_can_delete_project(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
return member is not None and member.role == WorkspaceMemberRole.OWNER
|
||||
|
||||
def check_is_admin_or_owner(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否是工作空间 Admin 或 Owner
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否是 Admin 或 Owner
|
||||
"""
|
||||
member = self.workspace_member_repository.find_by_workspace_and_user(
|
||||
workspace_id,
|
||||
user_id,
|
||||
)
|
||||
|
||||
return member is not None and member.role in [
|
||||
WorkspaceMemberRole.OWNER,
|
||||
WorkspaceMemberRole.ADMIN,
|
||||
]
|
||||
|
||||
def check_can_manage_members(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否可以管理成员(邀请、移除、修改角色)
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否可以管理成员
|
||||
"""
|
||||
return self.check_is_admin_or_owner(workspace_id, user_id)
|
||||
|
||||
def check_can_edit_workspace(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否可以编辑工作空间(修改名称、设置等)
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否可以编辑工作空间
|
||||
"""
|
||||
return self.check_is_admin_or_owner(workspace_id, user_id)
|
||||
|
||||
def check_can_create_project(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否可以创建项目
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否可以创建项目
|
||||
"""
|
||||
member = self.workspace_member_repository.find_by_workspace_and_user(
|
||||
workspace_id,
|
||||
user_id,
|
||||
)
|
||||
|
||||
# Owner, Admin, Member 可以创建项目,Viewer 不可以
|
||||
return member is not None and member.role in [
|
||||
WorkspaceMemberRole.OWNER,
|
||||
WorkspaceMemberRole.ADMIN,
|
||||
WorkspaceMemberRole.MEMBER,
|
||||
]
|
||||
|
||||
def check_can_edit_project(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否可以编辑项目
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否可以编辑项目
|
||||
"""
|
||||
# 与创建项目权限相同
|
||||
return self.check_can_create_project(workspace_id, user_id)
|
||||
|
||||
def check_can_delete_project(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否可以删除项目
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否可以删除项目
|
||||
"""
|
||||
# 只有 Owner 和 Admin 可以删除项目
|
||||
return self.check_is_admin_or_owner(workspace_id, user_id)
|
||||
|
||||
def check_can_view_workspace(
|
||||
self,
|
||||
workspace_id: str,
|
||||
user_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
检查用户是否可以查看工作空间
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
user_id: 用户 ID
|
||||
|
||||
Returns:
|
||||
是否可以查看工作空间
|
||||
"""
|
||||
has_access, _ = self.check_workspace_access(workspace_id, user_id)
|
||||
return has_access
|
||||
def check_can_view_workspace(self, workspace_id, user_id):
|
||||
return True
|
||||
|
||||
|
||||
# 权限级别定义
|
||||
class Permission:
|
||||
"""权限常量"""
|
||||
|
||||
# 工作空间权限
|
||||
WORKSPACE_VIEW = "workspace:view"
|
||||
WORKSPACE_EDIT = "workspace:edit"
|
||||
WORKSPACE_DELETE = "workspace:delete"
|
||||
WORKSPACE_MANAGE_SUBSCRIPTION = "workspace:manage_subscription"
|
||||
|
||||
# 成员权限
|
||||
MEMBER_VIEW = "member:view"
|
||||
MEMBER_INVITE = "member:invite"
|
||||
MEMBER_REMOVE = "member:remove"
|
||||
MEMBER_UPDATE_ROLE = "member:update_role"
|
||||
|
||||
# 项目权限
|
||||
PROJECT_VIEW = "project:view"
|
||||
PROJECT_CREATE = "project:create"
|
||||
PROJECT_EDIT = "project:edit"
|
||||
PROJECT_DELETE = "project:delete"
|
||||
|
||||
# 资产权限
|
||||
ASSET_VIEW = "asset:view"
|
||||
ASSET_UPLOAD = "asset:upload"
|
||||
ASSET_EDIT = "asset:edit"
|
||||
ASSET_DELETE = "asset:delete"
|
||||
|
||||
|
||||
# 角色权限映射
|
||||
ROLE_PERMISSIONS = {
|
||||
WorkspaceMemberRole.OWNER: [
|
||||
# 所有权限
|
||||
Permission.WORKSPACE_VIEW,
|
||||
Permission.WORKSPACE_EDIT,
|
||||
Permission.WORKSPACE_DELETE,
|
||||
Permission.WORKSPACE_MANAGE_SUBSCRIPTION,
|
||||
Permission.MEMBER_VIEW,
|
||||
Permission.MEMBER_INVITE,
|
||||
Permission.MEMBER_REMOVE,
|
||||
Permission.MEMBER_UPDATE_ROLE,
|
||||
Permission.PROJECT_VIEW,
|
||||
Permission.PROJECT_CREATE,
|
||||
Permission.PROJECT_EDIT,
|
||||
Permission.PROJECT_DELETE,
|
||||
Permission.ASSET_VIEW,
|
||||
Permission.ASSET_UPLOAD,
|
||||
Permission.ASSET_EDIT,
|
||||
Permission.ASSET_DELETE,
|
||||
],
|
||||
WorkspaceMemberRole.ADMIN: [
|
||||
Permission.WORKSPACE_VIEW,
|
||||
Permission.WORKSPACE_EDIT,
|
||||
Permission.MEMBER_VIEW,
|
||||
Permission.MEMBER_INVITE,
|
||||
Permission.MEMBER_REMOVE,
|
||||
Permission.MEMBER_UPDATE_ROLE,
|
||||
Permission.PROJECT_VIEW,
|
||||
Permission.PROJECT_CREATE,
|
||||
Permission.PROJECT_EDIT,
|
||||
Permission.PROJECT_DELETE,
|
||||
Permission.ASSET_VIEW,
|
||||
Permission.ASSET_UPLOAD,
|
||||
Permission.ASSET_EDIT,
|
||||
Permission.ASSET_DELETE,
|
||||
],
|
||||
WorkspaceMemberRole.MEMBER: [
|
||||
Permission.WORKSPACE_VIEW,
|
||||
Permission.MEMBER_VIEW,
|
||||
Permission.PROJECT_VIEW,
|
||||
Permission.PROJECT_CREATE,
|
||||
Permission.PROJECT_EDIT,
|
||||
Permission.ASSET_VIEW,
|
||||
Permission.ASSET_UPLOAD,
|
||||
Permission.ASSET_EDIT,
|
||||
Permission.ASSET_DELETE,
|
||||
],
|
||||
WorkspaceMemberRole.VIEWER: [
|
||||
Permission.WORKSPACE_VIEW,
|
||||
Permission.MEMBER_VIEW,
|
||||
Permission.PROJECT_VIEW,
|
||||
Permission.ASSET_VIEW,
|
||||
],
|
||||
}
|
||||
ROLE_PERMISSIONS = {}
|
||||
|
||||
|
||||
def has_permission(role: str, permission: str) -> bool:
|
||||
"""
|
||||
检查角色是否有指定权限
|
||||
|
||||
Args:
|
||||
role: 用户角色
|
||||
permission: 权限标识
|
||||
|
||||
Returns:
|
||||
是否有权限
|
||||
"""
|
||||
permissions = ROLE_PERMISSIONS.get(role, [])
|
||||
return permission in permissions
|
||||
def has_permission(role, permission):
|
||||
return True
|
||||
|
||||
+18
-151
@@ -1,181 +1,48 @@
|
||||
"""
|
||||
配额检查服务
|
||||
用于检查工作空间是否超出配额限制
|
||||
Quota checker - stub implementation.
|
||||
Workspace concept removed. All quota checks pass by default.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class QuotaChecker:
|
||||
"""配额检查器"""
|
||||
"""Stub quota checker - all checks pass since workspace is removed."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
workspace_repository,
|
||||
project_repository,
|
||||
):
|
||||
def __init__(self, workspace_repository=None, project_repository=None):
|
||||
self.workspace_repository = workspace_repository
|
||||
self.project_repository = project_repository
|
||||
|
||||
def check_can_create_project(
|
||||
self,
|
||||
workspace_id: str,
|
||||
) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
检查是否可以创建项目
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
|
||||
Returns:
|
||||
(是否可以, 错误信息)
|
||||
"""
|
||||
workspace = self.workspace_repository.find_by_id(workspace_id)
|
||||
if not workspace:
|
||||
return False, "Workspace not found"
|
||||
|
||||
# 获取当前项目数量
|
||||
current_count = self.project_repository.count_by_workspace(workspace_id)
|
||||
|
||||
# 检查是否超出配额(999999 表示无限)
|
||||
if workspace.max_projects != 999999 and current_count >= workspace.max_projects:
|
||||
return (
|
||||
False,
|
||||
f"Project limit reached ({workspace.max_projects}). Upgrade your plan to create more projects.",
|
||||
)
|
||||
|
||||
def check_can_create_project(self, workspace_id=None):
|
||||
return True, None
|
||||
|
||||
def check_storage_available(
|
||||
self,
|
||||
workspace_id: str,
|
||||
additional_gb: float,
|
||||
) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
检查存储空间是否足够
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
additional_gb: 需要的额外存储空间(GB)
|
||||
|
||||
Returns:
|
||||
(是否可以, 错误信息)
|
||||
"""
|
||||
workspace = self.workspace_repository.find_by_id(workspace_id)
|
||||
if not workspace:
|
||||
return False, "Workspace not found"
|
||||
|
||||
# 检查存储空间
|
||||
new_usage = workspace.used_storage_gb + additional_gb
|
||||
|
||||
if new_usage > workspace.max_storage_gb:
|
||||
remaining = workspace.max_storage_gb - workspace.used_storage_gb
|
||||
return (
|
||||
False,
|
||||
f"Storage limit exceeded. Available: {remaining:.2f}GB, Required: {additional_gb:.2f}GB. Upgrade your plan for more storage.",
|
||||
)
|
||||
|
||||
def check_storage_available(self, workspace_id=None, additional_gb=0):
|
||||
return True, None
|
||||
|
||||
def get_quota_status(self, workspace_id: str) -> dict:
|
||||
"""
|
||||
获取配额使用状态
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
|
||||
Returns:
|
||||
配额状态信息
|
||||
"""
|
||||
workspace = self.workspace_repository.find_by_id(workspace_id)
|
||||
if not workspace:
|
||||
return None
|
||||
|
||||
# 获取项目数量
|
||||
project_count = self.project_repository.count_by_workspace(workspace_id)
|
||||
|
||||
# 计算使用率
|
||||
project_usage_percent = (
|
||||
(project_count / workspace.max_projects * 100) if workspace.max_projects != 999999 else 0 # 无限制
|
||||
)
|
||||
|
||||
storage_usage_percent = (
|
||||
(workspace.used_storage_gb / workspace.max_storage_gb * 100) if workspace.max_storage_gb > 0 else 0
|
||||
)
|
||||
|
||||
def get_quota_status(self, workspace_id=None):
|
||||
return {
|
||||
"workspace_id": workspace.id,
|
||||
"subscription_plan": workspace.subscription_plan,
|
||||
"projects": {
|
||||
"used": project_count,
|
||||
"limit": workspace.max_projects,
|
||||
"unlimited": workspace.max_projects == 999999,
|
||||
"usage_percent": project_usage_percent,
|
||||
},
|
||||
"storage": {
|
||||
"used_gb": workspace.used_storage_gb,
|
||||
"limit_gb": workspace.max_storage_gb,
|
||||
"remaining_gb": workspace.max_storage_gb - workspace.used_storage_gb,
|
||||
"usage_percent": storage_usage_percent,
|
||||
},
|
||||
"workspace_id": workspace_id or "",
|
||||
"subscription_plan": "unlimited",
|
||||
"projects": {"used": 0, "limit": 999999, "unlimited": True, "usage_percent": 0},
|
||||
"storage": {"used_gb": 0, "limit_gb": 999999, "remaining_gb": 999999, "usage_percent": 0},
|
||||
}
|
||||
|
||||
def update_storage_usage(
|
||||
self,
|
||||
workspace_id: str,
|
||||
delta_gb: float,
|
||||
) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
更新存储使用量
|
||||
|
||||
Args:
|
||||
workspace_id: 工作空间 ID
|
||||
delta_gb: 变化量(正数为增加,负数为减少)
|
||||
|
||||
Returns:
|
||||
(是否成功, 错误信息)
|
||||
"""
|
||||
workspace = self.workspace_repository.find_by_id(workspace_id)
|
||||
if not workspace:
|
||||
return False, "Workspace not found"
|
||||
|
||||
# 更新使用量
|
||||
new_usage = workspace.used_storage_gb + delta_gb
|
||||
|
||||
# 不能为负数
|
||||
if new_usage < 0:
|
||||
new_usage = 0
|
||||
|
||||
workspace.used_storage_gb = new_usage
|
||||
self.workspace_repository.save(workspace)
|
||||
|
||||
def update_storage_usage(self, workspace_id=None, delta_gb=0):
|
||||
return True, None
|
||||
|
||||
|
||||
class QuotaWarningLevel:
|
||||
"""配额警告级别"""
|
||||
|
||||
NORMAL = "normal" # <80%
|
||||
WARNING = "warning" # 80-90%
|
||||
CRITICAL = "critical" # 90-100%
|
||||
EXCEEDED = "exceeded" # >100%
|
||||
NORMAL = "normal"
|
||||
WARNING = "warning"
|
||||
CRITICAL = "critical"
|
||||
EXCEEDED = "exceeded"
|
||||
|
||||
|
||||
def get_warning_level(usage_percent: float) -> str:
|
||||
"""
|
||||
根据使用率获取警告级别
|
||||
|
||||
Args:
|
||||
usage_percent: 使用率(0-100)
|
||||
|
||||
Returns:
|
||||
警告级别
|
||||
"""
|
||||
def get_warning_level(usage_percent):
|
||||
if usage_percent >= 100:
|
||||
return QuotaWarningLevel.EXCEEDED
|
||||
elif usage_percent >= 90:
|
||||
return QuotaWarningLevel.CRITICAL
|
||||
elif usage_percent >= 80:
|
||||
return QuotaWarningLevel.WARNING
|
||||
else:
|
||||
return QuotaWarningLevel.NORMAL
|
||||
return QuotaWarningLevel.NORMAL
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Stub port for workspace invitation repository - workspace concept removed."""
|
||||
from __future__ import annotations
|
||||
from typing import Protocol
|
||||
|
||||
class WorkspaceInvitationRepository(Protocol):
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Stub port for workspace member repository - workspace concept removed."""
|
||||
from __future__ import annotations
|
||||
from typing import Protocol
|
||||
|
||||
class WorkspaceMemberRepository(Protocol):
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Stub port for workspace repository - workspace concept removed."""
|
||||
from __future__ import annotations
|
||||
from typing import Protocol
|
||||
|
||||
class WorkspaceRepository(Protocol):
|
||||
pass
|
||||
+1
-1
@@ -8,7 +8,7 @@ email-validator==2.2.0
|
||||
pydantic-settings==2.6.0
|
||||
|
||||
# 数据库
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg2-binary==2.9.10
|
||||
psycopg[binary]==3.1.18
|
||||
sqlalchemy==2.0.35
|
||||
alembic==1.13.3
|
||||
|
||||
Reference in New Issue
Block a user