"""v2.0: Add role system, feedback, tariffs, backlog category Revision ID: 002 Revises: 001 Create Date: 2026-05-11 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql 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: # ── users: add v2.0 role system ── op.add_column("users", sa.Column("role", sa.String(20), server_default=sa.text("'user'"), nullable=False, index=True)) op.add_column("users", sa.Column("is_owner", sa.Boolean(), server_default=sa.text("false"), nullable=False)) op.add_column("users", sa.Column("permissions", postgresql.JSONB, nullable=True)) op.add_column("users", sa.Column("accepted_terms_at", sa.DateTime(timezone=True), nullable=True)) op.add_column("users", sa.Column("accepted_terms_version", sa.String(20), nullable=True)) # ── agent_configs: add description ── op.add_column("agent_configs", sa.Column("description", sa.Text(), nullable=True, server_default=sa.text("''"))) # ── backlog_tasks: add category ── op.add_column("backlog_tasks", sa.Column("category", sa.String(50), server_default=sa.text("'general'"), nullable=False, index=True)) # ── feedback ── op.create_table( "feedback", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=True, index=True), sa.Column("text", sa.Text(), nullable=False), sa.Column("page_url", sa.String(512), nullable=True), sa.Column("status", sa.String(20), nullable=False, server_default=sa.text("'new'"), index=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()), ) op.create_foreign_key( "fk_feedback_user_id", "feedback", "users", ["user_id"], ["id"], ondelete="SET NULL", ) # ── tariff_plans ── op.create_table( "tariff_plans", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("name", sa.String(100), nullable=False), sa.Column("code", sa.String(50), unique=True, nullable=False, index=True), sa.Column("description", sa.Text(), nullable=True), sa.Column("price_monthly", sa.Numeric(10, 2), nullable=False, server_default=sa.text("0")), sa.Column("price_yearly", sa.Numeric(10, 2), nullable=True), sa.Column("features", postgresql.JSONB, nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.text("true")), sa.Column("sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()), ) # ── user_subscriptions ── op.create_table( "user_subscriptions", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True), sa.Column("user_id", postgresql.UUID(as_uuid=True), unique=True, nullable=False, index=True), sa.Column("plan_id", postgresql.UUID(as_uuid=True), nullable=False), sa.Column("status", sa.String(20), nullable=False, server_default=sa.text("'active'"), index=True), sa.Column("current_period_start", sa.DateTime(timezone=True), nullable=True), sa.Column("current_period_end", sa.DateTime(timezone=True), nullable=True), sa.Column("canceled_at", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()), ) op.create_foreign_key( "fk_user_subscriptions_user_id", "user_subscriptions", "users", ["user_id"], ["id"], ondelete="CASCADE", ) op.create_foreign_key( "fk_user_subscriptions_plan_id", "user_subscriptions", "tariff_plans", ["plan_id"], ["id"], ondelete="RESTRICT", ) def downgrade() -> None: op.drop_table("user_subscriptions") op.drop_table("tariff_plans") op.drop_table("feedback") op.drop_column("backlog_tasks", "category") op.drop_column("agent_configs", "description") op.drop_column("users", "accepted_terms_version") op.drop_column("users", "accepted_terms_at") op.drop_column("users", "permissions") op.drop_column("users", "is_owner") op.drop_column("users", "role")