"""Initial Hub schema with clients, instances, and usage samples. Revision ID: 001 Revises: Create Date: 2024-12-09 """ 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 clients table op.create_table( "clients", sa.Column("id", sa.UUID(), nullable=False), sa.Column("name", sa.String(length=255), nullable=False), sa.Column("contact_email", sa.String(length=255), nullable=True), sa.Column("billing_plan", sa.String(length=50), nullable=False, server_default="free"), sa.Column("status", sa.String(length=50), nullable=False, server_default="active"), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.Column( "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.PrimaryKeyConstraint("id"), ) # Create instances table op.create_table( "instances", sa.Column("id", sa.UUID(), nullable=False), sa.Column("client_id", sa.UUID(), nullable=False), sa.Column("instance_id", sa.String(length=255), nullable=False), # Licensing sa.Column("license_key_hash", sa.String(length=64), nullable=False), sa.Column("license_key_prefix", sa.String(length=12), nullable=False), sa.Column("license_status", sa.String(length=50), nullable=False, server_default="active"), sa.Column("license_issued_at", sa.DateTime(timezone=True), nullable=False), sa.Column("license_expires_at", sa.DateTime(timezone=True), nullable=True), # Activation state sa.Column("activated_at", sa.DateTime(timezone=True), nullable=True), sa.Column("last_activation_at", sa.DateTime(timezone=True), nullable=True), sa.Column("activation_count", sa.Integer(), nullable=False, server_default="0"), # Telemetry sa.Column("hub_api_key_hash", sa.String(length=64), nullable=True), # Metadata sa.Column("region", sa.String(length=50), nullable=True), sa.Column("version", sa.String(length=50), nullable=True), sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=True), sa.Column("status", sa.String(length=50), nullable=False, server_default="pending"), # Timestamps sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.Column( "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.ForeignKeyConstraint( ["client_id"], ["clients.id"], ondelete="CASCADE", ), sa.PrimaryKeyConstraint("id"), ) op.create_index( op.f("ix_instances_instance_id"), "instances", ["instance_id"], unique=True, ) # Create usage_samples table op.create_table( "usage_samples", sa.Column("id", sa.UUID(), nullable=False), sa.Column("instance_id", sa.UUID(), nullable=False), # Time window sa.Column("window_start", sa.DateTime(timezone=True), nullable=False), sa.Column("window_end", sa.DateTime(timezone=True), nullable=False), sa.Column("window_type", sa.String(length=20), nullable=False), # Tool (ONLY name) sa.Column("tool_name", sa.String(length=255), nullable=False), # Counts sa.Column("call_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("success_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("error_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("rate_limited_count", sa.Integer(), nullable=False, server_default="0"), # Duration stats sa.Column("total_duration_ms", sa.Integer(), nullable=False, server_default="0"), sa.Column("min_duration_ms", sa.Integer(), nullable=False, server_default="0"), sa.Column("max_duration_ms", sa.Integer(), nullable=False, server_default="0"), sa.ForeignKeyConstraint( ["instance_id"], ["instances.id"], ondelete="CASCADE", ), sa.PrimaryKeyConstraint("id"), ) op.create_index( op.f("ix_usage_samples_instance_id"), "usage_samples", ["instance_id"], unique=False, ) op.create_index( op.f("ix_usage_samples_tool_name"), "usage_samples", ["tool_name"], unique=False, ) def downgrade() -> None: op.drop_index(op.f("ix_usage_samples_tool_name"), table_name="usage_samples") op.drop_index(op.f("ix_usage_samples_instance_id"), table_name="usage_samples") op.drop_table("usage_samples") op.drop_index(op.f("ix_instances_instance_id"), table_name="instances") op.drop_table("instances") op.drop_table("clients")