diff --git a/apps/api/drizzle/0013_redirect_domains.sql b/apps/api/drizzle/0013_redirect_domains.sql new file mode 100644 index 0000000..4cacd3d --- /dev/null +++ b/apps/api/drizzle/0013_redirect_domains.sql @@ -0,0 +1,24 @@ +CREATE TABLE IF NOT EXISTS "redirect_domains" ( + "id" text PRIMARY KEY NOT NULL, + "team_id" text NOT NULL, + "hostname" text NOT NULL, + "path_prefix" text DEFAULT '' NOT NULL, + "rules" jsonb DEFAULT '[]'::jsonb NOT NULL, + "verification_token" text NOT NULL, + "txt_satisfied" boolean, + "routing_satisfied" boolean, + "status" text DEFAULT 'pending' NOT NULL, + "verified_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "redirect_domains_hostname_unique" UNIQUE("hostname"), + CONSTRAINT "redirect_domains_verified_at_check" CHECK (("status" <> 'verified' OR "verified_at" IS NOT NULL)) +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "redirect_domains" ADD CONSTRAINT "redirect_domains_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "redirect_domains_team_idx" ON "redirect_domains" USING btree ("team_id"); \ No newline at end of file diff --git a/apps/api/drizzle/meta/0013_snapshot.json b/apps/api/drizzle/meta/0013_snapshot.json new file mode 100644 index 0000000..f99d38b --- /dev/null +++ b/apps/api/drizzle/meta/0013_snapshot.json @@ -0,0 +1,10231 @@ +{ + "id": "4831526a-a54d-4900-8fcd-0cff0cc095b7", + "prevId": "8e0797eb-6275-46d8-b506-b3bf93432828", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "auth_user_id": { + "name": "auth_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "accounts_auth_user_id_auth_user_id_fk": { + "name": "accounts_auth_user_id_auth_user_id_fk", + "tableFrom": "accounts", + "tableTo": "auth_user", + "columnsFrom": [ + "auth_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "accounts_auth_user_id_unique": { + "name": "accounts_auth_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "auth_user_id" + ] + }, + "accounts_email_unique": { + "name": "accounts_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_settings": { + "name": "ai_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_settings": { + "name": "model_settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "azure_endpoint": { + "name": "azure_endpoint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "embedding_provider": { + "name": "embedding_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_embedding_api_key": { + "name": "encrypted_embedding_api_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "embedding_deployment": { + "name": "embedding_deployment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "embedding_endpoint": { + "name": "embedding_endpoint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_articles": { + "name": "index_articles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "index_changelog": { + "name": "index_changelog", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ai_settings_team_id_teams_id_fk": { + "name": "ai_settings_team_id_teams_id_fk", + "tableFrom": "ai_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ai_settings_team_id_unique": { + "name": "ai_settings_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_usage_daily": { + "name": "ai_usage_daily", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usage_date": { + "name": "usage_date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reserved_tokens": { + "name": "reserved_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "used_tokens": { + "name": "used_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_usage_daily_team_date_idx": { + "name": "ai_usage_daily_team_date_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_usage_daily_team_id_teams_id_fk": { + "name": "ai_usage_daily_team_id_teams_id_fk", + "tableFrom": "ai_usage_daily", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_prefix": { + "name": "key_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "api_keys_team_idx": { + "name": "api_keys_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_team_id_teams_id_fk": { + "name": "api_keys_team_id_teams_id_fk", + "tableFrom": "api_keys", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_hash_unique": { + "name": "api_keys_key_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "key_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_account": { + "name": "auth_account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_account_user_id_idx": { + "name": "auth_account_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_account_user_id_auth_user_id_fk": { + "name": "auth_account_user_id_auth_user_id_fk", + "tableFrom": "auth_account", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_access_token": { + "name": "auth_oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_oauth_access_token_client_id_idx": { + "name": "auth_oauth_access_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_session_id_idx": { + "name": "auth_oauth_access_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_user_id_idx": { + "name": "auth_oauth_access_token_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_authorization_code_id_idx": { + "name": "auth_oauth_access_token_authorization_code_id_idx", + "columns": [ + { + "expression": "authorization_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_access_token_refresh_id_idx": { + "name": "auth_oauth_access_token_refresh_id_idx", + "columns": [ + { + "expression": "refresh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_oauth_access_token_client_id_auth_oauth_client_client_id_fk": { + "name": "auth_oauth_access_token_client_id_auth_oauth_client_client_id_fk", + "tableFrom": "auth_oauth_access_token", + "tableTo": "auth_oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auth_oauth_access_token_session_id_auth_session_id_fk": { + "name": "auth_oauth_access_token_session_id_auth_session_id_fk", + "tableFrom": "auth_oauth_access_token", + "tableTo": "auth_session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auth_oauth_access_token_user_id_auth_user_id_fk": { + "name": "auth_oauth_access_token_user_id_auth_user_id_fk", + "tableFrom": "auth_oauth_access_token", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auth_oauth_access_token_refresh_id_auth_oauth_refresh_token_id_fk": { + "name": "auth_oauth_access_token_refresh_id_auth_oauth_refresh_token_id_fk", + "tableFrom": "auth_oauth_access_token", + "tableTo": "auth_oauth_refresh_token", + "columnsFrom": [ + "refresh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_oauth_access_token_token_unique": { + "name": "auth_oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_client": { + "name": "auth_oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_oauth_client_user_id_idx": { + "name": "auth_oauth_client_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_oauth_client_user_id_auth_user_id_fk": { + "name": "auth_oauth_client_user_id_auth_user_id_fk", + "tableFrom": "auth_oauth_client", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_oauth_client_client_id_unique": { + "name": "auth_oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": [ + "client_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_client_assertion": { + "name": "auth_oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_client_resource": { + "name": "auth_oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_oauth_client_resource_client_id_idx": { + "name": "auth_oauth_client_resource_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_client_resource_resource_id_idx": { + "name": "auth_oauth_client_resource_resource_id_idx", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_client_resource_client_id_resource_id_idx": { + "name": "auth_oauth_client_resource_client_id_resource_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_oauth_client_resource_client_id_auth_oauth_client_client_id_fk": { + "name": "auth_oauth_client_resource_client_id_auth_oauth_client_client_id_fk", + "tableFrom": "auth_oauth_client_resource", + "tableTo": "auth_oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auth_oauth_client_resource_resource_id_auth_oauth_resource_identifier_fk": { + "name": "auth_oauth_client_resource_resource_id_auth_oauth_resource_identifier_fk", + "tableFrom": "auth_oauth_client_resource", + "tableTo": "auth_oauth_resource", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "identifier" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_consent": { + "name": "auth_oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_oauth_consent_client_id_idx": { + "name": "auth_oauth_consent_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_consent_user_id_idx": { + "name": "auth_oauth_consent_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_oauth_consent_client_id_auth_oauth_client_client_id_fk": { + "name": "auth_oauth_consent_client_id_auth_oauth_client_client_id_fk", + "tableFrom": "auth_oauth_consent", + "tableTo": "auth_oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auth_oauth_consent_user_id_auth_user_id_fk": { + "name": "auth_oauth_consent_user_id_auth_user_id_fk", + "tableFrom": "auth_oauth_consent", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_refresh_token": { + "name": "auth_oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_oauth_refresh_token_client_id_idx": { + "name": "auth_oauth_refresh_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_refresh_token_authorization_code_id_idx": { + "name": "auth_oauth_refresh_token_authorization_code_id_idx", + "columns": [ + { + "expression": "authorization_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_refresh_token_session_id_idx": { + "name": "auth_oauth_refresh_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_oauth_refresh_token_user_id_idx": { + "name": "auth_oauth_refresh_token_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_oauth_refresh_token_client_id_auth_oauth_client_client_id_fk": { + "name": "auth_oauth_refresh_token_client_id_auth_oauth_client_client_id_fk", + "tableFrom": "auth_oauth_refresh_token", + "tableTo": "auth_oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auth_oauth_refresh_token_session_id_auth_session_id_fk": { + "name": "auth_oauth_refresh_token_session_id_auth_session_id_fk", + "tableFrom": "auth_oauth_refresh_token", + "tableTo": "auth_session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auth_oauth_refresh_token_user_id_auth_user_id_fk": { + "name": "auth_oauth_refresh_token_user_id_auth_user_id_fk", + "tableFrom": "auth_oauth_refresh_token", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_oauth_refresh_token_token_unique": { + "name": "auth_oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_oauth_resource": { + "name": "auth_oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_oauth_resource_identifier_unique": { + "name": "auth_oauth_resource_identifier_unique", + "nullsNotDistinct": false, + "columns": [ + "identifier" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_session": { + "name": "auth_session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "reauthenticated_at": { + "name": "reauthenticated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_session_user_id_idx": { + "name": "auth_session_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_session_user_id_auth_user_id_fk": { + "name": "auth_session_user_id_auth_user_id_fk", + "tableFrom": "auth_session", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_session_token_unique": { + "name": "auth_session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_user": { + "name": "auth_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_user_email_unique": { + "name": "auth_user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_verification": { + "name": "auth_verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "auth_verification_identifier_idx": { + "name": "auth_verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.authorization_audit_events": { + "name": "authorization_audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_public_id": { + "name": "team_public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_account_id": { + "name": "actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_member_id": { + "name": "target_member_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_email": { + "name": "target_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "before_permissions": { + "name": "before_permissions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "after_permissions": { + "name": "after_permissions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "authorization_audit_team_created_idx": { + "name": "authorization_audit_team_created_idx", + "columns": [ + { + "expression": "team_public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "authorization_audit_expires_idx": { + "name": "authorization_audit_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "authorization_audit_events_actor_account_id_accounts_id_fk": { + "name": "authorization_audit_events_actor_account_id_accounts_id_fk", + "tableFrom": "authorization_audit_events", + "tableTo": "accounts", + "columnsFrom": [ + "actor_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chatbot_settings": { + "name": "chatbot_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Assistant'" + }, + "greeting": { + "name": "greeting", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accent_color": { + "name": "accent_color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "escalation_enabled": { + "name": "escalation_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "variant": { + "name": "variant", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'bubble'" + }, + "widget_style": { + "name": "widget_style", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chatbot_settings_team_id_teams_id_fk": { + "name": "chatbot_settings_team_id_teams_id_fk", + "tableFrom": "chatbot_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chatbot_settings_team_id_unique": { + "name": "chatbot_settings_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_domains": { + "name": "custom_domains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "primary": { + "name": "primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "routing_satisfied": { + "name": "routing_satisfied", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_domains_team_idx": { + "name": "custom_domains_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "custom_domains_team_id_teams_id_fk": { + "name": "custom_domains_team_id_teams_id_fk", + "tableFrom": "custom_domains", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "custom_domains_hostname_unique": { + "name": "custom_domains_hostname_unique", + "nullsNotDistinct": false, + "columns": [ + "hostname" + ] + } + }, + "policies": {}, + "checkConstraints": { + "custom_domains_verified_at_check": { + "name": "custom_domains_verified_at_check", + "value": "(\"status\" <> 'verified' OR \"verified_at\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.doc_sections": { + "name": "doc_sections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "section_id": { + "name": "section_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "doc_sections_team_slug_idx": { + "name": "doc_sections_team_slug_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_sections_team_idx": { + "name": "doc_sections_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "doc_sections_team_id_teams_id_fk": { + "name": "doc_sections_team_id_teams_id_fk", + "tableFrom": "doc_sections", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "doc_sections_section_id_unique": { + "name": "doc_sections_section_id_unique", + "nullsNotDistinct": false, + "columns": [ + "section_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "section_id_prefix_check": { + "name": "section_id_prefix_check", + "value": "\"section_id\" LIKE 'sec\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.document_chunks": { + "name": "document_chunks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "anchor": { + "name": "anchor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "document_chunks_team_idx": { + "name": "document_chunks_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_chunks_embedding_idx": { + "name": "document_chunks_embedding_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "document_chunks_team_id_teams_id_fk": { + "name": "document_chunks_team_id_teams_id_fk", + "tableFrom": "document_chunks", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_chunks_document_id_documents_id_fk": { + "name": "document_chunks_document_id_documents_id_fk", + "tableFrom": "document_chunks", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_index_jobs": { + "name": "document_index_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "document_index_jobs_document_idx": { + "name": "document_index_jobs_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "document_index_jobs_poll_idx": { + "name": "document_index_jobs_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_index_jobs_team_id_teams_id_fk": { + "name": "document_index_jobs_team_id_teams_id_fk", + "tableFrom": "document_index_jobs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_index_jobs_document_id_documents_id_fk": { + "name": "document_index_jobs_document_id_documents_id_fk", + "tableFrom": "document_index_jobs", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_api_operations": { + "name": "documentation_api_operations", + "schema": "", + "columns": { + "spec_id": { + "name": "spec_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "route": { + "name": "route", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deprecated": { + "name": "deprecated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "slice": { + "name": "slice", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunks": { + "name": "chunks", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "documentation_api_operations_route_idx": { + "name": "documentation_api_operations_route_idx", + "columns": [ + { + "expression": "spec_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "route", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_api_operations_team_idx": { + "name": "documentation_api_operations_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_api_operations_spec_id_documentation_api_specs_spec_id_fk": { + "name": "documentation_api_operations_spec_id_documentation_api_specs_spec_id_fk", + "tableFrom": "documentation_api_operations", + "tableTo": "documentation_api_specs", + "columnsFrom": [ + "spec_id" + ], + "columnsTo": [ + "spec_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_api_operations_team_id_teams_id_fk": { + "name": "documentation_api_operations_team_id_teams_id_fk", + "tableFrom": "documentation_api_operations", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_api_specs": { + "name": "documentation_api_specs", + "schema": "", + "columns": { + "spec_id": { + "name": "spec_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_path": { + "name": "source_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_blob_sha": { + "name": "source_blob_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mount": { + "name": "mount", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "spec": { + "name": "spec", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "spec_hash": { + "name": "spec_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "info": { + "name": "info", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "diagnostics": { + "name": "diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_api_specs_team_idx": { + "name": "documentation_api_specs_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_api_specs_team_id_teams_id_fk": { + "name": "documentation_api_specs_team_id_teams_id_fk", + "tableFrom": "documentation_api_specs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_delivery_outbox": { + "name": "documentation_delivery_outbox", + "schema": "", + "columns": { + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "publication_id": { + "name": "publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy_epoch": { + "name": "policy_epoch", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "destinations": { + "name": "destinations", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"local\"]'::jsonb" + }, + "acknowledgments": { + "name": "acknowledgments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "documentation_delivery_outbox_team_id_teams_id_fk": { + "name": "documentation_delivery_outbox_team_id_teams_id_fk", + "tableFrom": "documentation_delivery_outbox", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_embedding_configs": { + "name": "documentation_embedding_configs", + "schema": "", + "columns": { + "config_hash": { + "name": "config_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment": { + "name": "deployment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "documentation_embedding_configs_team_id_teams_id_fk": { + "name": "documentation_embedding_configs_team_id_teams_id_fk", + "tableFrom": "documentation_embedding_configs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_embeddings": { + "name": "documentation_embeddings", + "schema": "", + "columns": { + "revision_id": { + "name": "revision_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_hash": { + "name": "config_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vectors": { + "name": "vectors", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_embeddings_revision_config_idx": { + "name": "documentation_embeddings_revision_config_idx", + "columns": [ + { + "expression": "revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "config_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_embeddings_revision_id_documentation_revisions_revision_id_fk": { + "name": "documentation_embeddings_revision_id_documentation_revisions_revision_id_fk", + "tableFrom": "documentation_embeddings", + "tableTo": "documentation_revisions", + "columnsFrom": [ + "revision_id" + ], + "columnsTo": [ + "revision_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_evidence": { + "name": "documentation_evidence", + "schema": "", + "columns": { + "evidence_id": { + "name": "evidence_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "retrieved": { + "name": "retrieved", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "documentation_evidence_dedupe_idx": { + "name": "documentation_evidence_dedupe_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_evidence_team_id_teams_id_fk": { + "name": "documentation_evidence_team_id_teams_id_fk", + "tableFrom": "documentation_evidence", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_evidence_item_id_documentation_improvements_item_id_fk": { + "name": "documentation_evidence_item_id_documentation_improvements_item_id_fk", + "tableFrom": "documentation_evidence", + "tableTo": "documentation_improvements", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "item_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_evidence_ticket_id_tickets_id_fk": { + "name": "documentation_evidence_ticket_id_tickets_id_fk", + "tableFrom": "documentation_evidence", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_evidence_message_id_ticket_messages_id_fk": { + "name": "documentation_evidence_message_id_ticket_messages_id_fk", + "tableFrom": "documentation_evidence", + "tableTo": "ticket_messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_bindings": { + "name": "documentation_git_bindings", + "schema": "", + "columns": { + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_login": { + "name": "owner_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_private": { + "name": "repository_private", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "directory": { + "name": "directory", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "docs_enabled": { + "name": "docs_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "pages_directory": { + "name": "pages_directory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pages_source_key": { + "name": "pages_source_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "docs_source_key": { + "name": "docs_source_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pages_diagnostics": { + "name": "pages_diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "api_spec_path": { + "name": "api_spec_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_mount": { + "name": "api_mount", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'api-reference'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "requested_generation": { + "name": "requested_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "grant_version": { + "name": "grant_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "grant_actor_account_id": { + "name": "grant_actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "grant_github_user_id": { + "name": "grant_github_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_admin_verified": { + "name": "repository_admin_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "capabilities": { + "name": "capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "observed_sha": { + "name": "observed_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "validated_sha": { + "name": "validated_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "live_sha": { + "name": "live_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_successful_sync_at": { + "name": "last_successful_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_diagnostic": { + "name": "last_diagnostic", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "connection_receipt": { + "name": "connection_receipt", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_reconcile_at": { + "name": "next_reconcile_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "next_pr_reconcile_at": { + "name": "next_pr_reconcile_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_pr_reconcile_error": { + "name": "last_pr_reconcile_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_git_bindings_installation_idx": { + "name": "documentation_git_bindings_installation_idx", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_bindings_reconcile_idx": { + "name": "documentation_git_bindings_reconcile_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_reconcile_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_bindings_pr_reconcile_idx": { + "name": "documentation_git_bindings_pr_reconcile_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_pr_reconcile_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_bindings_team_id_teams_id_fk": { + "name": "documentation_git_bindings_team_id_teams_id_fk", + "tableFrom": "documentation_git_bindings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_checks": { + "name": "documentation_git_checks", + "schema": "", + "columns": { + "check_id": { + "name": "check_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connection_generation": { + "name": "connection_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pull_request_number": { + "name": "pull_request_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sync_run_id": { + "name": "sync_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "publication_result": { + "name": "publication_result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "check_run_id": { + "name": "check_run_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_phase": { + "name": "error_phase", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_git_checks_poll_idx": { + "name": "documentation_git_checks_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_checks_sync_run_idx": { + "name": "documentation_git_checks_sync_run_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sync_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_checks_active_pr_idx": { + "name": "documentation_git_checks_active_pr_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pull_request_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"documentation_git_checks\".\"status\" in ('queued', 'validating', 'reporting')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_checks_team_id_teams_id_fk": { + "name": "documentation_git_checks_team_id_teams_id_fk", + "tableFrom": "documentation_git_checks", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_deliveries": { + "name": "documentation_git_deliveries", + "schema": "", + "columns": { + "delivery_id": { + "name": "delivery_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ignored'" + }, + "run_ids": { + "name": "run_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_files": { + "name": "documentation_git_files", + "schema": "", + "columns": { + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_path": { + "name": "source_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "blob_sha": { + "name": "blob_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "markdown": { + "name": "markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "frontmatter": { + "name": "frontmatter", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_git_files_path_idx": { + "name": "documentation_git_files_path_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_files_document_idx": { + "name": "documentation_git_files_document_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"documentation_git_files\".\"removed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_files_team_id_teams_id_fk": { + "name": "documentation_git_files_team_id_teams_id_fk", + "tableFrom": "documentation_git_files", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_grants": { + "name": "documentation_git_grants", + "schema": "", + "columns": { + "grant_id": { + "name": "grant_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_account_id": { + "name": "actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_user_id": { + "name": "github_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "directory": { + "name": "directory", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "grant_version": { + "name": "grant_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "connection_generation": { + "name": "connection_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "capabilities": { + "name": "capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "repository_admin_verified": { + "name": "repository_admin_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_git_grants_idempotency_idx": { + "name": "documentation_git_grants_idempotency_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_grants_version_idx": { + "name": "documentation_git_grants_version_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "grant_version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_grants_team_id_teams_id_fk": { + "name": "documentation_git_grants_team_id_teams_id_fk", + "tableFrom": "documentation_git_grants", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_jobs": { + "name": "documentation_git_jobs", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connection_generation": { + "name": "connection_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "request_generation": { + "name": "request_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reconcile'" + }, + "reviewed_sha": { + "name": "reviewed_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "observed_sha": { + "name": "observed_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "publication_id": { + "name": "publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "diagnostic": { + "name": "diagnostic", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "diagnostic_report": { + "name": "diagnostic_report", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_git_jobs_generation_idx": { + "name": "documentation_git_jobs_generation_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_generation", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_jobs_poll_idx": { + "name": "documentation_git_jobs_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_jobs_team_id_teams_id_fk": { + "name": "documentation_git_jobs_team_id_teams_id_fk", + "tableFrom": "documentation_git_jobs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_migration_prs": { + "name": "documentation_git_migration_prs", + "schema": "", + "columns": { + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "setup_id": { + "name": "setup_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_credential": { + "name": "actor_credential", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "policy_epoch": { + "name": "policy_epoch", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "purge_generation": { + "name": "purge_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "initialize_empty_repository": { + "name": "initialize_empty_repository", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_sha": { + "name": "base_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tree_sha": { + "name": "tree_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bootstrap_sha": { + "name": "bootstrap_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pull_request_number": { + "name": "pull_request_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pull_request_url": { + "name": "pull_request_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merged_commit_sha": { + "name": "merged_commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "diagnostic": { + "name": "diagnostic", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_git_migration_prs_idempotency_idx": { + "name": "documentation_git_migration_prs_idempotency_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_migration_prs_work_idx": { + "name": "documentation_git_migration_prs_work_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_migration_prs_plan_id_documentation_git_migrations_plan_id_fk": { + "name": "documentation_git_migration_prs_plan_id_documentation_git_migrations_plan_id_fk", + "tableFrom": "documentation_git_migration_prs", + "tableTo": "documentation_git_migrations", + "columnsFrom": [ + "plan_id" + ], + "columnsTo": [ + "plan_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_migration_prs_team_id_teams_id_fk": { + "name": "documentation_git_migration_prs_team_id_teams_id_fk", + "tableFrom": "documentation_git_migration_prs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_migration_prs_setup_id_documentation_git_setups_setup_id_fk": { + "name": "documentation_git_migration_prs_setup_id_documentation_git_setups_setup_id_fk", + "tableFrom": "documentation_git_migration_prs", + "tableTo": "documentation_git_setups", + "columnsFrom": [ + "setup_id" + ], + "columnsTo": [ + "setup_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_migrations": { + "name": "documentation_git_migrations", + "schema": "", + "columns": { + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "setup_id": { + "name": "setup_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_account_id": { + "name": "actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "preflight_id": { + "name": "preflight_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "docs_draft_version": { + "name": "docs_draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active_publication_id": { + "name": "active_publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_hash": { + "name": "plan_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selection": { + "name": "selection", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "sections": { + "name": "sections", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "verified_sha": { + "name": "verified_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_git_migrations_team_idx": { + "name": "documentation_git_migrations_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_migrations_team_id_teams_id_fk": { + "name": "documentation_git_migrations_team_id_teams_id_fk", + "tableFrom": "documentation_git_migrations", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_migrations_setup_id_documentation_git_setups_setup_id_fk": { + "name": "documentation_git_migrations_setup_id_documentation_git_setups_setup_id_fk", + "tableFrom": "documentation_git_migrations", + "tableTo": "documentation_git_setups", + "columnsFrom": [ + "setup_id" + ], + "columnsTo": [ + "setup_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_migrations_preflight_id_documentation_git_preflights_preflight_id_fk": { + "name": "documentation_git_migrations_preflight_id_documentation_git_preflights_preflight_id_fk", + "tableFrom": "documentation_git_migrations", + "tableTo": "documentation_git_preflights", + "columnsFrom": [ + "preflight_id" + ], + "columnsTo": [ + "preflight_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_preflights": { + "name": "documentation_git_preflights", + "schema": "", + "columns": { + "preflight_id": { + "name": "preflight_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "setup_id": { + "name": "setup_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_login": { + "name": "owner_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_private": { + "name": "repository_private", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "directory": { + "name": "directory", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "docs_draft_version": { + "name": "docs_draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "manifest_hash": { + "name": "manifest_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_git_preflights_team_idx": { + "name": "documentation_git_preflights_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_preflights_team_id_teams_id_fk": { + "name": "documentation_git_preflights_team_id_teams_id_fk", + "tableFrom": "documentation_git_preflights", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_preflights_setup_id_documentation_git_setups_setup_id_fk": { + "name": "documentation_git_preflights_setup_id_documentation_git_setups_setup_id_fk", + "tableFrom": "documentation_git_preflights", + "tableTo": "documentation_git_setups", + "columnsFrom": [ + "setup_id" + ], + "columnsTo": [ + "setup_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_proposal_documents": { + "name": "documentation_git_proposal_documents", + "schema": "", + "columns": { + "proposal_id": { + "name": "proposal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "draft_version": { + "name": "draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_path": { + "name": "source_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_path": { + "name": "target_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "open": { + "name": "open", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": { + "documentation_git_proposal_documents_identity_idx": { + "name": "documentation_git_proposal_documents_identity_idx", + "columns": [ + { + "expression": "proposal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_proposal_documents_open_idx": { + "name": "documentation_git_proposal_documents_open_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"documentation_git_proposal_documents\".\"open\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_proposal_documents_proposal_id_documentation_git_proposals_proposal_id_fk": { + "name": "documentation_git_proposal_documents_proposal_id_documentation_git_proposals_proposal_id_fk", + "tableFrom": "documentation_git_proposal_documents", + "tableTo": "documentation_git_proposals", + "columnsFrom": [ + "proposal_id" + ], + "columnsTo": [ + "proposal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_proposal_documents_team_id_teams_id_fk": { + "name": "documentation_git_proposal_documents_team_id_teams_id_fk", + "tableFrom": "documentation_git_proposal_documents", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_proposals": { + "name": "documentation_git_proposals", + "schema": "", + "columns": { + "proposal_id": { + "name": "proposal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_credential": { + "name": "actor_credential", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "source_context": { + "name": "source_context", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "connection_generation": { + "name": "connection_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "grant_version": { + "name": "grant_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updates_proposal_id": { + "name": "updates_proposal_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "changes": { + "name": "changes", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "base_sha": { + "name": "base_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tree_sha": { + "name": "tree_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merged_commit_sha": { + "name": "merged_commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pull_request_number": { + "name": "pull_request_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pull_request_url": { + "name": "pull_request_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "diagnostics": { + "name": "diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_git_proposals_idempotency_idx": { + "name": "documentation_git_proposals_idempotency_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_git_proposals_poll_idx": { + "name": "documentation_git_proposals_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_proposals_team_id_teams_id_fk": { + "name": "documentation_git_proposals_team_id_teams_id_fk", + "tableFrom": "documentation_git_proposals", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_git_setups": { + "name": "documentation_git_setups", + "schema": "", + "columns": { + "setup_id": { + "name": "setup_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_account_id": { + "name": "actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_verifier": { + "name": "encrypted_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_user_token": { + "name": "encrypted_user_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_user_id": { + "name": "github_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_login": { + "name": "github_login", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purpose": { + "name": "purpose", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "return_path": { + "name": "return_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_git_setups_expiry_idx": { + "name": "documentation_git_setups_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_git_setups_team_id_teams_id_fk": { + "name": "documentation_git_setups_team_id_teams_id_fk", + "tableFrom": "documentation_git_setups", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_git_setups_actor_account_id_accounts_id_fk": { + "name": "documentation_git_setups_actor_account_id_accounts_id_fk", + "tableFrom": "documentation_git_setups", + "tableTo": "accounts", + "columnsFrom": [ + "actor_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "documentation_git_setups_state_hash_unique": { + "name": "documentation_git_setups_state_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "state_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_improvement_drafts": { + "name": "documentation_improvement_drafts", + "schema": "", + "columns": { + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approved_version": { + "name": "approved_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "item_version": { + "name": "item_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "draft_version": { + "name": "draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "documentation_improvement_drafts_version_idx": { + "name": "documentation_improvement_drafts_version_idx", + "columns": [ + { + "expression": "item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "approved_version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_improvement_drafts_item_id_documentation_improvements_item_id_fk": { + "name": "documentation_improvement_drafts_item_id_documentation_improvements_item_id_fk", + "tableFrom": "documentation_improvement_drafts", + "tableTo": "documentation_improvements", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "item_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_improvement_reviews": { + "name": "documentation_improvement_reviews", + "schema": "", + "columns": { + "review_id": { + "name": "review_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_account_id": { + "name": "actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_version": { + "name": "item_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input": { + "name": "input", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_improvement_reviews_item_idx": { + "name": "documentation_improvement_reviews_item_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_improvement_reviews_team_id_teams_id_fk": { + "name": "documentation_improvement_reviews_team_id_teams_id_fk", + "tableFrom": "documentation_improvement_reviews", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_improvement_reviews_item_id_documentation_improvements_item_id_fk": { + "name": "documentation_improvement_reviews_item_id_documentation_improvements_item_id_fk", + "tableFrom": "documentation_improvement_reviews", + "tableTo": "documentation_improvements", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "item_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_improvements": { + "name": "documentation_improvements", + "schema": "", + "columns": { + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "group_key": { + "name": "group_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_document_id": { + "name": "target_document_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "predecessor_id": { + "name": "predecessor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merged_into_id": { + "name": "merged_into_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved": { + "name": "approved", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "draft_document_id": { + "name": "draft_document_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_version": { + "name": "draft_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "draft_hash": { + "name": "draft_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision_id": { + "name": "revision_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "publication_id": { + "name": "publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_improvements_team_idx": { + "name": "documentation_improvements_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_improvements_team_id_teams_id_fk": { + "name": "documentation_improvements_team_id_teams_id_fk", + "tableFrom": "documentation_improvements", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_memberships": { + "name": "documentation_memberships", + "schema": "", + "columns": { + "publication_id": { + "name": "publication_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision_id": { + "name": "revision_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "route": { + "name": "route", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "documentation_memberships_route_idx": { + "name": "documentation_memberships_route_idx", + "columns": [ + { + "expression": "publication_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "route", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_memberships_document_idx": { + "name": "documentation_memberships_document_idx", + "columns": [ + { + "expression": "publication_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_memberships_publication_id_documentation_publications_publication_id_fk": { + "name": "documentation_memberships_publication_id_documentation_publications_publication_id_fk", + "tableFrom": "documentation_memberships", + "tableTo": "documentation_publications", + "columnsFrom": [ + "publication_id" + ], + "columnsTo": [ + "publication_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documentation_memberships_revision_id_documentation_revisions_revision_id_fk": { + "name": "documentation_memberships_revision_id_documentation_revisions_revision_id_fk", + "tableFrom": "documentation_memberships", + "tableTo": "documentation_revisions", + "columnsFrom": [ + "revision_id" + ], + "columnsTo": [ + "revision_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_publications": { + "name": "documentation_publications", + "schema": "", + "columns": { + "publication_id": { + "name": "publication_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_publication_id": { + "name": "base_publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "admission_type": { + "name": "admission_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'explicit'" + }, + "git_context": { + "name": "git_context", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "purge_generation": { + "name": "purge_generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "docs_draft_version": { + "name": "docs_draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "indexing": { + "name": "indexing", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "navigation": { + "name": "navigation", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "diagnostics": { + "name": "diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_publications_idempotency_idx": { + "name": "documentation_publications_idempotency_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documentation_publications_poll_idx": { + "name": "documentation_publications_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_publications_team_id_teams_id_fk": { + "name": "documentation_publications_team_id_teams_id_fk", + "tableFrom": "documentation_publications", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_purges": { + "name": "documentation_purges", + "schema": "", + "columns": { + "purge_id": { + "name": "purge_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_hash": { + "name": "plan_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "actor_account_id": { + "name": "actor_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preflight'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "documentation_purges_idempotency_idx": { + "name": "documentation_purges_idempotency_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_purges_team_id_teams_id_fk": { + "name": "documentation_purges_team_id_teams_id_fk", + "tableFrom": "documentation_purges", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_revisions": { + "name": "documentation_revisions", + "schema": "", + "columns": { + "revision_id": { + "name": "revision_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'docs-v1'" + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "markdown": { + "name": "markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "headings": { + "name": "headings", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "chunks": { + "name": "chunks", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documentation_revisions_team_idx": { + "name": "documentation_revisions_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documentation_revisions_team_id_teams_id_fk": { + "name": "documentation_revisions_team_id_teams_id_fk", + "tableFrom": "documentation_revisions", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentation_settings": { + "name": "documentation_settings", + "schema": "", + "columns": { + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'editor'" + }, + "draft_version": { + "name": "draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "active_publication_id": { + "name": "active_publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_api_spec_id": { + "name": "active_api_spec_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_generation": { + "name": "requested_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "policy_epoch": { + "name": "policy_epoch", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "purge_generation": { + "name": "purge_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "indexing": { + "name": "indexing", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"enabled\":true}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "documentation_settings_team_id_teams_id_fk": { + "name": "documentation_settings_team_id_teams_id_fk", + "tableFrom": "documentation_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documents": { + "name": "documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "excerpt": { + "name": "excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "featured_image": { + "name": "featured_image", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "published_at": { + "name": "published_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_version": { + "name": "draft_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "draft_slug": { + "name": "draft_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_markdown": { + "name": "draft_markdown", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_hash": { + "name": "draft_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_git_source": { + "name": "draft_git_source", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "draft_title": { + "name": "draft_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "draft_content": { + "name": "draft_content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "draft_excerpt": { + "name": "draft_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_featured_image": { + "name": "draft_featured_image", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "draft_meta": { + "name": "draft_meta", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "author_account_id": { + "name": "author_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "documents_team_kind_slug_idx": { + "name": "documents_team_kind_slug_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"documents\".\"kind\" <> 'doc'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_team_kind_idx": { + "name": "documents_team_kind_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_published_idx": { + "name": "documents_published_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "published_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "documents_team_id_teams_id_fk": { + "name": "documents_team_id_teams_id_fk", + "tableFrom": "documents", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documents_author_account_id_accounts_id_fk": { + "name": "documents_author_account_id_accounts_id_fk", + "tableFrom": "documents", + "tableTo": "accounts", + "columnsFrom": [ + "author_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "documents_document_id_unique": { + "name": "documents_document_id_unique", + "nullsNotDistinct": false, + "columns": [ + "document_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "document_id_prefix_check": { + "name": "document_id_prefix_check", + "value": "\"document_id\" LIKE 'doc\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.email_reply_tokens": { + "name": "email_reply_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requester_email": { + "name": "requester_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_reply_tokens_ticket_requester_idx": { + "name": "email_reply_tokens_ticket_requester_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requester_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_reply_tokens_team_id_teams_id_fk": { + "name": "email_reply_tokens_team_id_teams_id_fk", + "tableFrom": "email_reply_tokens", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_reply_tokens_ticket_id_tickets_id_fk": { + "name": "email_reply_tokens_ticket_id_tickets_id_fk", + "tableFrom": "email_reply_tokens", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_reply_tokens_token_unique": { + "name": "email_reply_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_assets": { + "name": "git_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "blob_sha": { + "name": "blob_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "git_assets_team_blob_idx": { + "name": "git_assets_team_blob_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "blob_sha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "git_assets_team_id_teams_id_fk": { + "name": "git_assets_team_id_teams_id_fk", + "tableFrom": "git_assets", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inbound_emails": { + "name": "inbound_emails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "inbound_emails_team_id_teams_id_fk": { + "name": "inbound_emails_team_id_teams_id_fk", + "tableFrom": "inbound_emails", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inbound_emails_ticket_id_tickets_id_fk": { + "name": "inbound_emails_ticket_id_tickets_id_fk", + "tableFrom": "inbound_emails", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "inbound_emails_message_id_unique": { + "name": "inbound_emails_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "message_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inbox_ai_runs": { + "name": "inbox_ai_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger_message_id": { + "name": "trigger_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stop_reason": { + "name": "stop_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "retrieval": { + "name": "retrieval", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "inbox_ai_runs_ticket_trigger_idx": { + "name": "inbox_ai_runs_ticket_trigger_idx", + "columns": [ + { + "expression": "ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_ai_runs_poll_idx": { + "name": "inbox_ai_runs_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inbox_ai_runs_team_id_teams_id_fk": { + "name": "inbox_ai_runs_team_id_teams_id_fk", + "tableFrom": "inbox_ai_runs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inbox_ai_runs_ticket_id_tickets_id_fk": { + "name": "inbox_ai_runs_ticket_id_tickets_id_fk", + "tableFrom": "inbox_ai_runs", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inbox_ai_runs_trigger_message_id_ticket_messages_id_fk": { + "name": "inbox_ai_runs_trigger_message_id_ticket_messages_id_fk", + "tableFrom": "inbox_ai_runs", + "tableTo": "ticket_messages", + "columnsFrom": [ + "trigger_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inbox_ai_settings": { + "name": "inbox_ai_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auto_reply": { + "name": "auto_reply", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "escalate_on_visitor_request": { + "name": "escalate_on_visitor_request", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "escalate_when_unsure": { + "name": "escalate_when_unsure", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "escalate_topics": { + "name": "escalate_topics", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "escalate_after_bot_turns": { + "name": "escalate_after_bot_turns", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inbox_ai_settings_team_channel_idx": { + "name": "inbox_ai_settings_team_channel_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inbox_ai_settings_team_id_teams_id_fk": { + "name": "inbox_ai_settings_team_id_teams_id_fk", + "tableFrom": "inbox_ai_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integrations": { + "name": "integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_team_provider_idx": { + "name": "integrations_team_provider_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integrations_team_id_teams_id_fk": { + "name": "integrations_team_id_teams_id_fk", + "tableFrom": "integrations", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_jwks": { + "name": "auth_jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_lit_id": { + "name": "media_lit_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "media_team_idx": { + "name": "media_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_medialit_idx": { + "name": "media_medialit_idx", + "columns": [ + { + "expression": "media_lit_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "media_team_id_teams_id_fk": { + "name": "media_team_id_teams_id_fk", + "tableFrom": "media", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_media_id_unique": { + "name": "media_media_id_unique", + "nullsNotDistinct": false, + "columns": [ + "media_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "media_id_prefix_check": { + "name": "media_id_prefix_check", + "value": "\"media_id\" LIKE 'med\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.oauth_post_login_team_selections": { + "name": "oauth_post_login_team_selections", + "schema": "", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_post_login_team_selections_session_id_auth_session_id_fk": { + "name": "oauth_post_login_team_selections_session_id_auth_session_id_fk", + "tableFrom": "oauth_post_login_team_selections", + "tableTo": "auth_session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_post_login_team_selections_team_id_teams_id_fk": { + "name": "oauth_post_login_team_selections_team_id_teams_id_fk", + "tableFrom": "oauth_post_login_team_selections", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.outbox_jobs": { + "name": "outbox_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "outbox_jobs_poll_idx": { + "name": "outbox_jobs_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "outbox_jobs_team_id_teams_id_fk": { + "name": "outbox_jobs_team_id_teams_id_fk", + "tableFrom": "outbox_jobs", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.page_pull_requests": { + "name": "page_pull_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "page_id": { + "name": "page_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_sha": { + "name": "base_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "directory": { + "name": "directory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "page_pull_requests_team_idx": { + "name": "page_pull_requests_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "page_pull_requests_team_id_teams_id_fk": { + "name": "page_pull_requests_team_id_teams_id_fk", + "tableFrom": "page_pull_requests", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "page_pull_requests_page_id_pages_id_fk": { + "name": "page_pull_requests_page_id_pages_id_fk", + "tableFrom": "page_pull_requests", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.page_revisions": { + "name": "page_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "page_revisions_team_hash_idx": { + "name": "page_revisions_team_hash_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "page_revisions_team_id_teams_id_fk": { + "name": "page_revisions_team_id_teams_id_fk", + "tableFrom": "page_revisions", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "page_id": { + "name": "page_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deletable": { + "name": "deletable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "draft_layout": { + "name": "draft_layout", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "draft_title": { + "name": "draft_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_description": { + "name": "draft_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_social_image": { + "name": "draft_social_image", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "draft_robots_allowed": { + "name": "draft_robots_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pages_team_idx": { + "name": "pages_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_team_slug_idx": { + "name": "pages_team_slug_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_team_id_teams_id_fk": { + "name": "pages_team_id_teams_id_fk", + "tableFrom": "pages", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "pages_page_id_unique": { + "name": "pages_page_id_unique", + "nullsNotDistinct": false, + "columns": [ + "page_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "page_id_prefix_check": { + "name": "page_id_prefix_check", + "value": "\"page_id\" LIKE 'pg\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.rate_limit_buckets": { + "name": "rate_limit_buckets", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reset_at": { + "name": "reset_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect_domains": { + "name": "redirect_domains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path_prefix": { + "name": "path_prefix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "rules": { + "name": "rules", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "txt_satisfied": { + "name": "txt_satisfied", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "routing_satisfied": { + "name": "routing_satisfied", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "redirect_domains_team_idx": { + "name": "redirect_domains_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "redirect_domains_team_id_teams_id_fk": { + "name": "redirect_domains_team_id_teams_id_fk", + "tableFrom": "redirect_domains", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redirect_domains_hostname_unique": { + "name": "redirect_domains_hostname_unique", + "nullsNotDistinct": false, + "columns": [ + "hostname" + ] + } + }, + "policies": {}, + "checkConstraints": { + "redirect_domains_verified_at_check": { + "name": "redirect_domains_verified_at_check", + "value": "(\"status\" <> 'verified' OR \"verified_at\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.site_cache_outbox": { + "name": "site_cache_outbox", + "schema": "", + "columns": { + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "invalidate_hosts": { + "name": "invalidate_hosts", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "destinations": { + "name": "destinations", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "acknowledgments": { + "name": "acknowledgments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lease_token": { + "name": "lease_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "site_cache_outbox_poll_idx": { + "name": "site_cache_outbox_poll_idx", + "columns": [ + { + "expression": "completed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "site_cache_outbox_team_id_teams_id_fk": { + "name": "site_cache_outbox_team_id_teams_id_fk", + "tableFrom": "site_cache_outbox", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.site_release_pages": { + "name": "site_release_pages", + "schema": "", + "columns": { + "release_id": { + "name": "release_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "page_id": { + "name": "page_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision_id": { + "name": "revision_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "site_release_pages_slug_idx": { + "name": "site_release_pages_slug_idx", + "columns": [ + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_release_pages_page_idx": { + "name": "site_release_pages_page_idx", + "columns": [ + { + "expression": "page_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "site_release_pages_release_id_site_releases_id_fk": { + "name": "site_release_pages_release_id_site_releases_id_fk", + "tableFrom": "site_release_pages", + "tableTo": "site_releases", + "columnsFrom": [ + "release_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "site_release_pages_page_id_pages_id_fk": { + "name": "site_release_pages_page_id_pages_id_fk", + "tableFrom": "site_release_pages", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "site_release_pages_revision_id_page_revisions_id_fk": { + "name": "site_release_pages_revision_id_page_revisions_id_fk", + "tableFrom": "site_release_pages", + "tableTo": "page_revisions", + "columnsFrom": [ + "revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.site_releases": { + "name": "site_releases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "release_id": { + "name": "release_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "docs_publication_id": { + "name": "docs_publication_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "site": { + "name": "site", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_key": { + "name": "source_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "site_releases_team_idx": { + "name": "site_releases_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "site_releases_team_id_teams_id_fk": { + "name": "site_releases_team_id_teams_id_fk", + "tableFrom": "site_releases", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "site_releases_release_id_unique": { + "name": "site_releases_release_id_unique", + "nullsNotDistinct": false, + "columns": [ + "release_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "release_id_prefix_check": { + "name": "release_id_prefix_check", + "value": "\"release_id\" LIKE 'rel\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.team_invitations": { + "name": "team_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "invitation_id": { + "name": "invitation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "invited_by_account_id": { + "name": "invited_by_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rejected_at": { + "name": "rejected_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_invitations_pending_team_email_idx": { + "name": "team_invitations_pending_team_email_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_invitations\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_invitations_team_created_idx": { + "name": "team_invitations_team_created_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_invitations_team_email_idx": { + "name": "team_invitations_team_email_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_invitations_expires_idx": { + "name": "team_invitations_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_invitations_team_id_teams_id_fk": { + "name": "team_invitations_team_id_teams_id_fk", + "tableFrom": "team_invitations", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_invitations_invited_by_account_id_accounts_id_fk": { + "name": "team_invitations_invited_by_account_id_accounts_id_fk", + "tableFrom": "team_invitations", + "tableTo": "accounts", + "columnsFrom": [ + "invited_by_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_invitations_invitation_id_unique": { + "name": "team_invitations_invitation_id_unique", + "nullsNotDistinct": false, + "columns": [ + "invitation_id" + ] + }, + "team_invitations_token_hash_unique": { + "name": "team_invitations_token_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "token_hash" + ] + } + }, + "policies": {}, + "checkConstraints": { + "invitation_id_prefix_check": { + "name": "invitation_id_prefix_check", + "value": "\"invitation_id\" LIKE 'tinv\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.team_members": { + "name": "team_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_members_team_account_idx": { + "name": "team_members_team_account_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_members_team_id_teams_id_fk": { + "name": "team_members_team_id_teams_id_fk", + "tableFrom": "team_members", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_members_account_id_accounts_id_fk": { + "name": "team_members_account_id_accounts_id_fk", + "tableFrom": "team_members", + "tableTo": "accounts", + "columnsFrom": [ + "account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_members_member_id_unique": { + "name": "team_members_member_id_unique", + "nullsNotDistinct": false, + "columns": [ + "member_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "member_id_prefix_check": { + "name": "member_id_prefix_check", + "value": "\"member_id\" LIKE 'tmem\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.team_settings": { + "name": "team_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "theme_id": { + "name": "theme_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_injection_head": { + "name": "code_injection_head", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_injection_body": { + "name": "code_injection_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_shared_widgets": { + "name": "draft_shared_widgets", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"header\":{\"widgetId\":\"shared-header\",\"name\":\"header\",\"deletable\":false,\"moveable\":false,\"shared\":true,\"settings\":{\"verticalPadding\":\"py-4\"}},\"footer\":{\"widgetId\":\"shared-footer\",\"name\":\"footer\",\"deletable\":false,\"moveable\":false,\"shared\":true,\"settings\":{}}}'::jsonb" + }, + "active_release_id": { + "name": "active_release_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_settings_team_id_teams_id_fk": { + "name": "team_settings_team_id_teams_id_fk", + "tableFrom": "team_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "team_settings_team_id_unique": { + "name": "team_settings_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_account_id": { + "name": "owner_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subdomain": { + "name": "subdomain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_subdomain": { + "name": "redirect_subdomain", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "hide_from_search": { + "name": "hide_from_search", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "custom_domain_cname_target": { + "name": "custom_domain_cname_target", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain_txt_record_name": { + "name": "custom_domain_txt_record_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain_verification_token": { + "name": "custom_domain_verification_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain_txt_satisfied": { + "name": "custom_domain_txt_satisfied", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_owner_account_id_accounts_id_fk": { + "name": "teams_owner_account_id_accounts_id_fk", + "tableFrom": "teams", + "tableTo": "accounts", + "columnsFrom": [ + "owner_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "teams_team_id_unique": { + "name": "teams_team_id_unique", + "nullsNotDistinct": false, + "columns": [ + "team_id" + ] + }, + "teams_external_id_unique": { + "name": "teams_external_id_unique", + "nullsNotDistinct": false, + "columns": [ + "external_id" + ] + }, + "teams_subdomain_unique": { + "name": "teams_subdomain_unique", + "nullsNotDistinct": false, + "columns": [ + "subdomain" + ] + } + }, + "policies": {}, + "checkConstraints": { + "team_id_prefix_check": { + "name": "team_id_prefix_check", + "value": "\"team_id\" LIKE 'team\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.themes": { + "name": "themes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "theme_id": { + "name": "theme_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "style": { + "name": "style", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "themes_team_idx": { + "name": "themes_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "themes_team_id_teams_id_fk": { + "name": "themes_team_id_teams_id_fk", + "tableFrom": "themes", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "themes_theme_id_unique": { + "name": "themes_theme_id_unique", + "nullsNotDistinct": false, + "columns": [ + "theme_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "theme_id_prefix_check": { + "name": "theme_id_prefix_check", + "value": "\"theme_id\" LIKE 'thm\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.ticket_access_grants": { + "name": "ticket_access_grants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ticket_access_grants_ticket_idx": { + "name": "ticket_access_grants_ticket_idx", + "columns": [ + { + "expression": "ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ticket_access_grants_team_id_teams_id_fk": { + "name": "ticket_access_grants_team_id_teams_id_fk", + "tableFrom": "ticket_access_grants", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ticket_access_grants_ticket_id_tickets_id_fk": { + "name": "ticket_access_grants_ticket_id_tickets_id_fk", + "tableFrom": "ticket_access_grants", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ticket_access_grants_token_hash_unique": { + "name": "ticket_access_grants_token_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "token_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ticket_attachments": { + "name": "ticket_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "attachment_id": { + "name": "attachment_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "media_lit_id": { + "name": "media_lit_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ticket_attachments_ticket_idx": { + "name": "ticket_attachments_ticket_idx", + "columns": [ + { + "expression": "ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ticket_attachments_message_idx": { + "name": "ticket_attachments_message_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ticket_attachments_team_id_teams_id_fk": { + "name": "ticket_attachments_team_id_teams_id_fk", + "tableFrom": "ticket_attachments", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ticket_attachments_ticket_id_tickets_id_fk": { + "name": "ticket_attachments_ticket_id_tickets_id_fk", + "tableFrom": "ticket_attachments", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ticket_attachments_message_id_ticket_messages_id_fk": { + "name": "ticket_attachments_message_id_ticket_messages_id_fk", + "tableFrom": "ticket_attachments", + "tableTo": "ticket_messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ticket_attachments_attachment_id_unique": { + "name": "ticket_attachments_attachment_id_unique", + "nullsNotDistinct": false, + "columns": [ + "attachment_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "attachment_id_prefix_check": { + "name": "attachment_id_prefix_check", + "value": "\"attachment_id\" LIKE 'att\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.ticket_mail_outbox": { + "name": "ticket_mail_outbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ticket_mail_outbox_ticket_seq_idx": { + "name": "ticket_mail_outbox_ticket_seq_idx", + "columns": [ + { + "expression": "ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ticket_mail_outbox_poll_idx": { + "name": "ticket_mail_outbox_poll_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ticket_mail_outbox_team_id_teams_id_fk": { + "name": "ticket_mail_outbox_team_id_teams_id_fk", + "tableFrom": "ticket_mail_outbox", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ticket_mail_outbox_ticket_id_tickets_id_fk": { + "name": "ticket_mail_outbox_ticket_id_tickets_id_fk", + "tableFrom": "ticket_mail_outbox", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ticket_messages": { + "name": "ticket_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_account_id": { + "name": "author_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ticket_messages_ticket_idx": { + "name": "ticket_messages_ticket_idx", + "columns": [ + { + "expression": "ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ticket_messages_team_id_teams_id_fk": { + "name": "ticket_messages_team_id_teams_id_fk", + "tableFrom": "ticket_messages", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ticket_messages_ticket_id_tickets_id_fk": { + "name": "ticket_messages_ticket_id_tickets_id_fk", + "tableFrom": "ticket_messages", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ticket_messages_author_account_id_accounts_id_fk": { + "name": "ticket_messages_author_account_id_accounts_id_fk", + "tableFrom": "ticket_messages", + "tableTo": "accounts", + "columnsFrom": [ + "author_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ticket_messages_message_id_unique": { + "name": "ticket_messages_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "message_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "message_id_prefix_check": { + "name": "message_id_prefix_check", + "value": "\"message_id\" LIKE 'tmsg\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + }, + "public.tickets": { + "name": "tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "requester_email": { + "name": "requester_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requester_name": { + "name": "requester_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'form'" + }, + "access_token_hash": { + "name": "access_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee_account_id": { + "name": "assignee_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_state": { + "name": "ai_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "ai_generation": { + "name": "ai_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ai_trigger_message_id": { + "name": "ai_trigger_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_stop_reason": { + "name": "ai_stop_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ai_stopped_at": { + "name": "ai_stopped_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tickets_team_status_idx": { + "name": "tickets_team_status_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_message_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tickets_team_id_teams_id_fk": { + "name": "tickets_team_id_teams_id_fk", + "tableFrom": "tickets", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tickets_assignee_account_id_accounts_id_fk": { + "name": "tickets_assignee_account_id_accounts_id_fk", + "tableFrom": "tickets", + "tableTo": "accounts", + "columnsFrom": [ + "assignee_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tickets_ticket_id_unique": { + "name": "tickets_ticket_id_unique", + "nullsNotDistinct": false, + "columns": [ + "ticket_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "ticket_id_prefix_check": { + "name": "ticket_id_prefix_check", + "value": "\"ticket_id\" LIKE 'tkt\\_%' ESCAPE '\\'" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/_journal.json b/apps/api/drizzle/meta/_journal.json index bdf2a94..5e5790a 100644 --- a/apps/api/drizzle/meta/_journal.json +++ b/apps/api/drizzle/meta/_journal.json @@ -92,6 +92,13 @@ "when": 1792922899930, "tag": "0012_openapi_reference", "breakpoints": true + }, + { + "idx": 13, + "version": "7", + "when": 1793442278464, + "tag": "0013_redirect_domains", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/api/src/auth/operation-policy.coverage.test.ts b/apps/api/src/auth/operation-policy.coverage.test.ts index ac654c1..be35ada 100644 --- a/apps/api/src/auth/operation-policy.coverage.test.ts +++ b/apps/api/src/auth/operation-policy.coverage.test.ts @@ -116,7 +116,7 @@ describe("fail-closed operation policy coverage", () => { it("registers every MCP tool with an explicit policy", () => { const policies = listMcpToolPolicies(); - expect(Object.keys(policies).length).toBe(135); + expect(Object.keys(policies).length).toBe(139); for (const policy of Object.values(policies)) { expect(policy.oauthScope || policy.scope).toBeTruthy(); expect(["allow", "deny"]).toContain(policy.apiKeyAccess); diff --git a/apps/api/src/auth/operation-policy.ts b/apps/api/src/auth/operation-policy.ts index 7aba5b8..e129c8f 100644 --- a/apps/api/src/auth/operation-policy.ts +++ b/apps/api/src/auth/operation-policy.ts @@ -532,6 +532,19 @@ export const REST_OPERATION_POLICIES = { "domains:write", MCP_SCOPES.domainsWrite, ), + "domains.addRedirectDomain": teamPermission("domains:write", MCP_SCOPES.domainsWrite), + "domains.updateRedirectDomain": teamPermission( + "domains:write", + MCP_SCOPES.domainsWrite, + ), + "domains.verifyRedirectDomain": teamPermission( + "domains:write", + MCP_SCOPES.domainsWrite, + ), + "domains.removeRedirectDomain": teamPermission( + "domains:write", + MCP_SCOPES.domainsWrite, + ), "settings.get": teamPermission("settings:read", MCP_SCOPES.settingsRead), "settings.update": teamPermission("settings:write", MCP_SCOPES.settingsWrite), diff --git a/apps/api/src/db/schema.ts b/apps/api/src/db/schema.ts index aa9d0dd..eb74645 100644 --- a/apps/api/src/db/schema.ts +++ b/apps/api/src/db/schema.ts @@ -130,6 +130,39 @@ export const customDomains = pgTable( ], ); +export type RedirectRule = { from: string; to: string }; + +/** Extra hosts that only 301 to the team's website, e.g. an old docs host + * (`docs.example.com/cli` -> `example.com/docs/cli`). `pathPrefix` is put in + * front of every path; `rules` send exact old paths to new ones first. Each + * host proves ownership with its own TXT token. */ +export const redirectDomains = pgTable( + "redirect_domains", + { + id: text("id").primaryKey().$defaultFn(genId), + teamId: text("team_id") + .notNull() + .references(() => teams.id, { onDelete: "cascade" }), + hostname: text("hostname").notNull().unique(), + pathPrefix: text("path_prefix").notNull().default(""), + rules: jsonb("rules").$type().notNull().default([]), + verificationToken: text("verification_token").notNull(), + txtSatisfied: boolean("txt_satisfied"), + routingSatisfied: boolean("routing_satisfied"), + status: text("status", { enum: customDomainStatuses }).notNull().default("pending"), + verifiedAt: timestamp("verified_at"), + createdAt: timestamp("created_at").notNull().defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), + }, + (table) => [ + index("redirect_domains_team_idx").on(table.teamId), + check( + "redirect_domains_verified_at_check", + sql`("status" <> 'verified' OR "verified_at" IS NOT NULL)`, + ), + ], +); + export const teamMembers = pgTable( "team_members", { diff --git a/apps/api/src/domains/contract.test.ts b/apps/api/src/domains/contract.test.ts index 497d43d..34143ff 100644 --- a/apps/api/src/domains/contract.test.ts +++ b/apps/api/src/domains/contract.test.ts @@ -1,10 +1,12 @@ import { describe, expect, it } from "vitest"; import { + addRedirectDomainBodySchema, contract, domainSettingsSchema, setHideFromSearchBodySchema, setPrimaryCustomDomainBodySchema, setRedirectSubdomainBodySchema, + updateRedirectDomainBodySchema, } from "@frontlit/api-contract"; describe("domains contract", () => { @@ -45,6 +47,7 @@ describe("domains contract", () => { redirectSubdomain: true, hideFromSearch: false, platformDomain: "frontlit.app", + redirectDomains: [], }); expect(settings.customDomains).toHaveLength(2); expect(setRedirectSubdomainBodySchema.parse({ redirectSubdomain: false })).toEqual({ @@ -58,4 +61,43 @@ describe("domains contract", () => { setPrimaryCustomDomainBodySchema.parse({ hostname: "WWW.Example.com" }), ).toEqual({ hostname: "www.example.com" }); }); + + it("validates redirect domain prefixes and per-path rules", () => { + expect(contract.domains.updateRedirectDomain.path).toBe( + "/domain/redirect-domains/:hostname", + ); + expect(addRedirectDomainBodySchema.parse({ hostname: "Docs.Example.com" })).toEqual( + { + hostname: "docs.example.com", + pathPrefix: "", + }, + ); + for (const pathPrefix of ["/docs", "/docs/v2", ""]) + expect(updateRedirectDomainBodySchema.safeParse({ pathPrefix }).success).toBe( + true, + ); + for (const pathPrefix of ["docs", "/docs/", "/a b", "https://x.com"]) + expect(updateRedirectDomainBodySchema.safeParse({ pathPrefix }).success).toBe( + false, + ); + expect( + updateRedirectDomainBodySchema.safeParse({ + rules: [{ from: "/api/uploadMedia", to: "/docs/api/upload-media" }], + }).success, + ).toBe(true); + // A rule must be a site path, and each old path has one rule. + expect( + updateRedirectDomainBodySchema.safeParse({ + rules: [{ from: "/a", to: "https://evil.example" }], + }).success, + ).toBe(false); + expect( + updateRedirectDomainBodySchema.safeParse({ + rules: [ + { from: "/a", to: "/b" }, + { from: "/a/", to: "/c" }, + ], + }).success, + ).toBe(false); + }); }); diff --git a/apps/api/src/domains/public-settings.ts b/apps/api/src/domains/public-settings.ts new file mode 100644 index 0000000..9eb0dac --- /dev/null +++ b/apps/api/src/domains/public-settings.ts @@ -0,0 +1,18 @@ +import type { getDomainSettings } from "./queries"; + +/** Domain settings as the REST API and MCP tools return them (ISO dates). */ +export function toPublicSettings( + settings: NonNullable>>, +) { + return { + ...settings, + customDomains: settings.customDomains.map((host) => ({ + ...host, + verifiedAt: host.verifiedAt?.toISOString() ?? null, + })), + redirectDomains: settings.redirectDomains.map((host) => ({ + ...host, + verifiedAt: host.verifiedAt?.toISOString() ?? null, + })), + }; +} diff --git a/apps/api/src/domains/queries.test.ts b/apps/api/src/domains/queries.test.ts index cb43bca..11e970c 100644 --- a/apps/api/src/domains/queries.test.ts +++ b/apps/api/src/domains/queries.test.ts @@ -15,6 +15,7 @@ import { promises as dns } from "node:dns"; import { db as testDb } from "../db/client"; import { teams } from "../db/schema"; import { + addRedirectDomain, attachCustomDomain, getCustomDomainCnameTarget, getPlatformSiteDomain, @@ -23,12 +24,15 @@ import { normalizeHost, pairedHostname, removeCustomDomain, + removeRedirectDomain, resolveHost, setHideFromSearch, setPrimaryCustomDomain, setRedirectSubdomain, setTeamSubdomain, + updateRedirectDomain, verifyCustomDomain, + verifyRedirectDomain, } from "./queries"; function uniqueName(label: string): string { @@ -516,4 +520,112 @@ describe("team website domains", () => { else process.env.PLATFORM_TEAM_ID = previousTeam; } }); + + describe("redirect domains", () => { + const redirectHost = () => `docs.${crypto.randomUUID().slice(0, 8)}.com`; + const redirectSettings = async (id: string, hostname: string) => + (await getDomainSettings(id))!.redirectDomains.find( + (host) => host.hostname === hostname, + )!; + + it("only redirects once its own TXT and routing records pass", async () => { + const hostname = redirectHost(); + expect(await addRedirectDomain(teamId, hostname.toUpperCase(), "/docs")).toEqual({ + ok: true, + }); + const added = await redirectSettings(teamId, hostname); + expect(added).toMatchObject({ + pathPrefix: "/docs", + rules: [], + status: "pending", + record: { type: "CNAME", values: [getCustomDomainCnameTarget()] }, + verification: { txtName: `_frontlit-verify.${hostname}`, txtSatisfied: null }, + }); + expect(await resolveHost(hostname)).toBeNull(); + + await mockDns({ txt: [[added.verification.txtValue]] }); + expect(await verifyRedirectDomain(teamId, hostname)).toBe(true); + expect(await redirectSettings(teamId, hostname)).toMatchObject({ + status: "failed", + verification: { txtSatisfied: true }, + routingSatisfied: false, + }); + expect(await resolveHost(hostname)).toBeNull(); + + await mockDns({ + txt: [[added.verification.txtValue]], + cname: [getCustomDomainCnameTarget()], + }); + expect(await verifyRedirectDomain(teamId, hostname)).toBe(true); + expect((await redirectSettings(teamId, hostname)).status).toBe("verified"); + const subdomainHost = (await getDomainSettings(teamId))!.subdomain!.hostname; + expect(await resolveHost(`${hostname}:443`)).toEqual({ + teamId, + canonicalHost: subdomainHost, + redirect: true, + hideFromSearch: true, + redirectTo: { pathPrefix: "/docs", rules: [] }, + }); + }); + + it("targets the verified custom domain and carries rule changes", async () => { + const apex = `${crypto.randomUUID().slice(0, 8)}.com`; + await attachAndPassAllChecks(teamId, apex); + const hostname = redirectHost(); + await addRedirectDomain(teamId, hostname, ""); + const { verification } = await redirectSettings(teamId, hostname); + await mockDns({ + txt: [[verification.txtValue]], + cname: [getCustomDomainCnameTarget()], + }); + await verifyRedirectDomain(teamId, hostname); + + const rules = [{ from: "/api/uploadMedia", to: "/docs/api/upload-media" }]; + expect( + await updateRedirectDomain(teamId, hostname, { pathPrefix: "/docs", rules }), + ).toBe(true); + expect(await resolveHost(hostname)).toEqual({ + teamId, + canonicalHost: apex, + redirect: true, + hideFromSearch: false, + redirectTo: { pathPrefix: "/docs", rules }, + }); + + expect(await removeRedirectDomain(teamId, hostname)).toBe(true); + expect(await resolveHost(hostname)).toBeNull(); + expect(await removeRedirectDomain(teamId, hostname)).toBe(false); + }); + + it("keeps custom and redirect hosts apart and scoped to their team", async () => { + const apex = `${crypto.randomUUID().slice(0, 8)}.com`; + await attachCustomDomain(teamId, apex); + expect(await addRedirectDomain(teamId, `www.${apex}`, "")).toEqual({ + ok: false, + reason: "taken", + }); + expect( + await addRedirectDomain(teamId, `docs.${getPlatformSiteDomain()}`, ""), + ).toEqual({ ok: false, reason: "reserved" }); + + const hostname = redirectHost(); + await addRedirectDomain(teamId, hostname, ""); + const { team: other } = await seedTeam(db, { name: uniqueName("Other") }); + expect(await addRedirectDomain(other.id, hostname, "")).toEqual({ + ok: false, + reason: "taken", + }); + expect(await attachCustomDomain(other.id, hostname)).toEqual({ + ok: false, + reason: "taken", + }); + // Another team can't change, verify or remove it. + expect(await updateRedirectDomain(other.id, hostname, { pathPrefix: "/x" })).toBe( + false, + ); + expect(await verifyRedirectDomain(other.id, hostname)).toBe(false); + expect(await removeRedirectDomain(other.id, hostname)).toBe(false); + expect((await redirectSettings(teamId, hostname)).pathPrefix).toBe(""); + }); + }); }); diff --git a/apps/api/src/domains/queries.ts b/apps/api/src/domains/queries.ts index d5ddddf..6a27be8 100644 --- a/apps/api/src/domains/queries.ts +++ b/apps/api/src/domains/queries.ts @@ -1,9 +1,15 @@ import { randomBytes } from "node:crypto"; import { promises as dns } from "node:dns"; -import { eq, inArray, or, sql } from "drizzle-orm"; +import { and, eq, inArray, or, sql } from "drizzle-orm"; import { db } from "../db/client"; import { supportsRowLocks, withDbTransaction } from "../db/transaction"; -import { customDomains, teams, type CustomDomainStatus } from "../db/schema"; +import { + customDomains, + redirectDomains, + teams, + type CustomDomainStatus, + type RedirectRule, +} from "../db/schema"; /** Labels that must never be claimed as a team slug. */ export const RESERVED_SUBDOMAIN_LABELS = new Set([ @@ -153,6 +159,13 @@ function ownershipHostname(hostname: string): string { return paired && isApexHostname(paired) ? paired : hostname; } +/** Platform hosts belong to FrontLit, except the platform team's own apex. */ +function isReservedHost(teamId: string, host: string): boolean { + const platform = getPlatformSiteDomain(); + const platformApex = isPlatformTeam(teamId) ? [platform, `www.${platform}`] : []; + return (host === platform || isPlatformHost(host)) && !platformApex.includes(host); +} + type TeamRow = typeof teams.$inferSelect; type CustomDomainRow = typeof customDomains.$inferSelect; @@ -168,6 +181,13 @@ function canonicalHostFor( return team.subdomain ? getSubdomainHost(team.subdomain) : null; } +function listRedirectDomains(teamId: string) { + return db.query.redirectDomains.findMany({ + where: eq(redirectDomains.teamId, teamId), + orderBy: (table, { asc }) => [asc(table.hostname)], + }); +} + function listCustomDomains(teamId: string): Promise { return db.query.customDomains.findMany({ where: eq(customDomains.teamId, teamId), @@ -197,6 +217,16 @@ export type DomainSettings = { redirectSubdomain: boolean; hideFromSearch: boolean; platformDomain: string; + redirectDomains: { + hostname: string; + pathPrefix: string; + rules: RedirectRule[]; + status: CustomDomainStatus; + verifiedAt: Date | null; + record: { type: "A" | "CNAME"; values: string[] }; + routingSatisfied: boolean | null; + verification: { txtName: string; txtValue: string; txtSatisfied: boolean | null }; + }[]; }; function routingRecord( @@ -218,6 +248,7 @@ export async function getDomainSettings( }); if (!team) return null; const hosts = await listCustomDomains(teamId); + const redirects = await listRedirectDomains(teamId); return { subdomain: team.subdomain @@ -243,6 +274,20 @@ export async function getDomainSettings( redirectSubdomain: team.redirectSubdomain, hideFromSearch: team.hideFromSearch, platformDomain: getPlatformSiteDomain(), + redirectDomains: redirects.map((host) => ({ + hostname: host.hostname, + pathPrefix: host.pathPrefix, + rules: host.rules, + status: host.status, + verifiedAt: host.verifiedAt, + record: routingRecord(team, host.hostname), + routingSatisfied: host.routingSatisfied, + verification: { + txtName: `${getCustomDomainTxtRecordName(team)}.${host.hostname}`, + txtValue: host.verificationToken, + txtSatisfied: host.txtSatisfied, + }, + })), }; } @@ -301,14 +346,7 @@ export async function attachCustomDomain( const hostname = normalizeHost(value); const paired = pairedHostname(hostname); const hostnames = paired ? [hostname, paired] : [hostname]; - const platform = getPlatformSiteDomain(); - const platformApex = isPlatformTeam(teamId) ? [platform, `www.${platform}`] : []; - if ( - hostnames.some( - (host) => - (host === platform || isPlatformHost(host)) && !platformApex.includes(host), - ) - ) + if (hostnames.some((host) => isReservedHost(teamId, host))) return { ok: false, reason: "reserved" }; try { @@ -332,6 +370,11 @@ export async function attachCustomDomain( if (existing.some((row) => row.teamId === teamId)) return { ok: false, reason: "already_attached" }; if (existing.length > 0) return { ok: false, reason: "taken" }; + const redirecting = await tx + .select({ hostname: redirectDomains.hostname }) + .from(redirectDomains) + .where(inArray(redirectDomains.hostname, hostnames)); + if (redirecting.length > 0) return { ok: false, reason: "taken" }; await tx.insert(customDomains).values( hostnames.map((host) => ({ @@ -529,6 +572,111 @@ export async function verifyCustomDomain(teamId: string): Promise { return true; } +export type AddRedirectDomainResult = + | { ok: true } + | { ok: false; reason: "subdomain_required" | "reserved" | "taken" }; + +/** Adds a host that only 301-redirects to the website, e.g. an old docs + * host. It has its own TXT token, so it can live in any zone. */ +export async function addRedirectDomain( + teamId: string, + value: string, + pathPrefix: string, +): Promise { + const hostname = normalizeHost(value); + if (isReservedHost(teamId, hostname)) return { ok: false, reason: "reserved" }; + const team = await db.query.teams.findFirst({ where: eq(teams.id, teamId) }); + if (!team?.subdomain) return { ok: false, reason: "subdomain_required" }; + const used = await db.query.customDomains.findFirst({ + where: eq(customDomains.hostname, hostname), + }); + if (used) return { ok: false, reason: "taken" }; + try { + await db.insert(redirectDomains).values({ + teamId, + hostname, + pathPrefix, + verificationToken: randomBytes(16).toString("hex"), + }); + return { ok: true }; + } catch (error) { + if (isUniqueViolation(error)) return { ok: false, reason: "taken" }; + throw error; + } +} + +export async function updateRedirectDomain( + teamId: string, + value: string, + changes: { pathPrefix?: string; rules?: RedirectRule[] }, +): Promise { + const updated = await db + .update(redirectDomains) + .set({ ...changes, updatedAt: new Date() }) + .where( + and( + eq(redirectDomains.teamId, teamId), + eq(redirectDomains.hostname, normalizeHost(value)), + ), + ) + .returning({ id: redirectDomains.id }); + return updated.length > 0; +} + +export async function removeRedirectDomain( + teamId: string, + value: string, +): Promise { + const removed = await db + .delete(redirectDomains) + .where( + and( + eq(redirectDomains.teamId, teamId), + eq(redirectDomains.hostname, normalizeHost(value)), + ), + ) + .returning({ id: redirectDomains.id }); + return removed.length > 0; +} + +/** Same checks as a custom domain: the host's TXT token and its routing + * record. It starts redirecting once both pass. */ +export async function verifyRedirectDomain( + teamId: string, + value: string, +): Promise { + const team = await db.query.teams.findFirst({ where: eq(teams.id, teamId) }); + const host = await db.query.redirectDomains.findFirst({ + where: and( + eq(redirectDomains.teamId, teamId), + eq(redirectDomains.hostname, normalizeHost(value)), + ), + }); + if (!team || !host) return false; + const txtSatisfied = await verifyDnsTxtRecord( + host.hostname, + host.verificationToken, + getCustomDomainTxtRecordName(team), + ); + const routingSatisfied = await verifyDnsRouting( + host.hostname, + getCustomDomainCnameTarget(team), + ); + const verified = txtSatisfied && routingSatisfied; + const now = new Date(); + await db + .update(redirectDomains) + .set({ + status: verified ? "verified" : "failed", + verifiedAt: verified ? now : host.verifiedAt, + txtSatisfied, + routingSatisfied, + updatedAt: now, + }) + .where(eq(redirectDomains.id, host.id)); + return true; +} + export type ResolvedHost = { teamId: string; canonicalHost: string; @@ -536,6 +684,8 @@ export type ResolvedHost = { redirect: boolean; /** Team-wide pre-launch switch: noindex everywhere, robots disallow all. */ hideFromSearch: boolean; + /** Set for a redirect domain: where its paths go on `canonicalHost`. */ + redirectTo?: { pathPrefix: string; rules: RedirectRule[] }; }; /** Resolves an incoming host to exactly one team website. @@ -556,6 +706,28 @@ export async function resolveHost(requestHost: string): Promise { const hosts: string[] = []; if (settings?.subdomain) hosts.push(settings.subdomain.hostname); for (const host of settings?.customDomains ?? []) hosts.push(host.hostname); + for (const host of settings?.redirectDomains ?? []) hosts.push(host.hostname); return hosts; } diff --git a/apps/api/src/domains/routes.ts b/apps/api/src/domains/routes.ts index a31a51b..2978f19 100644 --- a/apps/api/src/domains/routes.ts +++ b/apps/api/src/domains/routes.ts @@ -5,31 +5,31 @@ import { requireAuth } from "../auth/middleware"; import { requireTeam } from "../auth/require-team"; import { authorizeHandlers } from "../auth/authorize"; import { + addRedirectDomain, attachCustomDomain, getDomainSettings, removeCustomDomain, + removeRedirectDomain, setPrimaryCustomDomain, setRedirectSubdomain, setHideFromSearch, setTeamSubdomain, + updateRedirectDomain, verifyCustomDomain, + verifyRedirectDomain, } from "./queries"; import { revalidateAfterDomainChange } from "./revalidate"; +import { toPublicSettings } from "./public-settings"; const router = Router(); router.use("/domain", requireAuth, requireTeam); const s = initServer(); -function toPublicSettings( - settings: NonNullable>>, -) { +async function settingsResponse(teamId: string) { return { - ...settings, - customDomains: settings.customDomains.map((host) => ({ - ...host, - verifiedAt: host.verifiedAt?.toISOString() ?? null, - })), + status: 200 as const, + body: toPublicSettings((await getDomainSettings(teamId))!), }; } @@ -152,6 +152,41 @@ const impl = s.router( body: toPublicSettings((await getDomainSettings((req as any).teamId))!), }; }, + addRedirectDomain: async ({ body, req }) => { + const teamId = (req as any).teamId; + const result = await addRedirectDomain(teamId, body.hostname, body.pathPrefix); + if (!result.ok) { + const messages = { + subdomain_required: + "Choose a FrontLit subdomain before adding a redirect domain", + reserved: "That hostname is reserved for default team subdomains", + taken: "That domain is already in use as a custom or redirect domain", + }; + return { status: 409, body: { error: messages[result.reason] } }; + } + return settingsResponse(teamId); + }, + updateRedirectDomain: async ({ params, body, req }) => { + const teamId = (req as any).teamId; + if (!(await updateRedirectDomain(teamId, params.hostname, body))) + return { status: 404, body: { error: "That redirect domain is not added" } }; + await revalidateAfterDomainChange(teamId); + return settingsResponse(teamId); + }, + verifyRedirectDomain: async ({ params, req }) => { + const teamId = (req as any).teamId; + if (!(await verifyRedirectDomain(teamId, params.hostname))) + return { status: 404, body: { error: "That redirect domain is not added" } }; + await revalidateAfterDomainChange(teamId); + return settingsResponse(teamId); + }, + removeRedirectDomain: async ({ params, req }) => { + const teamId = (req as any).teamId; + if (!(await removeRedirectDomain(teamId, params.hostname))) + return { status: 404, body: { error: "That redirect domain is not added" } }; + await revalidateAfterDomainChange(teamId, [params.hostname]); + return settingsResponse(teamId); + }, }, { get: "domains.get", @@ -162,6 +197,10 @@ const impl = s.router( setPrimaryCustomDomain: "domains.setPrimaryCustomDomain", verifyCustomDomain: "domains.verifyCustomDomain", removeCustomDomain: "domains.removeCustomDomain", + addRedirectDomain: "domains.addRedirectDomain", + updateRedirectDomain: "domains.updateRedirectDomain", + verifyRedirectDomain: "domains.verifyRedirectDomain", + removeRedirectDomain: "domains.removeRedirectDomain", }, ), ); diff --git a/apps/api/src/mcp/policy.ts b/apps/api/src/mcp/policy.ts index 37dfadd..98b34ad 100644 --- a/apps/api/src/mcp/policy.ts +++ b/apps/api/src/mcp/policy.ts @@ -190,6 +190,10 @@ const toolScopes = { set_primary_custom_domain: MCP_SCOPES.domainsWrite, verify_custom_domain: MCP_SCOPES.domainsWrite, remove_custom_domain: MCP_SCOPES.domainsWrite, + add_redirect_domain: MCP_SCOPES.domainsWrite, + update_redirect_domain: MCP_SCOPES.domainsWrite, + verify_redirect_domain: MCP_SCOPES.domainsWrite, + remove_redirect_domain: MCP_SCOPES.domainsWrite, get_team_settings: MCP_SCOPES.settingsRead, update_team_settings: MCP_SCOPES.settingsWrite, @@ -220,6 +224,8 @@ export const HIGH_IMPACT_MCP_TOOLS = new Set([ "send_broadcast", "attach_custom_domain", "remove_custom_domain", + "add_redirect_domain", + "remove_redirect_domain", ]); export type McpHumanRequirement = diff --git a/apps/api/src/mcp/server.test.ts b/apps/api/src/mcp/server.test.ts index aa4e9e4..bad69f2 100644 --- a/apps/api/src/mcp/server.test.ts +++ b/apps/api/src/mcp/server.test.ts @@ -74,9 +74,9 @@ describe("FrontLit MCP server", () => { const result = await client.listTools(); const policyNames = Object.keys(listMcpToolPolicies()).sort(); - expect(result.tools).toHaveLength(135); + expect(result.tools).toHaveLength(139); expect(result.tools.map((tool) => tool.name).sort()).toEqual(policyNames); - expect(new Set(result.tools.map((tool) => tool.name)).size).toBe(135); + expect(new Set(result.tools.map((tool) => tool.name)).size).toBe(139); expect(result.tools.map((tool) => tool.name)).toEqual( expect.arrayContaining([ "list_articles", diff --git a/apps/api/src/mcp/tools/domains.ts b/apps/api/src/mcp/tools/domains.ts index be5f346..320b164 100644 --- a/apps/api/src/mcp/tools/domains.ts +++ b/apps/api/src/mcp/tools/domains.ts @@ -1,38 +1,35 @@ import type { McpToolRegistrar } from "../tool-registry"; import { z } from "zod"; -import { domainSettingsSchema } from "@frontlit/api-contract"; import { + domainSettingsSchema, + redirectPathPrefixSchema, + redirectRulesSchema, +} from "@frontlit/api-contract"; +import { + addRedirectDomain, attachCustomDomain, getDomainSettings, removeCustomDomain, + removeRedirectDomain, setPrimaryCustomDomain, setRedirectSubdomain, setHideFromSearch, setTeamSubdomain, + updateRedirectDomain, verifyCustomDomain, + verifyRedirectDomain, } from "../../domains/queries"; import { revalidateAfterDomainChange } from "../../domains/revalidate"; +import { toPublicSettings } from "../../domains/public-settings"; import { AUTH_ERROR, INTERNAL_ERROR, errorResult, jsonResult } from "./responses"; import { getTeamId } from "./auth"; -function toPublicSettings( - settings: NonNullable>>, -) { - return { - ...settings, - customDomains: settings.customDomains.map((host) => ({ - ...host, - verifiedAt: host.verifiedAt?.toISOString() ?? null, - })), - }; -} - export function registerDomainTools(server: McpToolRegistrar): void { server.registerTool( "get_domain_settings", { description: - "Gets the current team's domain settings: default subdomain, the custom domain's hosts (an apex/www pair) with their per-host DNS records and status, the shared ownership TXT record, the canonical host, and whether the subdomain redirects to it.", + "Gets the current team's domain settings: default subdomain, the custom domain's hosts (an apex/www pair) with their per-host DNS records and status, the shared ownership TXT record, the canonical host, whether the subdomain redirects to it, and the redirect domains with their DNS records, path prefix and rules.", inputSchema: {}, outputSchema: domainSettingsSchema, annotations: { @@ -256,4 +253,125 @@ export function registerDomainTools(server: McpToolRegistrar): void { } }, ); + + const notAdded = "That redirect domain is not added to this team"; + + server.registerTool( + "add_redirect_domain", + { + description: + "Adds a redirect domain to the current team and starts DNS verification. Once verified it serves nothing itself: every request 301-redirects to the website's canonical host, with `pathPrefix` put in front of the path. Use it to keep an old host's links working, e.g. `docs.example.com` with prefix `/docs` sends `/cli` to `example.com/docs/cli`. The host needs its own TXT record (see get_domain_settings) plus the usual routing record.", + inputSchema: { + hostname: z.string().min(1), + pathPrefix: redirectPathPrefixSchema.optional(), + }, + outputSchema: domainSettingsSchema, + annotations: { + readOnlyHint: false, + destructiveHint: false, + openWorldHint: false, + }, + }, + async (args: any, extra: any) => { + const teamId = getTeamId(extra); + if (!teamId) return AUTH_ERROR; + try { + const result = await addRedirectDomain( + teamId, + args.hostname, + args.pathPrefix ?? "", + ); + if (!result.ok) return errorResult(result.reason); + return jsonResult(toPublicSettings((await getDomainSettings(teamId))!)); + } catch { + return INTERNAL_ERROR; + } + }, + ); + + server.registerTool( + "update_redirect_domain", + { + description: + "Changes a redirect domain's path prefix and/or its per-path rules. A rule sends one exact old path (trailing slash ignored) to a site path and wins over the prefix, e.g. `/api/uploadMedia` -> `/docs/api/upload-media`. Sending `rules` replaces the whole list.", + inputSchema: { + hostname: z.string().min(1), + pathPrefix: redirectPathPrefixSchema.optional(), + rules: redirectRulesSchema.optional(), + }, + outputSchema: domainSettingsSchema, + annotations: { + readOnlyHint: false, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + async (args: any, extra: any) => { + const teamId = getTeamId(extra); + if (!teamId) return AUTH_ERROR; + try { + const updated = await updateRedirectDomain(teamId, args.hostname, { + ...(args.pathPrefix !== undefined ? { pathPrefix: args.pathPrefix } : {}), + ...(args.rules !== undefined ? { rules: args.rules } : {}), + }); + if (!updated) return errorResult(notAdded); + await revalidateAfterDomainChange(teamId); + return jsonResult(toPublicSettings((await getDomainSettings(teamId))!)); + } catch { + return INTERNAL_ERROR; + } + }, + ); + + server.registerTool( + "verify_redirect_domain", + { + description: + "Re-checks a redirect domain's own ownership TXT record and its routing record. It starts redirecting once both pass.", + inputSchema: { hostname: z.string().min(1) }, + outputSchema: domainSettingsSchema, + annotations: { + readOnlyHint: false, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + }, + async (args: any, extra: any) => { + const teamId = getTeamId(extra); + if (!teamId) return AUTH_ERROR; + try { + if (!(await verifyRedirectDomain(teamId, args.hostname))) + return errorResult(notAdded); + await revalidateAfterDomainChange(teamId); + return jsonResult(toPublicSettings((await getDomainSettings(teamId))!)); + } catch { + return INTERNAL_ERROR; + } + }, + ); + + server.registerTool( + "remove_redirect_domain", + { + description: + "Removes a redirect domain from the current team. Its links stop redirecting.", + inputSchema: { hostname: z.string().min(1) }, + outputSchema: domainSettingsSchema, + annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }, + }, + async (args: any, extra: any) => { + const teamId = getTeamId(extra); + if (!teamId) return AUTH_ERROR; + try { + if (!(await removeRedirectDomain(teamId, args.hostname))) + return errorResult(notAdded); + await revalidateAfterDomainChange(teamId, [args.hostname]); + return jsonResult(toPublicSettings((await getDomainSettings(teamId))!)); + } catch { + return INTERNAL_ERROR; + } + }, + ); } diff --git a/apps/api/src/public/routes.ts b/apps/api/src/public/routes.ts index eff524c..3316730 100644 --- a/apps/api/src/public/routes.ts +++ b/apps/api/src/public/routes.ts @@ -303,6 +303,7 @@ const impl = s.router(contract.public, { teamId: team.teamId, canonicalHost: resolved.canonicalHost, redirect: resolved.redirect, + ...(resolved.redirectTo ? { redirectTo: resolved.redirectTo } : {}), hideFromSearch: resolved.hideFromSearch, }, }; @@ -1059,7 +1060,8 @@ router.get( /** Caddy on-demand TLS `ask` adapter. Caddy sends `GET ?domain=` and * issues a certificate only when this returns 200. Same host authority as * `GET /public/resolve-host`, so redirecting hosts (the other half of a - * verified apex/www pair, a redirected subdomain) get certificates too — + * verified apex/www pair, a redirected subdomain, a verified redirect + * domain) get certificates too — * an HTTPS 301 needs one. */ router.get("/public/tls-ask", publicReadLimiter, async (req, res) => { const domain = String(req.query.domain ?? req.query.host ?? ""); diff --git a/apps/sites/lib/api.ts b/apps/sites/lib/api.ts index 987c67b..de385aa 100644 --- a/apps/sites/lib/api.ts +++ b/apps/sites/lib/api.ts @@ -77,6 +77,8 @@ export interface ResolvedHost { /** True when this host should 301 to `canonicalHost`. */ redirect: boolean; hideFromSearch: boolean; + /** Set for a redirect domain: where its paths go on `canonicalHost`. */ + redirectTo?: { pathPrefix: string; rules: { from: string; to: string }[] }; } export function resolveHost(host: string): Promise { diff --git a/apps/sites/lib/host.test.ts b/apps/sites/lib/host.test.ts index 568483e..0f641a2 100644 --- a/apps/sites/lib/host.test.ts +++ b/apps/sites/lib/host.test.ts @@ -58,6 +58,47 @@ describe("decideHost", () => { }); }); + it("sends a redirect domain's paths under its prefix, rules first", () => { + const resolved = { + teamId: "team_abc", + canonicalHost: "medialit.cloud", + redirect: true, + hideFromSearch: false, + redirectTo: { + pathPrefix: "/docs", + rules: [ + { from: "/api/uploadMedia", to: "/docs/api/upload-media" }, + { from: "/llms.txt/", to: "/docs?from=llms" }, + ], + }, + }; + const go = (pathname: string, search = "") => + decideHost(resolved, { ...url, pathname, search }); + expect(go("/cli", "?x=1")).toEqual({ + action: "redirect", + location: "https://medialit.cloud/docs/cli?x=1", + }); + expect(go("/")).toEqual({ + action: "redirect", + location: "https://medialit.cloud/docs", + }); + expect(go("/api/uploadMedia/", "?v=2")).toEqual({ + action: "redirect", + location: "https://medialit.cloud/docs/api/upload-media?v=2", + }); + // A rule with its own query keeps it instead of the request's. + expect(go("/llms.txt", "?x=1")).toEqual({ + action: "redirect", + location: "https://medialit.cloud/docs?from=llms", + }); + expect( + decideHost( + { ...resolved, redirectTo: { pathPrefix: "", rules: [] } }, + { ...url, pathname: "/" }, + ), + ).toEqual({ action: "redirect", location: "https://medialit.cloud/?ref=x" }); + }); + it("normalizes ports and casing before cache/API lookup", () => { expect(normalizeHost("Acme.Localhost:3002")).toBe("acme.localhost"); }); diff --git a/apps/sites/lib/host.ts b/apps/sites/lib/host.ts index 286e49f..628aa43 100644 --- a/apps/sites/lib/host.ts +++ b/apps/sites/lib/host.ts @@ -24,14 +24,36 @@ export function decideHost( if (!resolved) return { action: "unknown" }; if (resolved.redirect) { const port = requestUrl.port ? `:${requestUrl.port}` : ""; + const target = resolved.redirectTo + ? redirectDomainTarget(resolved.redirectTo, requestUrl) + : `${requestUrl.pathname}${requestUrl.search}`; return { action: "redirect", - location: `${requestUrl.protocol}//${resolved.canonicalHost}${port}${requestUrl.pathname}${requestUrl.search}`, + location: `${requestUrl.protocol}//${resolved.canonicalHost}${port}${target}`, }; } return { action: "rewrite", teamId: resolved.teamId }; } +/** Path and query on the website for a request to a redirect domain. A + * rule for the exact path (trailing slash ignored) wins; any other path + * keeps its query and gets the prefix: `/cli` -> `/docs/cli`. */ +export function redirectDomainTarget( + redirectTo: NonNullable, + requestUrl: { pathname: string; search: string }, +): string { + const path = requestUrl.pathname.replace(/\/+$/, "") || "/"; + const rule = redirectTo.rules.find( + (candidate) => (candidate.from.replace(/\/+$/, "") || "/") === path, + ); + if (rule) return rule.to.includes("?") ? rule.to : `${rule.to}${requestUrl.search}`; + const prefixed = + requestUrl.pathname === "/" + ? redirectTo.pathPrefix || "/" + : `${redirectTo.pathPrefix}${requestUrl.pathname}`; + return `${prefixed}${requestUrl.search}`; +} + /** Normalize a browser/edge Host header before using it as a cache key or * calling the API. In local development it is usually `slug.localhost:3002`. */ export function normalizeHost(value: string): string { diff --git a/apps/web/app/(workspace)/website/domain/dns-records.tsx b/apps/web/app/(workspace)/website/domain/dns-records.tsx new file mode 100644 index 0000000..ce64d99 --- /dev/null +++ b/apps/web/app/(workspace)/website/domain/dns-records.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { useState } from "react"; +import { Check, Copy } from "lucide-react"; +import type { VariantProps } from "class-variance-authority"; +import { IconButton } from "@/components/ui/codelit/icon-button"; +import { Badge, type badgeVariants } from "@/components/ui/codelit/badge"; + +export const STATUS_VARIANT: Record< + string, + NonNullable["variant"]> +> = { + pending: "warning", + verified: "success", + failed: "destructive", +}; + +function recordState(value: boolean | null): string { + if (value === null) return "not checked"; + return value ? "satisfied" : "unsatisfied"; +} + +function RecordStateBadge({ value }: { value: boolean | null }) { + return {recordState(value)}; +} + +export function DnsRecordRow({ + type, + name, + value, + satisfied, +}: { + type: string; + name: string; + value: string; + satisfied: boolean | null; +}) { + return ( + + {type} + + {name} + + + {value} + + + + + + + + + ); +} + +function CopyValueButton({ value }: { value: string }) { + const [copied, setCopied] = useState(false); + + async function copy() { + try { + await navigator.clipboard.writeText(value); + setCopied(true); + window.setTimeout(() => setCopied(false), 1500); + } catch { + // Clipboard can be unavailable over insecure origins. + } + } + + return ( + void copy()} + > + {copied ? : } + + ); +} + +/** The DNS records to create for a set of hosts, with copy buttons. */ +export function DnsRecordsTable({ children }: { children: React.ReactNode }) { + return ( +
+ + + + + + + + + + + {children} +
TypeNameValueStatus + Copy +
+
+ ); +} diff --git a/apps/web/app/(workspace)/website/domain/page.tsx b/apps/web/app/(workspace)/website/domain/page.tsx index 6373f68..7fe520a 100644 --- a/apps/web/app/(workspace)/website/domain/page.tsx +++ b/apps/web/app/(workspace)/website/domain/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; -import { Check, Copy, ExternalLink } from "lucide-react"; +import { ExternalLink } from "lucide-react"; import { attachCustomDomain, getDomainSettings, @@ -19,24 +19,15 @@ import { ScrollablePage } from "@/components/workspace/scrollable-page"; import { Banner } from "@/components/workspace/banner"; import { DeleteConfirm } from "@/components/workspace/delete-confirm"; import { Button } from "@/components/ui/codelit/button"; -import { IconButton } from "@/components/ui/codelit/icon-button"; import { Input } from "@/components/ui/codelit/input"; import { Label } from "@/components/ui/codelit/label"; import { Switch } from "@/components/ui/codelit/switch"; -import { Badge, type badgeVariants } from "@/components/ui/codelit/badge"; +import { Badge } from "@/components/ui/codelit/badge"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/codelit/card"; -import type { VariantProps } from "class-variance-authority"; import { useSetBreadcrumb } from "@/components/workspace/breadcrumb-context"; import { siteUrl } from "@/lib/site-url"; - -const STATUS_VARIANT: Record< - string, - NonNullable["variant"]> -> = { - pending: "warning", - verified: "success", - failed: "destructive", -}; +import { DnsRecordRow, DnsRecordsTable, STATUS_VARIANT } from "./dns-records"; +import { RedirectDomainsCard } from "./redirect-domains"; function platformSuffix(settings: DomainSettings): string { if (settings.subdomain?.hostname) @@ -44,70 +35,6 @@ function platformSuffix(settings: DomainSettings): string { return settings.platformDomain; } -function recordState(value: boolean | null): string { - if (value === null) return "not checked"; - return value ? "satisfied" : "unsatisfied"; -} - -function RecordStateBadge({ value }: { value: boolean | null }) { - return {recordState(value)}; -} - -function DnsRecordRow({ - type, - name, - value, - satisfied, -}: { - type: string; - name: string; - value: string; - satisfied: boolean | null; -}) { - return ( - - {type} - - {name} - - - {value} - - - - - - - - - ); -} - -function CopyValueButton({ value }: { value: string }) { - const [copied, setCopied] = useState(false); - - async function copy() { - try { - await navigator.clipboard.writeText(value); - setCopied(true); - window.setTimeout(() => setCopied(false), 1500); - } catch { - // Clipboard can be unavailable over insecure origins. - } - } - - return ( - void copy()} - > - {copied ? : } - - ); -} - export default function DomainPage() { useSetBreadcrumb([ { label: "Website", href: "/website?tab=pages" }, @@ -279,40 +206,25 @@ export default function DomainPage() {

Add these DNS records, then verify:

-
- - - - - - - - - - - - {settings.customDomains.flatMap((host) => - host.record.values.map((value) => ( - - )), - )} + + {settings.customDomains.flatMap((host) => + host.record.values.map((value) => ( - -
TypeNameValueStatus - Copy -
-
+ )), + )} + + )} @@ -399,6 +311,8 @@ export default function DomainPage() { + + Search engines diff --git a/apps/web/app/(workspace)/website/domain/redirect-domains.tsx b/apps/web/app/(workspace)/website/domain/redirect-domains.tsx new file mode 100644 index 0000000..7087ac1 --- /dev/null +++ b/apps/web/app/(workspace)/website/domain/redirect-domains.tsx @@ -0,0 +1,264 @@ +"use client"; + +import { useState } from "react"; +import { + addRedirectDomain, + removeRedirectDomain, + updateRedirectDomain, + verifyRedirectDomain, + type DomainSettings, + type RedirectDomain, + type RedirectRule, +} from "@/lib/api"; +import { DeleteConfirm } from "@/components/workspace/delete-confirm"; +import { Button } from "@/components/ui/codelit/button"; +import { Input } from "@/components/ui/codelit/input"; +import { Label } from "@/components/ui/codelit/label"; +import { Textarea } from "@/components/ui/codelit/textarea"; +import { Badge } from "@/components/ui/codelit/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/codelit/card"; +import { DnsRecordRow, DnsRecordsTable, STATUS_VARIANT } from "./dns-records"; + +type Run = (action: () => Promise) => Promise; + +/** One rule per line: `/old-path /new-path`. */ +function formatRules(rules: RedirectRule[]): string { + return rules.map((rule) => `${rule.from} ${rule.to}`).join("\n"); +} + +function parseRules(text: string): RedirectRule[] | string { + const rules: RedirectRule[] = []; + const seen = new Set(); + for (const [index, line] of text.split("\n").entries()) { + const parts = line.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) continue; + if (parts.length !== 2 || !parts.every((part) => part.startsWith("/"))) + return `Line ${index + 1}: write the old path and the new path, e.g. /api/old /docs/api/new`; + const from = parts[0]!.replace(/\/+$/, "") || "/"; + if (seen.has(from)) + return `Line ${index + 1}: ${parts[0]} already has a rule above.`; + seen.add(from); + rules.push({ from: parts[0]!, to: parts[1]! }); + } + return rules; +} + +const PREFIX = /^(\/[A-Za-z0-9._~-]+)*$/; + +function RedirectDomainItem({ + host, + canonicalHost, + busy, + run, +}: { + host: RedirectDomain; + canonicalHost: string | null; + busy: boolean; + run: Run; +}) { + const [prefix, setPrefix] = useState(host.pathPrefix); + const [rules, setRules] = useState(formatRules(host.rules)); + const [formError, setFormError] = useState(null); + const changed = + prefix !== host.pathPrefix || rules.trim() !== formatRules(host.rules).trim(); + + function save() { + if (!PREFIX.test(prefix.trim())) { + setFormError( + 'The path prefix must be empty or look like "/docs", with no trailing slash.', + ); + return; + } + const parsed = parseRules(rules); + if (typeof parsed === "string") { + setFormError(parsed); + return; + } + setFormError(null); + void run(() => + updateRedirectDomain(host.hostname, { pathPrefix: prefix.trim(), rules: parsed }), + ); + } + + return ( +
  • +
    + + {host.hostname} + {host.status} + + + {host.hostname}/page → {canonicalHost} + {host.pathPrefix}/page + +
    + + {host.status !== "verified" && ( +
    +

    Add these DNS records, then check DNS:

    + + {host.record.values.map((value) => ( + + ))} + + +
    + )} + +
    + + setPrefix(event.target.value)} + placeholder="/docs" + /> +

    + Put in front of every path. Leave empty to keep paths as they are. +

    +
    + +
    + +