chore: remove redundant .github/workflows (using Gitea Actions)

This commit is contained in:
CI Bot
2026-06-27 00:41:09 +08:00
58 changed files with 785 additions and 3208 deletions
@@ -14,7 +14,6 @@ class SQLAlchemyAssetLibraryRepository:
return None
return AssetLibrary(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
name=model.name,
kind=AssetLibraryKind(model.kind),
@@ -24,15 +23,16 @@ class SQLAlchemyAssetLibraryRepository:
updated_at=model.updated_at,
)
def list_by_project(self, project_id: str, kind: AssetLibraryKind | None = None) -> list[AssetLibrary]:
query = self.session.query(AssetLibraryModel).filter(AssetLibraryModel.project_id == project_id)
if kind is not None:
query = query.filter(AssetLibraryModel.kind == kind.value)
models = query.all()
def find_by_id(self, library_id: str) -> AssetLibrary | None:
return self.get(library_id)
def find_by_project(self, project_id: str) -> list[AssetLibrary]:
models = self.session.query(AssetLibraryModel).filter(
AssetLibraryModel.project_id == project_id
).all()
return [
AssetLibrary(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
name=model.name,
kind=AssetLibraryKind(model.kind),
@@ -47,7 +47,6 @@ class SQLAlchemyAssetLibraryRepository:
def create(self, library: AssetLibrary) -> AssetLibrary:
model = AssetLibraryModel(
id=library.id,
workspace_id=library.workspace_id,
project_id=library.project_id,
name=library.name,
kind=library.kind.value,
@@ -59,3 +58,45 @@ class SQLAlchemyAssetLibraryRepository:
self.session.add(model)
self.session.commit()
return library
def update(self, library: AssetLibrary) -> AssetLibrary:
model = self.session.query(AssetLibraryModel).filter(
AssetLibraryModel.id == library.id
).first()
if model:
model.project_id = library.project_id
model.name = library.name
model.kind = library.kind.value
model.asset_count = library.asset_count
model.total_size = library.total_size
model.updated_at = library.updated_at
self.session.commit()
return library
def delete(self, library_id: str) -> bool:
model = self.session.query(AssetLibraryModel).filter(
AssetLibraryModel.id == library_id
).first()
if model:
self.session.delete(model)
self.session.commit()
return True
return False
async def increment_asset_count(self, library_id: str, size_delta: int) -> None:
model = self.session.query(AssetLibraryModel).filter(
AssetLibraryModel.id == library_id
).first()
if model:
model.asset_count = (model.asset_count or 0) + 1
model.total_size = (model.total_size or 0) + size_delta
self.session.commit()
async def decrement_asset_count(self, library_id: str, size_delta: int) -> None:
model = self.session.query(AssetLibraryModel).filter(
AssetLibraryModel.id == library_id
).first()
if model:
model.asset_count = max(0, (model.asset_count or 0) - 1)
model.total_size = max(0, (model.total_size or 0) - size_delta)
self.session.commit()
@@ -11,21 +11,41 @@ class SQLAlchemyAssetRepository:
def __init__(self, session: Session):
self.session = session
def list_by_library(self, library_id: str) -> list[Asset]:
models = self.session.query(AssetModel).filter(AssetModel.asset_library_id == library_id).all()
async def find_by_library(
self,
library_id: str,
skip: int = 0,
limit: int = 100,
) -> list[Asset]:
models = self.session.query(AssetModel).filter(
AssetModel.asset_library_id == library_id
).offset(skip).limit(limit).all()
return [self._to_domain(model) for model in models]
def get(self, asset_id: str) -> Asset | None:
async def find_by_project(
self,
project_id: str,
skip: int = 0,
limit: int = 100,
) -> list[Asset]:
models = self.session.query(AssetModel).filter(
AssetModel.project_id == project_id
).offset(skip).limit(limit).all()
return [self._to_domain(model) for model in models]
async def find_by_id(self, asset_id: str) -> Asset | None:
model = self.session.query(AssetModel).filter(AssetModel.id == asset_id).first()
if model is None:
return None
return self._to_domain(model)
def create(self, asset: Asset) -> Asset:
def get(self, asset_id: str) -> Asset | None:
return self.find_by_id(asset_id)
async def create(self, asset: Asset) -> Asset:
now = datetime.now(timezone.utc)
model = AssetModel(
id=asset.id,
workspace_id=asset.workspace_id,
project_id=asset.project_id,
asset_library_id=asset.library_id,
name=asset.name,
@@ -50,7 +70,7 @@ class SQLAlchemyAssetRepository:
self.session.commit()
return asset
def update(self, asset: Asset) -> Asset:
async def update(self, asset: Asset) -> Asset:
model = self.session.query(AssetModel).filter(AssetModel.id == asset.id).first()
if model is None:
raise ValueError(f"Asset {asset.id} not found")
@@ -72,6 +92,19 @@ class SQLAlchemyAssetRepository:
self.session.commit()
return asset
async def delete(self, asset_id: str) -> bool:
model = self.session.query(AssetModel).filter(AssetModel.id == asset_id).first()
if model:
self.session.delete(model)
self.session.commit()
return True
return False
async def count_by_project(self, project_id: str) -> int:
return self.session.query(AssetModel).filter(
AssetModel.project_id == project_id
).count()
def _to_domain(self, model: AssetModel) -> Asset:
metadata = {}
if model.classification_result:
@@ -88,7 +121,6 @@ class SQLAlchemyAssetRepository:
}.get(mime_type, mime_type)
return Asset(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
library_id=model.asset_library_id,
name=model.name,
@@ -1,7 +1,7 @@
from sqlalchemy.orm import Session
from packages.adapters.sqlalchemy_impl.models import ClassificationJobModel
from packages.domain import ClassificationJob, ClassificationJobStatus
from packages.domain import ClassificationJob
class SQLAlchemyClassificationJobRepository:
@@ -11,10 +11,9 @@ class SQLAlchemyClassificationJobRepository:
def create(self, job: ClassificationJob) -> ClassificationJob:
model = ClassificationJobModel(
id=job.id,
workspace_id=job.workspace_id,
project_id=job.project_id,
asset_id=job.asset_id,
status=job.status.value,
status=job.status,
classification=job.classification,
confidence=job.confidence,
error_message=job.error_message,
@@ -31,10 +30,9 @@ class SQLAlchemyClassificationJobRepository:
return None
return ClassificationJob(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
asset_id=model.asset_id,
status=ClassificationJobStatus(model.status),
status=model.status,
classification=model.classification,
confidence=model.confidence,
error_message=model.error_message,
@@ -46,7 +44,7 @@ class SQLAlchemyClassificationJobRepository:
model = self.session.query(ClassificationJobModel).filter(ClassificationJobModel.id == job.id).first()
if model is None:
raise ValueError(f"ClassificationJob {job.id} not found")
model.status = job.status.value
model.status = job.status
model.classification = job.classification
model.confidence = job.confidence
model.error_message = job.error_message
@@ -13,7 +13,6 @@ class SQLAlchemyGeneratedVideoRepository:
def create(self, video: GeneratedVideo) -> GeneratedVideo:
model = GeneratedVideoModel(
id=video.id,
workspace_id=video.workspace_id,
project_id=video.project_id,
generation_task_id=video.generation_task_id,
name=video.name,
@@ -43,7 +42,6 @@ class SQLAlchemyGeneratedVideoRepository:
return None
return GeneratedVideo(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
generation_task_id=model.generation_task_id,
name=model.name,
@@ -1,7 +1,7 @@
from sqlalchemy.orm import Session
from packages.adapters.sqlalchemy_impl.models import GenerationTaskModel
from packages.domain import GenerationTask, GenerationTaskStatus
from packages.domain import GenerationTask
class SQLAlchemyGenerationTaskRepository:
@@ -11,13 +11,12 @@ class SQLAlchemyGenerationTaskRepository:
def create(self, task: GenerationTask) -> GenerationTask:
model = GenerationTaskModel(
id=task.id,
workspace_id=task.workspace_id,
project_id=task.project_id,
strategy_id=task.strategy_id,
asset_library_id=task.asset_library_id,
voice_library_id=task.voice_library_id,
edit_plan_id=task.edit_plan_id,
status=task.status.value,
status=task.status,
progress=task.progress,
result_count=task.result_count,
error_message=task.error_message,
@@ -36,13 +35,12 @@ class SQLAlchemyGenerationTaskRepository:
return None
return GenerationTask(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
strategy_id=model.strategy_id,
asset_library_id=model.asset_library_id,
voice_library_id=model.voice_library_id,
edit_plan_id=getattr(model, "edit_plan_id", "") or "",
status=GenerationTaskStatus(model.status),
status=model.status,
progress=model.progress,
result_count=int(model.result_count or 0),
error_message=model.error_message,
@@ -62,7 +60,7 @@ class SQLAlchemyGenerationTaskRepository:
raise ValueError(f"GenerationTask {task.id} not found")
model.voice_library_id = task.voice_library_id
model.edit_plan_id = task.edit_plan_id
model.status = task.status.value
model.status = task.status
model.progress = task.progress
model.result_count = task.result_count
model.error_message = task.error_message
@@ -11,7 +11,6 @@ class SQLAlchemyIngestJobRepository:
def create(self, job: IngestJob) -> IngestJob:
model = IngestJobModel(
id=job.id,
workspace_id=job.workspace_id,
project_id=job.project_id,
library_id=job.library_id,
storage_key=job.storage_key,
@@ -31,7 +30,6 @@ class SQLAlchemyIngestJobRepository:
return None
return IngestJob(
id=model.id,
workspace_id=model.workspace_id,
project_id=model.project_id,
library_id=model.library_id,
storage_key=model.storage_key,
+5 -50
View File
@@ -1,6 +1,6 @@
from datetime import datetime, timezone
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, Text, UniqueConstraint, create_engine
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, Text, JSON, UniqueConstraint, create_engine
from sqlalchemy.orm import declarative_base
Base = declarative_base()
@@ -20,58 +20,25 @@ class UserModel(Base):
password_reset_expires_at = Column(DateTime, nullable=True)
last_login_at = Column(DateTime, nullable=True)
last_login_ip = Column(String(50), nullable=True)
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
class WorkspaceModel(Base):
__tablename__ = "workspaces"
id = Column(String(36), primary_key=True)
name = Column(String(100), nullable=False)
owner_user_id = Column(String(36), nullable=False, index=True)
# 订阅相关字段 (移到 User 级别)
subscription_plan = Column(String(20), nullable=False, default="free")
subscription_status = Column(String(20), nullable=False, default="active")
subscription_expires_at = Column(DateTime, nullable=True)
# 配额限制 (移到 User 级别)
max_projects = Column(Float, nullable=False, default=3)
max_storage_gb = Column(Float, nullable=False, default=10)
used_storage_gb = Column(Float, nullable=False, default=0.0)
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
class WorkspaceMemberModel(Base):
__tablename__ = "workspace_members"
__table_args__ = (UniqueConstraint("workspace_id", "user_id", name="uq_workspace_members_workspace_user"),)
id = Column(String(36), primary_key=True)
workspace_id = Column(String(36), nullable=False, index=True)
user_id = Column(String(36), nullable=False, index=True)
role = Column(String(20), nullable=False)
invited_by = Column(String(36), nullable=True)
joined_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
class WorkspaceInvitationModel(Base):
__tablename__ = "workspace_invitations"
id = Column(String(36), primary_key=True)
workspace_id = Column(String(36), nullable=False, index=True)
inviter_user_id = Column(String(36), nullable=False)
invitee_email = Column(String(255), nullable=False, index=True)
role = Column(String(20), nullable=False)
invitation_token = Column(String(255), nullable=False, unique=True, index=True)
status = Column(String(20), nullable=False, default="pending", index=True)
expires_at = Column(DateTime, nullable=True)
accepted_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
class ProjectModel(Base):
__tablename__ = "projects"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
owner_user_id = Column(String(32), nullable=False, index=True)
name = Column(String(100), nullable=False)
description = Column(Text, nullable=False, default="")
shared_users = Column(JSON, nullable=False, default=list) # 被共享的用户 ID 列表
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
@@ -79,7 +46,6 @@ class AssetLibraryModel(Base):
__tablename__ = "asset_libraries"
id = Column(String(36), primary_key=True)
workspace_id = Column(String(36), nullable=False, index=True)
project_id = Column(String(36), nullable=True, index=True)
name = Column(String(255), nullable=False)
kind = Column(String(20), nullable=False, index=True)
@@ -93,7 +59,6 @@ class AssetModel(Base):
__tablename__ = "assets"
id = Column(String(36), primary_key=True)
workspace_id = Column(String(36), nullable=False, index=True)
project_id = Column(String(36), nullable=False, index=True)
asset_library_id = Column(String(36), nullable=False, index=True)
name = Column(String(500), nullable=False)
@@ -119,7 +84,6 @@ class ProjectTitleModel(Base):
__tablename__ = "project_titles"
id = Column(String(36), primary_key=True)
workspace_id = Column(String(36), nullable=False, index=True)
project_id = Column(String(36), nullable=False, index=True)
text = Column(String(200), nullable=False)
category = Column(String(50), nullable=False, default="default", index=True)
@@ -135,7 +99,6 @@ class EditTemplateModel(Base):
__tablename__ = "edit_templates"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
name = Column(String(120), nullable=False)
description = Column(Text, nullable=False, default="")
@@ -151,7 +114,6 @@ class EditPlanModel(Base):
__tablename__ = "edit_plans"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
template_id = Column(String(32), nullable=False, index=True)
asset_library_id = Column(String(32), nullable=False, index=True)
@@ -181,7 +143,6 @@ class IngestJobModel(Base):
__tablename__ = "ingest_jobs"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
library_id = Column(String(32), nullable=False, index=True)
storage_key = Column(String(255), nullable=False)
@@ -196,7 +157,6 @@ class ClassificationJobModel(Base):
__tablename__ = "classification_jobs"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
asset_id = Column(String(32), nullable=False, index=True)
status = Column(String(20), nullable=False, default="pending")
@@ -211,7 +171,6 @@ class GenerationTaskModel(Base):
__tablename__ = "generation_tasks"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
strategy_id = Column(String(32), nullable=False, default="")
asset_library_id = Column(String(32), nullable=False, index=True)
@@ -232,7 +191,6 @@ class GeneratedVideoModel(Base):
__tablename__ = "generated_videos"
id = Column(String(32), primary_key=True)
workspace_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
generation_task_id = Column(String(32), nullable=False, index=True)
name = Column(String(255), nullable=False)
@@ -259,7 +217,6 @@ class TaskModel(Base):
id = Column(String(32), primary_key=True)
project_id = Column(String(32), nullable=False, index=True)
workspace_id = Column(String(32), nullable=False, index=True)
name = Column(String(200), nullable=False)
description = Column(Text, nullable=False, default="")
status = Column(String(20), nullable=False, default="pending", index=True)
@@ -281,7 +238,6 @@ class MilestoneModel(Base):
id = Column(String(32), primary_key=True)
project_id = Column(String(32), nullable=False, index=True)
workspace_id = Column(String(32), nullable=False, index=True)
name = Column(String(200), nullable=False)
description = Column(Text, nullable=False, default="")
target_date = Column(DateTime, nullable=True)
@@ -297,7 +253,6 @@ class TaskIssueModel(Base):
id = Column(String(32), primary_key=True)
task_id = Column(String(32), nullable=False, index=True)
project_id = Column(String(32), nullable=False, index=True)
workspace_id = Column(String(32), nullable=False, index=True)
title = Column(String(200), nullable=False)
description = Column(Text, nullable=False, default="")
resolved = Column(Boolean, nullable=False, default=False)
@@ -11,15 +11,33 @@ class SQLAlchemyProjectRepository:
def _to_entity(self, model: ProjectModel) -> Project:
return Project(
id=model.id,
workspace_id=model.workspace_id,
owner_user_id=model.owner_user_id,
name=model.name,
description=model.description,
shared_users=model.shared_users or [],
created_at=model.created_at,
)
def list_by_workspace(self, workspace_id: str) -> list[Project]:
models = self.session.query(ProjectModel).filter(ProjectModel.workspace_id == workspace_id).all()
return [self._to_entity(model) for model in models]
def save(self, project: Project) -> Project:
"""保存项目(创建或更新)"""
existing = self.session.query(ProjectModel).filter(ProjectModel.id == project.id).first()
if existing:
existing.owner_user_id = project.owner_user_id
existing.name = project.name
existing.description = project.description
existing.shared_users = project.shared_users
else:
model = ProjectModel(
id=project.id,
owner_user_id=project.owner_user_id,
name=project.name,
description=project.description,
shared_users=project.shared_users,
created_at=project.created_at,
)
self.session.add(model)
self.session.commit()
return project
def find_by_id(self, project_id: str) -> Project | None:
model = self.session.query(ProjectModel).filter(ProjectModel.id == project_id).first()
@@ -27,14 +45,36 @@ class SQLAlchemyProjectRepository:
return None
return self._to_entity(model)
def create(self, project: Project) -> Project:
model = ProjectModel(
id=project.id,
workspace_id=project.workspace_id,
name=project.name,
description=project.description,
created_at=project.created_at,
)
self.session.add(model)
def find_by_owner_user_id(self, owner_user_id: str) -> list[Project]:
"""根据所有者用户 ID 查找项目"""
models = self.session.query(ProjectModel).filter(
ProjectModel.owner_user_id == owner_user_id
).all()
return [self._to_entity(model) for model in models]
def find_accessible_projects(self, user_id: str) -> list[Project]:
"""查找用户可访问的所有项目(自己拥有的 + 被共享的)"""
from sqlalchemy import or_
models = self.session.query(ProjectModel).filter(
or_(
ProjectModel.owner_user_id == user_id,
ProjectModel.shared_users.contains([user_id])
)
).all()
return [self._to_entity(model) for model in models]
def count_by_owner(self, owner_user_id: str) -> int:
"""统计用户的项目数量"""
return self.session.query(ProjectModel).filter(
ProjectModel.owner_user_id == owner_user_id
).count()
def delete(self, project_id: str) -> bool:
"""删除项目"""
model = self.session.query(ProjectModel).filter(ProjectModel.id == project_id).first()
if model is None:
return False
self.session.delete(model)
self.session.commit()
return project
return True
@@ -1,79 +0,0 @@
from __future__ import annotations
from sqlalchemy.orm import Session
from packages.adapters.sqlalchemy_impl.models import WorkspaceInvitationModel
from packages.domain.entities import WorkspaceInvitation
from packages.ports.workspace_invitation_repository import WorkspaceInvitationRepository
class SQLAlchemyWorkspaceInvitationRepository(WorkspaceInvitationRepository):
def __init__(self, session: Session):
self.session = session
def save(self, invitation: WorkspaceInvitation) -> None:
model = self.session.get(WorkspaceInvitationModel, invitation.id)
if model is None:
model = WorkspaceInvitationModel(id=invitation.id)
self.session.add(model)
model.workspace_id = invitation.workspace_id
model.inviter_user_id = invitation.inviter_user_id
model.invitee_email = invitation.invitee_email.strip().lower()
model.role = str(invitation.role)
model.invitation_token = invitation.invitation_token
model.status = str(invitation.status)
model.expires_at = invitation.expires_at
model.accepted_at = invitation.accepted_at
model.created_at = invitation.created_at
self.session.commit()
self.session.refresh(model)
def find_by_id(self, invitation_id: str) -> WorkspaceInvitation | None:
return self._to_entity(self.session.get(WorkspaceInvitationModel, invitation_id))
def find_by_token(self, token: str) -> WorkspaceInvitation | None:
model = (
self.session.query(WorkspaceInvitationModel)
.filter(WorkspaceInvitationModel.invitation_token == token)
.first()
)
return self._to_entity(model)
def find_pending_by_workspace_and_email(self, workspace_id: str, email: str) -> WorkspaceInvitation | None:
model = (
self.session.query(WorkspaceInvitationModel)
.filter(
WorkspaceInvitationModel.workspace_id == workspace_id,
WorkspaceInvitationModel.invitee_email == email.strip().lower(),
WorkspaceInvitationModel.status == "pending",
)
.first()
)
return self._to_entity(model)
def delete(self, invitation_id: str) -> bool:
model = self.session.get(WorkspaceInvitationModel, invitation_id)
if model is None:
return False
self.session.delete(model)
self.session.commit()
return True
@staticmethod
def _to_entity(model: WorkspaceInvitationModel | None) -> WorkspaceInvitation | None:
if model is None:
return None
return WorkspaceInvitation(
id=model.id,
workspace_id=model.workspace_id,
inviter_user_id=model.inviter_user_id,
invitee_email=model.invitee_email,
role=model.role,
invitation_token=model.invitation_token,
status=model.status,
expires_at=model.expires_at,
accepted_at=model.accepted_at,
created_at=model.created_at,
)
@@ -1,59 +0,0 @@
from __future__ import annotations
from sqlalchemy.orm import Session
from packages.adapters.sqlalchemy_impl.models import WorkspaceModel
from packages.domain.entities import Workspace
from packages.ports.workspace_repository import WorkspaceRepository
class SQLAlchemyWorkspaceRepository(WorkspaceRepository):
def __init__(self, session: Session):
self.session = session
def save(self, workspace: Workspace) -> None:
model = self.session.get(WorkspaceModel, workspace.id)
if model is None:
model = WorkspaceModel(id=workspace.id)
self.session.add(model)
model.name = workspace.name
model.owner_user_id = workspace.owner_user_id
model.subscription_plan = workspace.subscription_plan
model.subscription_status = workspace.subscription_status
model.subscription_expires_at = workspace.subscription_expires_at
model.max_projects = workspace.max_projects
model.max_storage_gb = workspace.max_storage_gb
model.used_storage_gb = workspace.used_storage_gb
model.created_at = workspace.created_at
self.session.commit()
self.session.refresh(model)
def find_by_id(self, workspace_id: str) -> Workspace | None:
return self._to_entity(self.session.get(WorkspaceModel, workspace_id))
def delete(self, workspace_id: str) -> bool:
model = self.session.get(WorkspaceModel, workspace_id)
if model is None:
return False
self.session.delete(model)
self.session.commit()
return True
@staticmethod
def _to_entity(model: WorkspaceModel | None) -> Workspace | None:
if model is None:
return None
return Workspace(
id=model.id,
name=model.name,
owner_user_id=model.owner_user_id,
subscription_plan=model.subscription_plan,
subscription_status=model.subscription_status,
subscription_expires_at=model.subscription_expires_at,
max_projects=int(model.max_projects),
max_storage_gb=int(model.max_storage_gb),
used_storage_gb=float(model.used_storage_gb),
created_at=model.created_at,
)