feat: add invite rewards and redeem codes
This commit is contained in:
@@ -30,7 +30,9 @@ def upgrade() -> None:
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users")
|
||||
op.execute("DROP FUNCTION IF EXISTS public.initialize_profile_and_invite_code_on_signup()")
|
||||
op.execute(
|
||||
"DROP FUNCTION IF EXISTS public.initialize_profile_and_invite_code_on_signup()"
|
||||
)
|
||||
op.execute("DROP FUNCTION IF EXISTS public.generate_invite_code()")
|
||||
|
||||
for table_name in [
|
||||
@@ -61,18 +63,37 @@ def _create_profiles() -> None:
|
||||
sa.Column("username", sa.String(length=30), nullable=False),
|
||||
sa.Column("avatar_url", sa.Text(), nullable=True),
|
||||
sa.Column("bio", sa.String(length=200), nullable=True),
|
||||
sa.Column("settings", postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), nullable=False),
|
||||
sa.Column(
|
||||
"settings",
|
||||
postgresql.JSONB(astext_type=sa.Text()),
|
||||
server_default=sa.text("'{}'::jsonb"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("referred_by", sa.UUID(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.CheckConstraint("char_length(username) >= 1", name="ck_profiles_username_non_empty"),
|
||||
sa.CheckConstraint(
|
||||
"char_length(username) >= 1", name="ck_profiles_username_non_empty"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["id"], ["auth.users.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(["referred_by"], ["profiles.id"], ondelete="SET NULL"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index("ix_profiles_username", "profiles", ["username"])
|
||||
op.create_index("ix_profiles_settings_gin", "profiles", ["settings"], postgresql_using="gin")
|
||||
op.create_index(
|
||||
"ix_profiles_settings_gin", "profiles", ["settings"], postgresql_using="gin"
|
||||
)
|
||||
op.create_index("ix_profiles_referred_by", "profiles", ["referred_by"])
|
||||
_enable_service_only_rls("profiles")
|
||||
|
||||
@@ -86,24 +107,60 @@ def _create_chat_tables() -> None:
|
||||
sa.Column("job_id", sa.UUID(), nullable=True),
|
||||
sa.Column("title", sa.String(length=255), nullable=True),
|
||||
sa.Column("status", sa.String(length=20), nullable=False),
|
||||
sa.Column("last_activity_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("message_count", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("total_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("total_cost", sa.Numeric(12, 6), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("state_snapshot", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column(
|
||||
"last_activity_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"message_count", sa.Integer(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"total_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"total_cost", sa.Numeric(12, 6), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"state_snapshot", postgresql.JSONB(astext_type=sa.Text()), nullable=True
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.CheckConstraint("session_type in ('chat', 'automation')", name="ck_sessions_session_type"),
|
||||
sa.CheckConstraint("status in ('pending', 'running', 'completed', 'failed')", name="ck_sessions_status"),
|
||||
sa.CheckConstraint("message_count >= 0", name="ck_sessions_message_count_non_negative"),
|
||||
sa.CheckConstraint("total_tokens >= 0", name="ck_sessions_total_tokens_non_negative"),
|
||||
sa.CheckConstraint("total_cost >= 0", name="ck_sessions_total_cost_non_negative"),
|
||||
sa.CheckConstraint(
|
||||
"session_type in ('chat', 'automation')", name="ck_sessions_session_type"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"status in ('pending', 'running', 'completed', 'failed')",
|
||||
name="ck_sessions_status",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"message_count >= 0", name="ck_sessions_message_count_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"total_tokens >= 0", name="ck_sessions_total_tokens_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"total_cost >= 0", name="ck_sessions_total_cost_non_negative"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["auth.users.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index("ix_sessions_user_id", "sessions", ["user_id"])
|
||||
op.create_index("ix_sessions_user_activity", "sessions", ["user_id", "last_activity_at"])
|
||||
op.create_index(
|
||||
"ix_sessions_user_activity", "sessions", ["user_id", "last_activity_at"]
|
||||
)
|
||||
_enable_service_only_rls("sessions")
|
||||
|
||||
op.create_table(
|
||||
@@ -115,27 +172,61 @@ def _create_chat_tables() -> None:
|
||||
sa.Column("content", sa.Text(), nullable=False),
|
||||
sa.Column("model_code", sa.String(length=50), nullable=True),
|
||||
sa.Column("tool_name", sa.String(length=100), nullable=True),
|
||||
sa.Column("input_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("output_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("cost", sa.Numeric(12, 6), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column(
|
||||
"input_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"output_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"cost", sa.Numeric(12, 6), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column("latency_ms", sa.Integer(), nullable=True),
|
||||
sa.Column("visibility_mask", sa.BigInteger(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column(
|
||||
"visibility_mask",
|
||||
sa.BigInteger(),
|
||||
server_default=sa.text("0"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("metadata", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.CheckConstraint("seq > 0", name="ck_messages_seq_positive"),
|
||||
sa.CheckConstraint("role in ('user', 'assistant', 'system', 'tool')", name="ck_messages_role"),
|
||||
sa.CheckConstraint("input_tokens >= 0", name="ck_messages_input_tokens_non_negative"),
|
||||
sa.CheckConstraint("output_tokens >= 0", name="ck_messages_output_tokens_non_negative"),
|
||||
sa.CheckConstraint(
|
||||
"role in ('user', 'assistant', 'system', 'tool')", name="ck_messages_role"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"input_tokens >= 0", name="ck_messages_input_tokens_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"output_tokens >= 0", name="ck_messages_output_tokens_non_negative"
|
||||
),
|
||||
sa.CheckConstraint("cost >= 0", name="ck_messages_cost_non_negative"),
|
||||
sa.CheckConstraint("latency_ms is null or latency_ms >= 0", name="ck_messages_latency_non_negative"),
|
||||
sa.CheckConstraint(
|
||||
"latency_ms is null or latency_ms >= 0",
|
||||
name="ck_messages_latency_non_negative",
|
||||
),
|
||||
sa.ForeignKeyConstraint(["session_id"], ["sessions.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("session_id", "seq", name="uq_messages_session_seq"),
|
||||
)
|
||||
op.create_index("ix_messages_session_id", "messages", ["session_id"])
|
||||
op.create_index("ix_messages_session_seq_visibility", "messages", ["session_id", "seq", "visibility_mask"])
|
||||
op.create_index(
|
||||
"ix_messages_session_seq_visibility",
|
||||
"messages",
|
||||
["session_id", "seq", "visibility_mask"],
|
||||
)
|
||||
_enable_service_only_rls("messages")
|
||||
|
||||
|
||||
@@ -143,18 +234,53 @@ def _create_points_tables() -> None:
|
||||
op.create_table(
|
||||
"user_points",
|
||||
sa.Column("user_id", sa.UUID(), nullable=False),
|
||||
sa.Column("balance", sa.BigInteger(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("frozen_balance", sa.BigInteger(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("lifetime_earned", sa.BigInteger(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("lifetime_spent", sa.BigInteger(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column(
|
||||
"balance", sa.BigInteger(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"frozen_balance",
|
||||
sa.BigInteger(),
|
||||
server_default=sa.text("0"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"lifetime_earned",
|
||||
sa.BigInteger(),
|
||||
server_default=sa.text("0"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"lifetime_spent",
|
||||
sa.BigInteger(),
|
||||
server_default=sa.text("0"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("version", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.CheckConstraint("balance >= 0", name="ck_user_points_balance_non_negative"),
|
||||
sa.CheckConstraint("frozen_balance >= 0", name="ck_user_points_frozen_balance_non_negative"),
|
||||
sa.CheckConstraint("lifetime_earned >= 0", name="ck_user_points_lifetime_earned_non_negative"),
|
||||
sa.CheckConstraint("lifetime_spent >= 0", name="ck_user_points_lifetime_spent_non_negative"),
|
||||
sa.CheckConstraint("frozen_balance <= balance", name="ck_user_points_frozen_le_balance"),
|
||||
sa.CheckConstraint(
|
||||
"frozen_balance >= 0", name="ck_user_points_frozen_balance_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"lifetime_earned >= 0", name="ck_user_points_lifetime_earned_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"lifetime_spent >= 0", name="ck_user_points_lifetime_spent_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"frozen_balance <= balance", name="ck_user_points_frozen_le_balance"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["auth.users.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("user_id"),
|
||||
)
|
||||
@@ -172,30 +298,89 @@ def _create_points_tables() -> None:
|
||||
sa.Column("biz_id", sa.UUID(), nullable=True),
|
||||
sa.Column("event_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("operator_id", sa.UUID(), nullable=True),
|
||||
sa.Column("metadata", postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column(
|
||||
"metadata",
|
||||
postgresql.JSONB(astext_type=sa.Text()),
|
||||
server_default=sa.text("'{}'::jsonb"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.CheckConstraint("amount > 0", name="ck_points_ledger_amount_positive"),
|
||||
sa.CheckConstraint("direction in (1, -1)", name="ck_points_ledger_direction_valid"),
|
||||
sa.CheckConstraint("balance_after >= 0", name="ck_points_ledger_balance_after_non_negative"),
|
||||
sa.CheckConstraint("change_type in ('register', 'consume', 'adjust', 'purchase', 'refund')", name="ck_points_ledger_change_type"),
|
||||
sa.CheckConstraint("biz_type is null or biz_type in ('chat', 'payment')", name="ck_points_ledger_biz_type"),
|
||||
sa.CheckConstraint("((change_type in ('register', 'adjust') and biz_type is null and biz_id is null) or (change_type = 'consume' and biz_type = 'chat' and biz_id is not null) or (change_type in ('purchase', 'refund') and biz_type = 'payment' and biz_id is not null))", name="ck_points_ledger_biz_binding"),
|
||||
sa.CheckConstraint("((change_type in ('register', 'purchase') and direction = 1) or (change_type in ('consume', 'refund') and direction = -1) or (change_type = 'adjust' and direction in (1, -1)))", name="ck_points_ledger_direction_by_change_type"),
|
||||
sa.CheckConstraint("jsonb_typeof(metadata) = 'object'", name="ck_points_ledger_metadata_object"),
|
||||
sa.CheckConstraint("metadata->>'schema_version' = '1' and metadata->>'operator_type' in ('user', 'system', 'admin') and coalesce(metadata->>'run_id', '') <> '' and (not (metadata ? 'ext') or jsonb_typeof(metadata->'ext') = 'object')", name="ck_points_ledger_metadata_common"),
|
||||
sa.CheckConstraint("(change_type <> 'register' or not (metadata ? 'charge'))", name="ck_points_ledger_metadata_register_shape"),
|
||||
sa.CheckConstraint("(change_type <> 'consume' or ((metadata ? 'charge') and jsonb_typeof(metadata->'charge') = 'object' and (metadata->'charge' ? 'message_id') and (metadata->'charge' ? 'message_seq') and (metadata->'charge' ? 'model_code') and (metadata->'charge' ? 'input_tokens') and (metadata->'charge' ? 'output_tokens') and (metadata->'charge' ? 'cost')))", name="ck_points_ledger_metadata_consume_shape"),
|
||||
sa.CheckConstraint("(change_type <> 'adjust' or ((metadata ? 'ext') and (metadata->'ext' ? 'reason') and coalesce(metadata #>> '{ext,reason}', '') <> ''))", name="ck_points_ledger_metadata_adjust_shape"),
|
||||
sa.CheckConstraint("(change_type not in ('purchase', 'refund') or ((metadata ? 'ext') and (metadata->'ext' ? 'source') and (metadata->'ext' ? 'platform') and (metadata->'ext' ? 'product_code') and (metadata->'ext' ? 'transaction_id') and coalesce(metadata #>> '{ext,source}', '') <> '' and coalesce(metadata #>> '{ext,platform}', '') <> '' and coalesce(metadata #>> '{ext,product_code}', '') <> '' and coalesce(metadata #>> '{ext,transaction_id}', '') <> ''))", name="ck_points_ledger_metadata_payment_shape"),
|
||||
sa.CheckConstraint("(change_type <> 'refund' or ((metadata ? 'ext') and (metadata->'ext' ? 'original_event_id') and coalesce(metadata #>> '{ext,original_event_id}', '') <> ''))", name="ck_points_ledger_metadata_refund_shape"),
|
||||
sa.ForeignKeyConstraint(["operator_id"], ["auth.users.id"], ondelete="SET NULL"),
|
||||
sa.CheckConstraint(
|
||||
"direction in (1, -1)", name="ck_points_ledger_direction_valid"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"balance_after >= 0", name="ck_points_ledger_balance_after_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"change_type in ('register', 'consume', 'adjust', 'purchase', 'refund')",
|
||||
name="ck_points_ledger_change_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"biz_type is null or biz_type in ('chat', 'payment')",
|
||||
name="ck_points_ledger_biz_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"((change_type in ('register', 'adjust') and biz_type is null and biz_id is null) or (change_type = 'consume' and biz_type = 'chat' and biz_id is not null) or (change_type in ('purchase', 'refund') and biz_type = 'payment' and biz_id is not null))",
|
||||
name="ck_points_ledger_biz_binding",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"((change_type in ('register', 'purchase') and direction = 1) or (change_type in ('consume', 'refund') and direction = -1) or (change_type = 'adjust' and direction in (1, -1)))",
|
||||
name="ck_points_ledger_direction_by_change_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"jsonb_typeof(metadata) = 'object'", name="ck_points_ledger_metadata_object"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"metadata->>'schema_version' = '1' and metadata->>'operator_type' in ('user', 'system', 'admin') and coalesce(metadata->>'run_id', '') <> '' and (not (metadata ? 'ext') or jsonb_typeof(metadata->'ext') = 'object')",
|
||||
name="ck_points_ledger_metadata_common",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(change_type <> 'register' or not (metadata ? 'charge'))",
|
||||
name="ck_points_ledger_metadata_register_shape",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(change_type <> 'consume' or ((metadata ? 'charge') and jsonb_typeof(metadata->'charge') = 'object' and (metadata->'charge' ? 'message_id') and (metadata->'charge' ? 'message_seq') and (metadata->'charge' ? 'model_code') and (metadata->'charge' ? 'input_tokens') and (metadata->'charge' ? 'output_tokens') and (metadata->'charge' ? 'cost')))",
|
||||
name="ck_points_ledger_metadata_consume_shape",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(change_type <> 'adjust' or ((metadata ? 'ext') and (metadata->'ext' ? 'reason') and coalesce(metadata #>> '{ext,reason}', '') <> ''))",
|
||||
name="ck_points_ledger_metadata_adjust_shape",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(change_type not in ('purchase', 'refund') or ((metadata ? 'ext') and (metadata->'ext' ? 'source') and (metadata->'ext' ? 'platform') and (metadata->'ext' ? 'product_code') and (metadata->'ext' ? 'transaction_id') and coalesce(metadata #>> '{ext,source}', '') <> '' and coalesce(metadata #>> '{ext,platform}', '') <> '' and coalesce(metadata #>> '{ext,product_code}', '') <> '' and coalesce(metadata #>> '{ext,transaction_id}', '') <> ''))",
|
||||
name="ck_points_ledger_metadata_payment_shape",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"(change_type <> 'refund' or ((metadata ? 'ext') and (metadata->'ext' ? 'original_event_id') and coalesce(metadata #>> '{ext,original_event_id}', '') <> ''))",
|
||||
name="ck_points_ledger_metadata_refund_shape",
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["operator_id"], ["auth.users.id"], ondelete="SET NULL"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["auth.users.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("user_id", "event_id", name="uq_points_ledger_user_event"),
|
||||
)
|
||||
op.create_index("ix_points_ledger_user_created_at", "points_ledger", ["user_id", sa.text("created_at DESC")])
|
||||
op.create_index("ix_points_ledger_biz_type_biz_id", "points_ledger", ["biz_type", "biz_id"])
|
||||
op.create_index(
|
||||
"ix_points_ledger_user_created_at",
|
||||
"points_ledger",
|
||||
["user_id", sa.text("created_at DESC")],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_points_ledger_biz_type_biz_id", "points_ledger", ["biz_type", "biz_id"]
|
||||
)
|
||||
_enable_service_only_rls("points_ledger")
|
||||
|
||||
op.create_table(
|
||||
@@ -213,24 +398,71 @@ def _create_points_tables() -> None:
|
||||
sa.Column("billed_to", sa.String(length=16), nullable=False),
|
||||
sa.Column("run_id", sa.String(length=128), nullable=True),
|
||||
sa.Column("request_id", sa.String(length=128), nullable=True),
|
||||
sa.Column("input_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("output_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("cost", sa.Numeric(12, 6), server_default=sa.text("0"), nullable=False),
|
||||
sa.Column("metadata", postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.CheckConstraint("amount >= 0", name="ck_points_audit_ledger_amount_non_negative"),
|
||||
sa.CheckConstraint("direction in (1, 0, -1)", name="ck_points_audit_ledger_direction_valid"),
|
||||
sa.CheckConstraint("balance_after >= 0", name="ck_points_audit_ledger_balance_after_non_negative"),
|
||||
sa.CheckConstraint("change_type in ('register', 'consume', 'adjust', 'purchase', 'refund')", name="ck_points_audit_ledger_change_type"),
|
||||
sa.CheckConstraint("biz_type is null or biz_type in ('chat', 'payment')", name="ck_points_audit_ledger_biz_type"),
|
||||
sa.CheckConstraint("billed_to in ('user', 'platform')", name="ck_points_audit_ledger_billed_to"),
|
||||
sa.CheckConstraint("jsonb_typeof(metadata) = 'object'", name="ck_points_audit_ledger_metadata_object"),
|
||||
sa.Column(
|
||||
"input_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"output_tokens", sa.Integer(), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"cost", sa.Numeric(12, 6), server_default=sa.text("0"), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"metadata",
|
||||
postgresql.JSONB(astext_type=sa.Text()),
|
||||
server_default=sa.text("'{}'::jsonb"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"amount >= 0", name="ck_points_audit_ledger_amount_non_negative"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"direction in (1, 0, -1)", name="ck_points_audit_ledger_direction_valid"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"balance_after >= 0",
|
||||
name="ck_points_audit_ledger_balance_after_non_negative",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"change_type in ('register', 'consume', 'adjust', 'purchase', 'refund')",
|
||||
name="ck_points_audit_ledger_change_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"biz_type is null or biz_type in ('chat', 'payment')",
|
||||
name="ck_points_audit_ledger_biz_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"billed_to in ('user', 'platform')", name="ck_points_audit_ledger_billed_to"
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"jsonb_typeof(metadata) = 'object'",
|
||||
name="ck_points_audit_ledger_metadata_object",
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("event_id", name="uq_points_audit_ledger_event_id"),
|
||||
)
|
||||
op.create_index("ix_points_audit_ledger_user_id_created_at", "points_audit_ledger", ["user_id_snapshot", sa.text("created_at DESC")])
|
||||
op.create_index("ix_points_audit_ledger_change_type_created_at", "points_audit_ledger", ["change_type", sa.text("created_at DESC")])
|
||||
op.create_index(
|
||||
"ix_points_audit_ledger_user_id_created_at",
|
||||
"points_audit_ledger",
|
||||
["user_id_snapshot", sa.text("created_at DESC")],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_points_audit_ledger_change_type_created_at",
|
||||
"points_audit_ledger",
|
||||
["change_type", sa.text("created_at DESC")],
|
||||
)
|
||||
_enable_service_only_rls("points_audit_ledger")
|
||||
|
||||
op.create_table(
|
||||
@@ -241,12 +473,29 @@ def _create_points_tables() -> None:
|
||||
sa.Column("first_user_id_snapshot", sa.UUID(), nullable=True),
|
||||
sa.Column("balance_snapshot", sa.BigInteger(), nullable=True),
|
||||
sa.Column("grant_event_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("has_purchased_starter_pack", sa.Boolean(), server_default=sa.text("false"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
||||
sa.Column(
|
||||
"has_purchased_starter_pack",
|
||||
sa.Boolean(),
|
||||
server_default=sa.text("false"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("email_hash", name="uq_register_bonus_claims_email_hash"),
|
||||
sa.UniqueConstraint("grant_event_id", name="uq_register_bonus_claims_grant_event_id"),
|
||||
sa.UniqueConstraint(
|
||||
"grant_event_id", name="uq_register_bonus_claims_grant_event_id"
|
||||
),
|
||||
)
|
||||
_enable_service_only_rls("register_bonus_claims")
|
||||
|
||||
@@ -269,7 +518,9 @@ def _create_invite_codes() -> None:
|
||||
"""
|
||||
)
|
||||
op.execute("CREATE INDEX ix_invite_codes_owner_id ON invite_codes(owner_id)")
|
||||
op.execute("CREATE INDEX ix_invite_codes_code ON invite_codes(code) WHERE status = 'active'")
|
||||
op.execute(
|
||||
"CREATE INDEX ix_invite_codes_code ON invite_codes(code) WHERE status = 'active'"
|
||||
)
|
||||
_enable_service_only_rls("invite_codes")
|
||||
|
||||
|
||||
@@ -370,23 +621,37 @@ def _create_signup_helpers() -> None:
|
||||
"""
|
||||
)
|
||||
op.execute("DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users")
|
||||
op.execute("CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.initialize_profile_and_invite_code_on_signup()")
|
||||
op.execute(
|
||||
"CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.initialize_profile_and_invite_code_on_signup()"
|
||||
)
|
||||
|
||||
|
||||
def _enable_service_only_rls(table_name: str) -> None:
|
||||
for role in ["anon", "authenticated"]:
|
||||
for action in ["select", "insert", "update", "delete"]:
|
||||
op.execute(f"DROP POLICY IF EXISTS {role}_{action}_{table_name} ON {table_name}")
|
||||
op.execute(
|
||||
f"DROP POLICY IF EXISTS {role}_{action}_{table_name} ON {table_name}"
|
||||
)
|
||||
op.execute(f"ALTER TABLE {table_name} ENABLE ROW LEVEL SECURITY")
|
||||
for role in ["anon", "authenticated"]:
|
||||
op.execute(f"CREATE POLICY {role}_select_{table_name} ON {table_name} FOR SELECT TO {role} USING (false)")
|
||||
op.execute(f"CREATE POLICY {role}_insert_{table_name} ON {table_name} FOR INSERT TO {role} WITH CHECK (false)")
|
||||
op.execute(f"CREATE POLICY {role}_update_{table_name} ON {table_name} FOR UPDATE TO {role} USING (false) WITH CHECK (false)")
|
||||
op.execute(f"CREATE POLICY {role}_delete_{table_name} ON {table_name} FOR DELETE TO {role} USING (false)")
|
||||
op.execute(
|
||||
f"CREATE POLICY {role}_select_{table_name} ON {table_name} FOR SELECT TO {role} USING (false)"
|
||||
)
|
||||
op.execute(
|
||||
f"CREATE POLICY {role}_insert_{table_name} ON {table_name} FOR INSERT TO {role} WITH CHECK (false)"
|
||||
)
|
||||
op.execute(
|
||||
f"CREATE POLICY {role}_update_{table_name} ON {table_name} FOR UPDATE TO {role} USING (false) WITH CHECK (false)"
|
||||
)
|
||||
op.execute(
|
||||
f"CREATE POLICY {role}_delete_{table_name} ON {table_name} FOR DELETE TO {role} USING (false)"
|
||||
)
|
||||
|
||||
|
||||
def _drop_service_only_rls(table_name: str) -> None:
|
||||
for role in ["anon", "authenticated"]:
|
||||
for action in ["select", "insert", "update", "delete"]:
|
||||
op.execute(f"DROP POLICY IF EXISTS {role}_{action}_{table_name} ON {table_name}")
|
||||
op.execute(
|
||||
f"DROP POLICY IF EXISTS {role}_{action}_{table_name} ON {table_name}"
|
||||
)
|
||||
op.execute(f"ALTER TABLE {table_name} DISABLE ROW LEVEL SECURITY")
|
||||
|
||||
Reference in New Issue
Block a user