refactor(db): introduce alembic schema baseline
This commit is contained in:
+8
-1
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from logging.config import fileConfig
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
@@ -12,6 +13,10 @@ from packages.adapters.sqlalchemy_impl.models import Base
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
database_url = os.getenv("DATABASE_URL")
|
||||
if database_url:
|
||||
config.set_main_option("sqlalchemy.url", database_url)
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
@@ -66,7 +71,9 @@ def run_migrations_online() -> None:
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection, target_metadata=target_metadata
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
compare_type=True,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
"""Current SQLAlchemy schema baseline.
|
||||
|
||||
Revision ID: 001
|
||||
Revises:
|
||||
Create Date: 2026-06-21
|
||||
|
||||
This revision represents the current runtime schema defined by
|
||||
packages.adapters.sqlalchemy_impl.models. Existing staging databases should be
|
||||
stamped to this revision after compatibility verification; fresh databases can
|
||||
run this migration normally.
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "001"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"users",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("email", sa.String(length=255), nullable=False),
|
||||
sa.Column("username", sa.String(length=100), nullable=True),
|
||||
sa.Column("display_name", sa.String(length=255), nullable=False),
|
||||
sa.Column("password_hash", sa.String(length=255), nullable=False),
|
||||
sa.Column("email_verified", sa.Boolean(), nullable=False),
|
||||
sa.Column("email_verification_token", sa.String(length=255), nullable=True),
|
||||
sa.Column("password_reset_token", sa.String(length=255), nullable=True),
|
||||
sa.Column("password_reset_expires_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("last_login_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("last_login_ip", sa.String(length=50), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
|
||||
op.create_index(op.f("ix_users_username"), "users", ["username"], unique=True)
|
||||
|
||||
op.create_table(
|
||||
"projects",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("name", sa.String(length=100), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_projects_workspace_id"), "projects", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"asset_libraries",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=36), nullable=True),
|
||||
sa.Column("name", sa.String(length=255), nullable=False),
|
||||
sa.Column("kind", sa.String(length=20), nullable=False),
|
||||
sa.Column("asset_count", sa.Float(), nullable=False),
|
||||
sa.Column("total_size", sa.Float(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_asset_libraries_kind"), "asset_libraries", ["kind"], unique=False)
|
||||
op.create_index(op.f("ix_asset_libraries_project_id"), "asset_libraries", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_asset_libraries_workspace_id"), "asset_libraries", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"assets",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("asset_library_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("name", sa.String(length=500), nullable=False),
|
||||
sa.Column("file_type", sa.String(length=20), nullable=False),
|
||||
sa.Column("file_size", sa.Float(), nullable=False),
|
||||
sa.Column("file_url", sa.String(length=1000), nullable=False),
|
||||
sa.Column("thumbnail_url", sa.String(length=1000), nullable=True),
|
||||
sa.Column("duration", sa.Float(), nullable=True),
|
||||
sa.Column("width", sa.Float(), nullable=True),
|
||||
sa.Column("height", sa.Float(), nullable=True),
|
||||
sa.Column("fps", sa.Float(), nullable=True),
|
||||
sa.Column("codec", sa.String(length=50), nullable=True),
|
||||
sa.Column("status", sa.String(length=20), nullable=False),
|
||||
sa.Column("classification_status", sa.String(length=20), nullable=False),
|
||||
sa.Column("classification_result", sa.Text(), nullable=True),
|
||||
sa.Column("quality_score", sa.Float(), nullable=True),
|
||||
sa.Column("uploaded_by_user_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_assets_asset_library_id"), "assets", ["asset_library_id"], unique=False)
|
||||
op.create_index(op.f("ix_assets_classification_status"), "assets", ["classification_status"], unique=False)
|
||||
op.create_index(op.f("ix_assets_created_at"), "assets", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_assets_file_type"), "assets", ["file_type"], unique=False)
|
||||
op.create_index(op.f("ix_assets_project_id"), "assets", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_assets_status"), "assets", ["status"], unique=False)
|
||||
op.create_index(op.f("ix_assets_workspace_id"), "assets", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"ingest_jobs",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("library_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("storage_key", sa.String(length=255), nullable=False),
|
||||
sa.Column("status", sa.String(length=20), nullable=False),
|
||||
sa.Column("error_message", sa.Text(), nullable=False),
|
||||
sa.Column("result_asset_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_ingest_jobs_library_id"), "ingest_jobs", ["library_id"], unique=False)
|
||||
op.create_index(op.f("ix_ingest_jobs_project_id"), "ingest_jobs", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_ingest_jobs_workspace_id"), "ingest_jobs", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"classification_jobs",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("asset_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("status", sa.String(length=20), nullable=False),
|
||||
sa.Column("classification", sa.String(length=50), nullable=False),
|
||||
sa.Column("confidence", sa.Float(), nullable=False),
|
||||
sa.Column("error_message", sa.Text(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_classification_jobs_asset_id"), "classification_jobs", ["asset_id"], unique=False)
|
||||
op.create_index(op.f("ix_classification_jobs_project_id"), "classification_jobs", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_classification_jobs_workspace_id"), "classification_jobs", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"generation_tasks",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("strategy_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("asset_library_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("voice_library_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("status", sa.String(length=20), nullable=False),
|
||||
sa.Column("progress", sa.Float(), nullable=False),
|
||||
sa.Column("result_count", sa.Float(), nullable=False),
|
||||
sa.Column("error_message", sa.Text(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("completed_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("created_by_user_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_generation_tasks_asset_library_id"), "generation_tasks", ["asset_library_id"], unique=False)
|
||||
op.create_index(op.f("ix_generation_tasks_project_id"), "generation_tasks", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_generation_tasks_status"), "generation_tasks", ["status"], unique=False)
|
||||
op.create_index(op.f("ix_generation_tasks_workspace_id"), "generation_tasks", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"generated_videos",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("generation_task_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("name", sa.String(length=255), nullable=False),
|
||||
sa.Column("file_url", sa.String(length=1000), nullable=False),
|
||||
sa.Column("file_size", sa.Float(), nullable=False),
|
||||
sa.Column("duration", sa.Float(), nullable=False),
|
||||
sa.Column("thumbnail_url", sa.String(length=1000), nullable=True),
|
||||
sa.Column("width", sa.Float(), nullable=False),
|
||||
sa.Column("height", sa.Float(), nullable=False),
|
||||
sa.Column("fps", sa.Float(), nullable=False),
|
||||
sa.Column("generated_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_generated_videos_generation_task_id"), "generated_videos", ["generation_task_id"], unique=False)
|
||||
op.create_index(op.f("ix_generated_videos_project_id"), "generated_videos", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_generated_videos_workspace_id"), "generated_videos", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"tasks",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("name", sa.String(length=200), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.Column("status", sa.String(length=20), nullable=False),
|
||||
sa.Column("priority", sa.String(length=20), nullable=False),
|
||||
sa.Column("parent_task_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("assignee_user_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("progress", sa.Float(), nullable=False),
|
||||
sa.Column("planned_start_date", sa.DateTime(), nullable=True),
|
||||
sa.Column("planned_end_date", sa.DateTime(), nullable=True),
|
||||
sa.Column("actual_start_date", sa.DateTime(), nullable=True),
|
||||
sa.Column("actual_end_date", sa.DateTime(), nullable=True),
|
||||
sa.Column("tags_json", sa.Text(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_tasks_parent_task_id"), "tasks", ["parent_task_id"], unique=False)
|
||||
op.create_index(op.f("ix_tasks_project_id"), "tasks", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_tasks_status"), "tasks", ["status"], unique=False)
|
||||
op.create_index(op.f("ix_tasks_workspace_id"), "tasks", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"milestones",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("name", sa.String(length=200), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.Column("target_date", sa.DateTime(), nullable=True),
|
||||
sa.Column("completed", sa.Boolean(), nullable=False),
|
||||
sa.Column("completed_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_milestones_project_id"), "milestones", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_milestones_workspace_id"), "milestones", ["workspace_id"], unique=False)
|
||||
|
||||
op.create_table(
|
||||
"task_issues",
|
||||
sa.Column("id", sa.String(length=32), nullable=False),
|
||||
sa.Column("task_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("project_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("workspace_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("title", sa.String(length=200), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.Column("resolved", sa.Boolean(), nullable=False),
|
||||
sa.Column("resolved_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("created_by_user_id", sa.String(length=32), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_task_issues_project_id"), "task_issues", ["project_id"], unique=False)
|
||||
op.create_index(op.f("ix_task_issues_task_id"), "task_issues", ["task_id"], unique=False)
|
||||
op.create_index(op.f("ix_task_issues_workspace_id"), "task_issues", ["workspace_id"], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("task_issues")
|
||||
op.drop_table("milestones")
|
||||
op.drop_table("tasks")
|
||||
op.drop_table("generated_videos")
|
||||
op.drop_table("generation_tasks")
|
||||
op.drop_table("classification_jobs")
|
||||
op.drop_table("ingest_jobs")
|
||||
op.drop_table("assets")
|
||||
op.drop_table("asset_libraries")
|
||||
op.drop_table("projects")
|
||||
op.drop_table("users")
|
||||
@@ -1,90 +0,0 @@
|
||||
"""Initial schema
|
||||
|
||||
Revision ID: 001
|
||||
Revises:
|
||||
Create Date: 2026-06-15
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '001'
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create projects table
|
||||
op.create_table(
|
||||
'projects',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('name', sa.String(100), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=False, server_default=''),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_projects_workspace_id'), 'projects', ['workspace_id'], unique=False)
|
||||
|
||||
# Create asset_libraries table
|
||||
op.create_table(
|
||||
'asset_libraries',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('project_id', sa.String(32), nullable=False),
|
||||
sa.Column('name', sa.String(100), nullable=False),
|
||||
sa.Column('kind', sa.String(20), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_asset_libraries_workspace_id'), 'asset_libraries', ['workspace_id'], unique=False)
|
||||
op.create_index(op.f('ix_asset_libraries_project_id'), 'asset_libraries', ['project_id'], unique=False)
|
||||
|
||||
# Create assets table
|
||||
op.create_table(
|
||||
'assets',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('project_id', sa.String(32), nullable=False),
|
||||
sa.Column('library_id', sa.String(32), nullable=False),
|
||||
sa.Column('name', sa.String(100), nullable=False),
|
||||
sa.Column('storage_key', sa.String(255), nullable=False),
|
||||
sa.Column('mime_type', sa.String(100), nullable=False),
|
||||
sa.Column('metadata_json', sa.Text(), nullable=False, server_default='{}'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_assets_workspace_id'), 'assets', ['workspace_id'], unique=False)
|
||||
op.create_index(op.f('ix_assets_project_id'), 'assets', ['project_id'], unique=False)
|
||||
op.create_index(op.f('ix_assets_library_id'), 'assets', ['library_id'], unique=False)
|
||||
|
||||
# Create ingest_jobs table
|
||||
op.create_table(
|
||||
'ingest_jobs',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('project_id', sa.String(32), nullable=False),
|
||||
sa.Column('library_id', sa.String(32), nullable=False),
|
||||
sa.Column('storage_key', sa.String(255), nullable=False),
|
||||
sa.Column('status', sa.String(20), nullable=False, server_default='pending'),
|
||||
sa.Column('error_message', sa.Text(), nullable=False, server_default=''),
|
||||
sa.Column('result_asset_id', sa.String(32), nullable=False, server_default=''),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_ingest_jobs_workspace_id'), 'ingest_jobs', ['workspace_id'], unique=False)
|
||||
op.create_index(op.f('ix_ingest_jobs_project_id'), 'ingest_jobs', ['project_id'], unique=False)
|
||||
op.create_index(op.f('ix_ingest_jobs_library_id'), 'ingest_jobs', ['library_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table('ingest_jobs')
|
||||
op.drop_table('assets')
|
||||
op.drop_table('asset_libraries')
|
||||
op.drop_table('projects')
|
||||
@@ -1,91 +0,0 @@
|
||||
"""Add project management tables
|
||||
|
||||
Revision ID: 002
|
||||
Revises: 001
|
||||
Create Date: 2026-06-16
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '002'
|
||||
down_revision: Union[str, None] = '001'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create tasks table
|
||||
op.create_table(
|
||||
'tasks',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('project_id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('name', sa.String(200), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=False, server_default=''),
|
||||
sa.Column('status', sa.String(20), nullable=False, server_default='pending'),
|
||||
sa.Column('priority', sa.String(20), nullable=False, server_default='medium'),
|
||||
sa.Column('parent_task_id', sa.String(32), nullable=False, server_default=''),
|
||||
sa.Column('assignee_user_id', sa.String(32), nullable=False, server_default=''),
|
||||
sa.Column('progress', sa.Float(), nullable=False, server_default='0'),
|
||||
sa.Column('planned_start_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('planned_end_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('actual_start_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('actual_end_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('tags_json', sa.Text(), nullable=False, server_default='[]'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_tasks_project_id'), 'tasks', ['project_id'], unique=False)
|
||||
op.create_index(op.f('ix_tasks_workspace_id'), 'tasks', ['workspace_id'], unique=False)
|
||||
op.create_index(op.f('ix_tasks_parent_task_id'), 'tasks', ['parent_task_id'], unique=False)
|
||||
op.create_index(op.f('ix_tasks_status'), 'tasks', ['status'], unique=False)
|
||||
|
||||
# Create milestones table
|
||||
op.create_table(
|
||||
'milestones',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('project_id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('name', sa.String(200), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=False, server_default=''),
|
||||
sa.Column('target_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('completed', sa.Boolean(), nullable=False, server_default='0'),
|
||||
sa.Column('completed_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_milestones_project_id'), 'milestones', ['project_id'], unique=False)
|
||||
op.create_index(op.f('ix_milestones_workspace_id'), 'milestones', ['workspace_id'], unique=False)
|
||||
|
||||
# Create task_issues table
|
||||
op.create_table(
|
||||
'task_issues',
|
||||
sa.Column('id', sa.String(32), nullable=False),
|
||||
sa.Column('task_id', sa.String(32), nullable=False),
|
||||
sa.Column('project_id', sa.String(32), nullable=False),
|
||||
sa.Column('workspace_id', sa.String(32), nullable=False),
|
||||
sa.Column('title', sa.String(200), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=False, server_default=''),
|
||||
sa.Column('resolved', sa.Boolean(), nullable=False, server_default='0'),
|
||||
sa.Column('resolved_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_by_user_id', sa.String(32), nullable=False, server_default=''),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_task_issues_task_id'), 'task_issues', ['task_id'], unique=False)
|
||||
op.create_index(op.f('ix_task_issues_project_id'), 'task_issues', ['project_id'], unique=False)
|
||||
op.create_index(op.f('ix_task_issues_workspace_id'), 'task_issues', ['workspace_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table('task_issues')
|
||||
op.drop_table('milestones')
|
||||
op.drop_table('tasks')
|
||||
@@ -19,6 +19,7 @@ class Settings(BaseSettings):
|
||||
DATABASE_POOL_TIMEOUT: int = 30
|
||||
DATABASE_POOL_RECYCLE: int = 3600
|
||||
USE_IN_MEMORY_DB: bool = False
|
||||
AUTO_CREATE_SCHEMA: bool = False
|
||||
|
||||
REDIS_URL: str = "redis://localhost:6379/0"
|
||||
REDIS_MAX_CONNECTIONS: int = 50
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@ engine, SessionLocal = build_session_factory(
|
||||
pool_timeout=settings.DATABASE_POOL_TIMEOUT,
|
||||
pool_recycle=settings.DATABASE_POOL_RECYCLE,
|
||||
)
|
||||
initialize_database(engine)
|
||||
if settings.AUTO_CREATE_SCHEMA:
|
||||
initialize_database(engine)
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
|
||||
@@ -14,6 +14,7 @@ class WorkerSettings(BaseSettings):
|
||||
database_max_overflow: int = 40
|
||||
database_pool_timeout: int = 30
|
||||
database_pool_recycle: int = 3600
|
||||
auto_create_schema: bool = False
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
|
||||
@@ -10,4 +10,5 @@ engine, SessionLocal = build_session_factory(
|
||||
pool_timeout=settings.database_pool_timeout,
|
||||
pool_recycle=settings.database_pool_recycle,
|
||||
)
|
||||
initialize_database(engine)
|
||||
if settings.auto_create_schema:
|
||||
initialize_database(engine)
|
||||
|
||||
@@ -8,6 +8,8 @@ RUN python -m pip install --upgrade pip setuptools wheel && \
|
||||
pip install --no-cache-dir --default-timeout=120 --retries 10 -r requirements.txt
|
||||
COPY apps/api /app/apps/api
|
||||
COPY packages /app/packages
|
||||
COPY alembic /app/alembic
|
||||
COPY alembic.ini /app/alembic.ini
|
||||
WORKDIR /app/apps/api
|
||||
EXPOSE 8000
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
|
||||
@@ -36,5 +36,25 @@ export COMPOSE_DOCKER_CLI_BUILD=0
|
||||
|
||||
docker compose build --pull=false api
|
||||
docker compose build --pull=false worker
|
||||
docker compose run --rm --no-deps api sh -c '
|
||||
cd /app &&
|
||||
python - <<"PY"
|
||||
import os
|
||||
from sqlalchemy import create_engine, text
|
||||
url = os.environ["DATABASE_URL"]
|
||||
engine = create_engine(url)
|
||||
with engine.begin() as conn:
|
||||
has_version = conn.execute(text("SELECT to_regclass('public.alembic_version')")).scalar() is not None
|
||||
has_tables = conn.execute(text("SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'public' AND tablename != 'alembic_version'")).scalar()
|
||||
if has_tables and not has_version:
|
||||
raise SystemExit("STAMP_REQUIRED")
|
||||
PY
|
||||
status=$?
|
||||
if [ "$status" -eq 0 ]; then
|
||||
alembic upgrade head
|
||||
else
|
||||
alembic stamp head && alembic upgrade head
|
||||
fi
|
||||
'
|
||||
docker compose up -d api worker web
|
||||
docker compose ps
|
||||
|
||||
@@ -13,5 +13,7 @@ RUN set -eux; \
|
||||
pip install --no-cache-dir --default-timeout=120 --retries 10 -r requirements.txt
|
||||
COPY apps/worker /app/apps/worker
|
||||
COPY packages /app/packages
|
||||
COPY alembic /app/alembic
|
||||
COPY alembic.ini /app/alembic.ini
|
||||
WORKDIR /app/apps/worker
|
||||
CMD ["celery", "-A", "worker_app.celery_app.celery_app", "worker", "--loglevel=info"]
|
||||
|
||||
@@ -11,6 +11,7 @@ pydantic-settings==2.6.0
|
||||
psycopg2-binary==2.9.9
|
||||
psycopg[binary]==3.1.18
|
||||
sqlalchemy==2.0.35
|
||||
alembic==1.13.3
|
||||
|
||||
# 认证
|
||||
pyjwt==2.9.0
|
||||
|
||||
Reference in New Issue
Block a user