Initial commit: VoIdeaAI - voice-first AI idea assistant

This commit is contained in:
2026-05-13 12:51:42 +03:00
commit 688d043dad
421 changed files with 47915 additions and 0 deletions
@@ -0,0 +1,117 @@
"""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")