dbbfb747a4
- Replace InternalAuthService with TokenService using JWT tokens - Add support for token issuance, refresh, verification and revocation - Implement automatic signing key rotation with Redis storage - Add database migration checks for indexes and foreign key constraints - Update gRPC endpoints to use token-based authentication - Remove deprecated API key based authentication system - Add JSON Web Token support with HMAC-SHA256 signing - Implement refresh token handling with automatic rotation - Add token revocation by JTI and user ID - Update build configuration to include core proto files - Migrate database schema to handle token-based authentication - Add comprehensive token validation and verification logic
2122 lines
93 KiB
PL/PgSQL
2122 lines
93 KiB
PL/PgSQL
-- ============================================================
|
|
-- Migration: 001_init.sql
|
|
-- Tables: 106 | ALL FKs → ON DELETE CASCADE
|
|
-- storage_node_id → NO FK (business-layer ref to etcd)
|
|
-- Circular/self-referencing FKs deferred to ALTER TABLE
|
|
-- ============================================================
|
|
|
|
BEGIN;
|
|
|
|
-- PHASE A: Create tables in dependency order
|
|
|
|
-- models/agents/agent_execution_steps.rs → agent_execution_step
|
|
CREATE TABLE IF NOT EXISTS agent_execution_step (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
execution_id UUID NOT NULL,
|
|
step_index INTEGER NOT NULL,
|
|
step_type TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
model_id UUID NULL,
|
|
tool_name TEXT NULL,
|
|
input JSONB NULL,
|
|
output JSONB NULL,
|
|
error_message TEXT NULL,
|
|
token_input_count INTEGER NULL,
|
|
token_output_count INTEGER NULL,
|
|
started_at TIMESTAMPTZ NULL,
|
|
finished_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_step_execution_id ON agent_execution_step (execution_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_step_model_id ON agent_execution_step (model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_step_status_created ON agent_execution_step (status, created_at DESC);
|
|
|
|
-- models/ais/ai_models.rs → ai_model
|
|
CREATE TABLE IF NOT EXISTS ai_model (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
provider TEXT NOT NULL,
|
|
model_id TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
model_type TEXT NOT NULL,
|
|
family TEXT NULL,
|
|
version TEXT NULL,
|
|
context_window INTEGER NULL,
|
|
max_output_tokens INTEGER NULL,
|
|
input_modalities TEXT[] NOT NULL,
|
|
output_modalities TEXT[] NOT NULL,
|
|
supported_features TEXT[] NOT NULL,
|
|
pricing_unit TEXT NULL,
|
|
input_price_per_unit TEXT NULL,
|
|
output_price_per_unit TEXT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
deprecated BOOLEAN NOT NULL,
|
|
released_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_model_id ON ai_model (model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_deleted ON ai_model (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/users/user.rs → user
|
|
CREATE TABLE IF NOT EXISTS "user" (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
username TEXT NOT NULL,
|
|
display_name TEXT NULL,
|
|
avatar_url TEXT NULL,
|
|
bio TEXT NULL,
|
|
status TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
visibility TEXT NOT NULL,
|
|
is_active BOOLEAN NOT NULL,
|
|
is_bot BOOLEAN NOT NULL,
|
|
last_login_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_status_created ON "user" (status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_user_deleted ON "user" (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/ais/ai_model_capabilities.rs → ai_model_capability
|
|
CREATE TABLE IF NOT EXISTS ai_model_capability (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ai_model_id UUID NOT NULL REFERENCES ai_model(id) ON DELETE CASCADE,
|
|
capability TEXT NOT NULL,
|
|
supported BOOLEAN NOT NULL,
|
|
config JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_capability_ai_model_id ON ai_model_capability (ai_model_id);
|
|
|
|
-- models/ais/ai_model_health.rs → ai_model_health
|
|
CREATE TABLE IF NOT EXISTS ai_model_health (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ai_model_id UUID NOT NULL REFERENCES ai_model(id) ON DELETE CASCADE,
|
|
status TEXT NOT NULL,
|
|
latency_ms INTEGER NULL,
|
|
error_rate TEXT NULL,
|
|
last_error TEXT NULL,
|
|
checked_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_health_ai_model_id ON ai_model_health (ai_model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_health_status_created ON ai_model_health (status, created_at DESC);
|
|
|
|
-- models/ais/ai_model_versions.rs → ai_model_version
|
|
CREATE TABLE IF NOT EXISTS ai_model_version (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ai_model_id UUID NOT NULL REFERENCES ai_model(id) ON DELETE CASCADE,
|
|
version TEXT NOT NULL,
|
|
provider_model_id TEXT NOT NULL,
|
|
context_window INTEGER NULL,
|
|
max_output_tokens INTEGER NULL,
|
|
changelog TEXT NULL,
|
|
stable BOOLEAN NOT NULL,
|
|
deprecated BOOLEAN NOT NULL,
|
|
released_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_version_ai_model_id ON ai_model_version (ai_model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_model_version_provider_model_id ON ai_model_version (provider_model_id);
|
|
|
|
-- models/notifications/notification_deliveries.rs → notification_delivery
|
|
CREATE TABLE IF NOT EXISTS notification_delivery (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
notification_id UUID NOT NULL,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
channel TEXT NOT NULL,
|
|
destination TEXT NULL,
|
|
status TEXT NOT NULL,
|
|
provider TEXT NULL,
|
|
provider_message_id TEXT NULL,
|
|
attempts INTEGER NOT NULL,
|
|
last_error TEXT NULL,
|
|
scheduled_at TIMESTAMPTZ NULL,
|
|
sent_at TIMESTAMPTZ NULL,
|
|
delivered_at TIMESTAMPTZ NULL,
|
|
failed_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_delivery_notification_id ON notification_delivery (notification_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_delivery_user_id ON notification_delivery (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_delivery_provider_message_id ON notification_delivery (provider_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_delivery_user_created ON notification_delivery (user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_delivery_status_created ON notification_delivery (status, created_at DESC);
|
|
|
|
-- models/notifications/notification_templates.rs → notification_template
|
|
CREATE TABLE IF NOT EXISTS notification_template (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
key TEXT NOT NULL,
|
|
notification_type TEXT NOT NULL,
|
|
channel TEXT NOT NULL,
|
|
locale TEXT NOT NULL,
|
|
subject_template TEXT NULL,
|
|
title_template TEXT NOT NULL,
|
|
body_template TEXT NOT NULL,
|
|
action_text_template TEXT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/users/user_appearance.rs → user_appearance
|
|
CREATE TABLE IF NOT EXISTS user_appearance (
|
|
PRIMARY KEY (user_id),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
theme TEXT NOT NULL,
|
|
color_scheme TEXT NOT NULL,
|
|
density TEXT NOT NULL,
|
|
font_size TEXT NOT NULL,
|
|
editor_theme TEXT NULL,
|
|
markdown_preview BOOLEAN NOT NULL,
|
|
reduced_motion BOOLEAN NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_appearance_user_created ON user_appearance (user_id, created_at DESC);
|
|
|
|
-- models/users/user_block.rs → user_block
|
|
CREATE TABLE IF NOT EXISTS user_block (
|
|
PRIMARY KEY (blocker_id, blocked_id),
|
|
blocker_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
blocked_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
reason TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/users/user_device.rs → user_device
|
|
CREATE TABLE IF NOT EXISTS user_device (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
device_name TEXT NOT NULL,
|
|
device_type TEXT NOT NULL,
|
|
fingerprint TEXT NULL,
|
|
ip_address TEXT NULL,
|
|
user_agent TEXT NULL,
|
|
trusted BOOLEAN NOT NULL,
|
|
last_seen_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_device_user_id ON user_device (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_device_user_created ON user_device (user_id, created_at DESC);
|
|
|
|
-- models/users/user_follow.rs → user_follow
|
|
CREATE TABLE IF NOT EXISTS user_follow (
|
|
PRIMARY KEY (follower_id, following_id),
|
|
follower_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
following_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/users/user_gpg_key.rs → user_gpg_key
|
|
CREATE TABLE IF NOT EXISTS user_gpg_key (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
key_id TEXT NOT NULL,
|
|
public_key TEXT NOT NULL,
|
|
fingerprint TEXT NOT NULL,
|
|
primary_email TEXT NULL,
|
|
expires_at TIMESTAMPTZ NULL,
|
|
verified_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_gpg_key_user_id ON user_gpg_key (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_gpg_key_key_id ON user_gpg_key (key_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_gpg_key_user_created ON user_gpg_key (user_id, created_at DESC);
|
|
|
|
-- models/users/user_mail.rs → user_mail
|
|
CREATE TABLE IF NOT EXISTS user_mail (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
email TEXT NOT NULL,
|
|
is_primary BOOLEAN NOT NULL,
|
|
is_verified BOOLEAN NOT NULL,
|
|
verification_token_hash TEXT NULL,
|
|
verified_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_mail_user_id ON user_mail (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_mail_user_created ON user_mail (user_id, created_at DESC);
|
|
|
|
-- models/users/user_notify_setting.rs → user_notify_setting
|
|
CREATE TABLE IF NOT EXISTS user_notify_setting (
|
|
PRIMARY KEY (user_id),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
email_notifications BOOLEAN NOT NULL,
|
|
web_notifications BOOLEAN NOT NULL,
|
|
mention_notifications BOOLEAN NOT NULL,
|
|
review_notifications BOOLEAN NOT NULL,
|
|
security_notifications BOOLEAN NOT NULL,
|
|
marketing_emails BOOLEAN NOT NULL,
|
|
digest_frequency TEXT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_notify_setting_user_created ON user_notify_setting (user_id, created_at DESC);
|
|
|
|
-- models/users/user_oauth.rs → user_oauth
|
|
CREATE TABLE IF NOT EXISTS user_oauth (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
provider TEXT NOT NULL,
|
|
provider_user_id TEXT NOT NULL,
|
|
provider_username TEXT NULL,
|
|
provider_email TEXT NULL,
|
|
access_token_ciphertext TEXT NULL,
|
|
refresh_token_ciphertext TEXT NULL,
|
|
token_expires_at TIMESTAMPTZ NULL,
|
|
linked_at TIMESTAMPTZ NOT NULL,
|
|
last_used_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_oauth_user_id ON user_oauth (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_oauth_provider_user_id ON user_oauth (provider_user_id);
|
|
|
|
-- models/users/user_password.rs → user_password
|
|
CREATE TABLE IF NOT EXISTS user_password (
|
|
PRIMARY KEY (user_id),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
password_hash TEXT NOT NULL,
|
|
password_algo TEXT NOT NULL,
|
|
password_salt TEXT NULL,
|
|
must_change_password BOOLEAN NOT NULL,
|
|
password_updated_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_password_user_created ON user_password (user_id, created_at DESC);
|
|
|
|
-- models/users/user_password_reset.rs → user_password_reset
|
|
CREATE TABLE IF NOT EXISTS user_password_reset (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
token_hash TEXT NOT NULL,
|
|
requested_ip TEXT NULL,
|
|
user_agent TEXT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
used_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_password_reset_user_id ON user_password_reset (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_password_reset_user_created ON user_password_reset (user_id, created_at DESC);
|
|
|
|
-- models/users/user_personal_access_token.rs → user_personal_access_token
|
|
CREATE TABLE IF NOT EXISTS user_personal_access_token (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
scopes TEXT[] NOT NULL,
|
|
last_used_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_personal_access_token_user_id ON user_personal_access_token (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_personal_access_token_user_created ON user_personal_access_token (user_id, created_at DESC);
|
|
|
|
-- models/users/user_profile.rs → user_profile
|
|
CREATE TABLE IF NOT EXISTS user_profile (
|
|
PRIMARY KEY (user_id),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
full_name TEXT NULL,
|
|
company TEXT NULL,
|
|
location TEXT NULL,
|
|
website_url TEXT NULL,
|
|
twitter_username TEXT NULL,
|
|
timezone TEXT NULL,
|
|
language TEXT NULL,
|
|
profile_readme TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_profile_user_created ON user_profile (user_id, created_at DESC);
|
|
|
|
-- models/users/user_security_log.rs → user_security_log
|
|
CREATE TABLE IF NOT EXISTS user_security_log (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
event_type TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
ip_address TEXT NULL,
|
|
user_agent TEXT NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_security_log_user_id ON user_security_log (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_security_log_user_created ON user_security_log (user_id, created_at DESC);
|
|
|
|
-- models/users/user_session.rs → user_session
|
|
CREATE TABLE IF NOT EXISTS user_session (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
session_token_hash TEXT NOT NULL,
|
|
refresh_token_hash TEXT NULL,
|
|
ip_address TEXT NULL,
|
|
user_agent TEXT NULL,
|
|
last_active_at TIMESTAMPTZ NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_session_user_id ON user_session (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_session_user_created ON user_session (user_id, created_at DESC);
|
|
|
|
-- models/users/user_ssh_key.rs → user_ssh_key
|
|
CREATE TABLE IF NOT EXISTS user_ssh_key (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
public_key TEXT NOT NULL,
|
|
fingerprint_sha256 TEXT NOT NULL,
|
|
key_type TEXT NOT NULL,
|
|
last_used_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_ssh_key_user_id ON user_ssh_key (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_ssh_key_user_created ON user_ssh_key (user_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace.rs → workspace
|
|
CREATE TABLE IF NOT EXISTS workspace (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
owner_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
slug TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
avatar_url TEXT NULL,
|
|
visibility TEXT NOT NULL,
|
|
plan TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
default_role TEXT NOT NULL,
|
|
is_personal BOOLEAN NOT NULL,
|
|
archived_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_owner_id ON workspace (owner_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_status_created ON workspace (status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_deleted ON workspace (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/agents/agent.rs → agent
|
|
CREATE TABLE IF NOT EXISTS agent (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
owner_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
avatar_url TEXT NULL,
|
|
agent_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
visibility TEXT NOT NULL,
|
|
default_model_id UUID NULL REFERENCES ai_model(id) ON DELETE CASCADE,
|
|
current_version_id UUID NULL,
|
|
system_prompt TEXT NULL,
|
|
tools TEXT[] NOT NULL,
|
|
tags TEXT[] NOT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
last_run_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_owner_id ON agent (owner_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_workspace_id ON agent (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_default_model_id ON agent (default_model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_current_version_id ON agent (current_version_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_ws_created ON agent (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_status_created ON agent (status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_deleted ON agent (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/repos/repo.rs → repo
|
|
CREATE TABLE IF NOT EXISTS repo (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
owner_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
slug TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
default_branch TEXT NOT NULL,
|
|
visibility TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
is_fork BOOLEAN NOT NULL,
|
|
forked_from_repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
storage_node_ids UUID[] NOT NULL,
|
|
primary_storage_node_id UUID NOT NULL,
|
|
storage_path TEXT NOT NULL,
|
|
git_service TEXT NOT NULL,
|
|
archived_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_workspace_id ON repo (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_owner_id ON repo (owner_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_forked_from_repo_id ON repo (forked_from_repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_primary_storage_node_id ON repo (primary_storage_node_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_ws_created ON repo (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_status_created ON repo (status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_deleted ON repo (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/workspaces/workspace_audit_logs.rs → workspace_audit_log
|
|
CREATE TABLE IF NOT EXISTS workspace_audit_log (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
actor_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
action TEXT NOT NULL,
|
|
target_type TEXT NULL,
|
|
target_id UUID NULL,
|
|
ip_address TEXT NULL,
|
|
user_agent TEXT NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_audit_log_workspace_id ON workspace_audit_log (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_audit_log_actor_id ON workspace_audit_log (actor_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_audit_log_target_id ON workspace_audit_log (target_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_audit_log_ws_created ON workspace_audit_log (workspace_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_billing.rs → workspace_billing
|
|
CREATE TABLE IF NOT EXISTS workspace_billing (
|
|
PRIMARY KEY (workspace_id),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
customer_id TEXT NULL,
|
|
subscription_id TEXT NULL,
|
|
plan TEXT NOT NULL,
|
|
billing_email TEXT NULL,
|
|
status TEXT NOT NULL,
|
|
seats INTEGER NOT NULL,
|
|
trial_ends_at TIMESTAMPTZ NULL,
|
|
current_period_start TIMESTAMPTZ NULL,
|
|
current_period_end TIMESTAMPTZ NULL,
|
|
canceled_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_billing_customer_id ON workspace_billing (customer_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_billing_subscription_id ON workspace_billing (subscription_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_billing_ws_created ON workspace_billing (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_billing_status_created ON workspace_billing (status, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_custom_branding.rs → workspace_custom_branding
|
|
CREATE TABLE IF NOT EXISTS workspace_custom_branding (
|
|
PRIMARY KEY (workspace_id),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
logo_url TEXT NULL,
|
|
favicon_url TEXT NULL,
|
|
primary_color TEXT NULL,
|
|
accent_color TEXT NULL,
|
|
custom_css TEXT NULL,
|
|
support_url TEXT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_custom_branding_ws_created ON workspace_custom_branding (workspace_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_domains.rs → workspace_domain
|
|
CREATE TABLE IF NOT EXISTS workspace_domain (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
domain TEXT NOT NULL,
|
|
verification_token_hash TEXT NULL,
|
|
is_primary BOOLEAN NOT NULL,
|
|
is_verified BOOLEAN NOT NULL,
|
|
verified_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_workspace_domain UNIQUE (workspace_id, domain)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_domain_workspace_id ON workspace_domain (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_domain_ws_created ON workspace_domain (workspace_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_integrations.rs → workspace_integration
|
|
CREATE TABLE IF NOT EXISTS workspace_integration (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
provider TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
config JSONB NULL,
|
|
secret_ciphertext TEXT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
installed_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
last_used_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_integration_workspace_id ON workspace_integration (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_integration_ws_created ON workspace_integration (workspace_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_invitations.rs → workspace_invitation
|
|
CREATE TABLE IF NOT EXISTS workspace_invitation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
email TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
invited_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
accepted_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
accepted_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_invitation_workspace_id ON workspace_invitation (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_invitation_ws_created ON workspace_invitation (workspace_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_members.rs → workspace_member
|
|
CREATE TABLE IF NOT EXISTS workspace_member (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
invited_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
joined_at TIMESTAMPTZ NULL,
|
|
last_active_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_workspace_member UNIQUE (workspace_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_member_workspace_id ON workspace_member (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_member_user_id ON workspace_member (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_member_ws_created ON workspace_member (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_member_user_created ON workspace_member (user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_member_status_created ON workspace_member (status, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_pending_approvals.rs → workspace_pending_approval
|
|
CREATE TABLE IF NOT EXISTS workspace_pending_approval (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
requester_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
request_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
reason TEXT NULL,
|
|
reviewed_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
reviewed_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_pending_approval_workspace_id ON workspace_pending_approval (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_pending_approval_requester_id ON workspace_pending_approval (requester_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_pending_approval_ws_created ON workspace_pending_approval (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_pending_approval_status_created ON workspace_pending_approval (status, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_settings.rs → workspace_settings
|
|
CREATE TABLE IF NOT EXISTS workspace_settings (
|
|
PRIMARY KEY (workspace_id),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
allow_public_repos BOOLEAN NOT NULL,
|
|
allow_member_invites BOOLEAN NOT NULL,
|
|
require_two_factor BOOLEAN NOT NULL,
|
|
default_repo_visibility TEXT NOT NULL,
|
|
default_branch_name TEXT NOT NULL,
|
|
issue_tracking_enabled BOOLEAN NOT NULL,
|
|
pull_requests_enabled BOOLEAN NOT NULL,
|
|
wiki_enabled BOOLEAN NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_settings_ws_created ON workspace_settings (workspace_id, created_at DESC);
|
|
|
|
-- models/workspaces/workspace_stats.rs → workspace_stats
|
|
CREATE TABLE IF NOT EXISTS workspace_stats (
|
|
PRIMARY KEY (workspace_id),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
members_count BIGINT NOT NULL,
|
|
repos_count BIGINT NOT NULL,
|
|
issues_count BIGINT NOT NULL,
|
|
pull_requests_count BIGINT NOT NULL,
|
|
storage_bytes BIGINT NOT NULL,
|
|
bandwidth_bytes BIGINT NOT NULL,
|
|
build_minutes_used BIGINT NOT NULL,
|
|
last_activity_at TIMESTAMPTZ NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/workspaces/workspace_webhooks.rs → workspace_webhook
|
|
CREATE TABLE IF NOT EXISTS workspace_webhook (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
url TEXT NOT NULL,
|
|
secret_ciphertext TEXT NULL,
|
|
events TEXT[] NOT NULL,
|
|
active BOOLEAN NOT NULL,
|
|
last_delivery_status TEXT NULL,
|
|
last_delivery_at TIMESTAMPTZ NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_webhook_workspace_id ON workspace_webhook (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_webhook_ws_created ON workspace_webhook (workspace_id, created_at DESC);
|
|
|
|
-- models/agents/agent_versions.rs → agent_version
|
|
CREATE TABLE IF NOT EXISTS agent_version (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
agent_id UUID NOT NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
version TEXT NOT NULL,
|
|
name TEXT NULL,
|
|
description TEXT NULL,
|
|
model_id UUID NULL,
|
|
system_prompt TEXT NOT NULL,
|
|
instructions TEXT NULL,
|
|
tools TEXT[] NOT NULL,
|
|
config JSONB NULL,
|
|
changelog TEXT NULL,
|
|
stable BOOLEAN NOT NULL,
|
|
published_by UUID NOT NULL,
|
|
published_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_version_agent_id ON agent_version (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_version_model_id ON agent_version (model_id);
|
|
|
|
-- models/agents/agent_event_subscriptions.rs → agent_event_subscription
|
|
CREATE TABLE IF NOT EXISTS agent_event_subscription (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
agent_id UUID NOT NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
event_type TEXT NOT NULL,
|
|
filters JSONB NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_event_subscription_agent_id ON agent_event_subscription (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_event_subscription_workspace_id ON agent_event_subscription (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_event_subscription_repo_id ON agent_event_subscription (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_event_subscription_repo_created ON agent_event_subscription (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_event_subscription_ws_created ON agent_event_subscription (workspace_id, created_at DESC);
|
|
|
|
-- models/agents/agent_schedules.rs → agent_schedule
|
|
CREATE TABLE IF NOT EXISTS agent_schedule (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
agent_id UUID NOT NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
cron_expr TEXT NOT NULL,
|
|
timezone TEXT NOT NULL,
|
|
payload JSONB NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
last_run_at TIMESTAMPTZ NULL,
|
|
next_run_at TIMESTAMPTZ NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_schedule_agent_id ON agent_schedule (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_schedule_workspace_id ON agent_schedule (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_schedule_repo_id ON agent_schedule (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_schedule_repo_created ON agent_schedule (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_schedule_ws_created ON agent_schedule (workspace_id, created_at DESC);
|
|
|
|
-- models/agents/agent_workspace_bindings.rs → agent_workspace_binding
|
|
CREATE TABLE IF NOT EXISTS agent_workspace_binding (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
agent_id UUID NOT NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL,
|
|
permissions TEXT[] NOT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
bound_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_agent_workspace_binding UNIQUE (agent_id, workspace_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_workspace_binding_agent_id ON agent_workspace_binding (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_workspace_binding_workspace_id ON agent_workspace_binding (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_workspace_binding_repo_id ON agent_workspace_binding (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_workspace_binding_repo_created ON agent_workspace_binding (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_workspace_binding_ws_created ON agent_workspace_binding (workspace_id, created_at DESC);
|
|
|
|
-- models/channels/channel.rs → channel
|
|
CREATE TABLE IF NOT EXISTS channel (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
topic TEXT NULL,
|
|
description TEXT NULL,
|
|
channel_type TEXT NOT NULL,
|
|
visibility TEXT NOT NULL,
|
|
archived BOOLEAN NOT NULL,
|
|
read_only BOOLEAN NOT NULL,
|
|
last_message_id UUID NULL,
|
|
last_message_at TIMESTAMPTZ NULL,
|
|
archived_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_workspace_id ON channel (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_repo_id ON channel (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_last_message_id ON channel (last_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_repo_created ON channel (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_ws_created ON channel (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_deleted ON channel (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/issues/issue.rs → issue
|
|
CREATE TABLE IF NOT EXISTS issue (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
number BIGINT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
body TEXT NULL,
|
|
state TEXT NOT NULL,
|
|
priority TEXT NOT NULL,
|
|
visibility TEXT NOT NULL,
|
|
locked BOOLEAN NOT NULL,
|
|
closed_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
closed_at TIMESTAMPTZ NULL,
|
|
due_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_author_id ON issue (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_deleted ON issue (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
DO $$ BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issue' AND column_name = 'repo_id') THEN
|
|
CREATE INDEX IF NOT EXISTS idx_issue_repo_id ON issue (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_repo_created ON issue (repo_id, created_at DESC);
|
|
END IF;
|
|
END $$;
|
|
|
|
-- models/issues/issue_labels.rs → issue_label
|
|
CREATE TABLE IF NOT EXISTS issue_label (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
color TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_label_repo_id ON issue_label (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_label_repo_created ON issue_label (repo_id, created_at DESC);
|
|
|
|
-- models/issues/issue_milestones.rs → issue_milestone
|
|
CREATE TABLE IF NOT EXISTS issue_milestone (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
state TEXT NOT NULL,
|
|
due_at TIMESTAMPTZ NULL,
|
|
closed_at TIMESTAMPTZ NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_milestone_repo_id ON issue_milestone (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_milestone_repo_created ON issue_milestone (repo_id, created_at DESC);
|
|
|
|
-- models/issues/issue_templates.rs → issue_template
|
|
CREATE TABLE IF NOT EXISTS issue_template (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
title_template TEXT NULL,
|
|
body_template TEXT NOT NULL,
|
|
labels TEXT[] NOT NULL,
|
|
active BOOLEAN NOT NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_template_repo_id ON issue_template (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_template_repo_created ON issue_template (repo_id, created_at DESC);
|
|
|
|
-- models/notifications/notification_blocks.rs → notification_block
|
|
CREATE TABLE IF NOT EXISTS notification_block (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
target_type TEXT NOT NULL,
|
|
target_id UUID NULL,
|
|
notification_type TEXT NULL,
|
|
channel TEXT NULL,
|
|
reason TEXT NULL,
|
|
expires_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_user_id ON notification_block (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_workspace_id ON notification_block (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_repo_id ON notification_block (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_target_id ON notification_block (target_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_repo_created ON notification_block (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_ws_created ON notification_block (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_block_user_created ON notification_block (user_id, created_at DESC);
|
|
|
|
-- models/notifications/notification_subscriptions.rs → notification_subscription
|
|
CREATE TABLE IF NOT EXISTS notification_subscription (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
target_type TEXT NOT NULL,
|
|
target_id UUID NULL,
|
|
event_types TEXT[] NOT NULL,
|
|
channels TEXT[] NOT NULL,
|
|
level TEXT NOT NULL,
|
|
muted BOOLEAN NOT NULL,
|
|
muted_until TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_user_id ON notification_subscription (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_workspace_id ON notification_subscription (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_repo_id ON notification_subscription (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_target_id ON notification_subscription (target_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_repo_created ON notification_subscription (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_ws_created ON notification_subscription (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_subscription_user_created ON notification_subscription (user_id, created_at DESC);
|
|
|
|
-- models/prs/pr_labels.rs → pr_label
|
|
CREATE TABLE IF NOT EXISTS pr_label (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
color TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_label_repo_id ON pr_label (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_label_repo_created ON pr_label (repo_id, created_at DESC);
|
|
|
|
-- models/prs/pull_request.rs → pull_request
|
|
CREATE TABLE IF NOT EXISTS pull_request (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
number BIGINT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
body TEXT NULL,
|
|
state TEXT NOT NULL,
|
|
source_repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
source_branch TEXT NOT NULL,
|
|
target_repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
target_branch TEXT NOT NULL,
|
|
base_commit_sha TEXT NULL,
|
|
head_commit_sha TEXT NOT NULL,
|
|
merge_commit_sha TEXT NULL,
|
|
draft BOOLEAN NOT NULL,
|
|
locked BOOLEAN NOT NULL,
|
|
merged_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
merged_at TIMESTAMPTZ NULL,
|
|
closed_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
closed_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pull_request_repo_id ON pull_request (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pull_request_author_id ON pull_request (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pull_request_source_repo_id ON pull_request (source_repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pull_request_target_repo_id ON pull_request (target_repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pull_request_repo_created ON pull_request (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_pull_request_deleted ON pull_request (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/repos/repo_deploy_keys.rs → repo_deploy_key
|
|
CREATE TABLE IF NOT EXISTS repo_deploy_key (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
public_key TEXT NOT NULL,
|
|
fingerprint_sha256 TEXT NOT NULL,
|
|
key_type TEXT NOT NULL,
|
|
read_only BOOLEAN NOT NULL,
|
|
last_used_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_deploy_key_repo_id ON repo_deploy_key (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_deploy_key_repo_created ON repo_deploy_key (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_invitations.rs → repo_invitation
|
|
CREATE TABLE IF NOT EXISTS repo_invitation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
email TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
invited_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
accepted_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
accepted_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_invitation_repo_id ON repo_invitation (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_invitation_repo_created ON repo_invitation (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_members.rs → repo_member
|
|
CREATE TABLE IF NOT EXISTS repo_member (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
invited_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
joined_at TIMESTAMPTZ NULL,
|
|
last_active_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_repo_member UNIQUE (repo_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_member_repo_id ON repo_member (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_member_user_id ON repo_member (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_member_repo_created ON repo_member (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_member_user_created ON repo_member (user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_member_status_created ON repo_member (status, created_at DESC);
|
|
|
|
-- models/repos/repo_push_commit.rs → repo_push_commit
|
|
CREATE TABLE IF NOT EXISTS repo_push_commit (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
pusher_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
branch_name TEXT NOT NULL,
|
|
old_commit_sha TEXT NULL,
|
|
latest_commit_sha TEXT NOT NULL,
|
|
commit_shas TEXT[] NOT NULL,
|
|
commit_count INTEGER NOT NULL,
|
|
push_status TEXT NOT NULL,
|
|
pushed_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_commit_repo_id ON repo_push_commit (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_commit_pusher_id ON repo_push_commit (pusher_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_commit_repo_created ON repo_push_commit (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_push_lock.rs → repo_push_lock
|
|
CREATE TABLE IF NOT EXISTS repo_push_lock (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
pusher_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
ref_name TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
queue_position INTEGER NOT NULL,
|
|
queued_at TIMESTAMPTZ NOT NULL,
|
|
started_at TIMESTAMPTZ NULL,
|
|
finished_at TIMESTAMPTZ NULL,
|
|
storage_node_id UUID NULL,
|
|
lease_token TEXT NULL,
|
|
error_message TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_lock_repo_id ON repo_push_lock (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_lock_pusher_id ON repo_push_lock (pusher_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_lock_storage_node_id ON repo_push_lock (storage_node_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_lock_repo_created ON repo_push_lock (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_push_lock_status_created ON repo_push_lock (status, created_at DESC);
|
|
|
|
-- models/repos/repo_stars.rs → repo_star
|
|
CREATE TABLE IF NOT EXISTS repo_star (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_repo_star UNIQUE (repo_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_star_repo_id ON repo_star (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_star_user_id ON repo_star (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_star_repo_created ON repo_star (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_star_user_created ON repo_star (user_id, created_at DESC);
|
|
|
|
-- models/repos/repo_stats.rs → repo_stats
|
|
CREATE TABLE IF NOT EXISTS repo_stats (
|
|
PRIMARY KEY (repo_id),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
stars_count BIGINT NOT NULL,
|
|
watchers_count BIGINT NOT NULL,
|
|
forks_count BIGINT NOT NULL,
|
|
branches_count BIGINT NOT NULL,
|
|
tags_count BIGINT NOT NULL,
|
|
commits_count BIGINT NOT NULL,
|
|
releases_count BIGINT NOT NULL,
|
|
open_issues_count BIGINT NOT NULL,
|
|
open_pull_requests_count BIGINT NOT NULL,
|
|
size_bytes BIGINT NOT NULL,
|
|
last_push_at TIMESTAMPTZ NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/repos/repo_tags.rs → repo_tag
|
|
CREATE TABLE IF NOT EXISTS repo_tag (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
target_commit_sha TEXT NOT NULL,
|
|
tagger_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
message TEXT NULL,
|
|
signed BOOLEAN NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_tag_repo_id ON repo_tag (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_tag_tagger_id ON repo_tag (tagger_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_tag_repo_created ON repo_tag (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_watches.rs → repo_watch
|
|
CREATE TABLE IF NOT EXISTS repo_watch (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
level TEXT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_repo_watch UNIQUE (repo_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_watch_repo_id ON repo_watch (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_watch_user_id ON repo_watch (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_watch_repo_created ON repo_watch (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_watch_user_created ON repo_watch (user_id, created_at DESC);
|
|
|
|
-- models/repos/repo_webhooks.rs → repo_webhook
|
|
CREATE TABLE IF NOT EXISTS repo_webhook (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
url TEXT NOT NULL,
|
|
secret_ciphertext TEXT NULL,
|
|
events TEXT[] NOT NULL,
|
|
active BOOLEAN NOT NULL,
|
|
last_delivery_status TEXT NULL,
|
|
last_delivery_at TIMESTAMPTZ NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_webhook_repo_id ON repo_webhook (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_webhook_repo_created ON repo_webhook (repo_id, created_at DESC);
|
|
|
|
-- models/channels/channel_events.rs → channel_event
|
|
CREATE TABLE IF NOT EXISTS channel_event (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
actor_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
event_type TEXT NOT NULL,
|
|
target_type TEXT NULL,
|
|
target_id UUID NULL,
|
|
old_value JSONB NULL,
|
|
new_value JSONB NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_event_channel_id ON channel_event (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_event_actor_id ON channel_event (actor_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_event_target_id ON channel_event (target_id);
|
|
|
|
-- models/channels/channel_invitations.rs → channel_invitation
|
|
CREATE TABLE IF NOT EXISTS channel_invitation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
invited_user_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
email TEXT NULL,
|
|
role TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
invited_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
accepted_at TIMESTAMPTZ NULL,
|
|
revoked_at TIMESTAMPTZ NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_invitation_channel_id ON channel_invitation (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_invitation_workspace_id ON channel_invitation (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_invitation_invited_user_id ON channel_invitation (invited_user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_invitation_ws_created ON channel_invitation (workspace_id, created_at DESC);
|
|
|
|
-- models/channels/channel_member_roles.rs → channel_member_role
|
|
CREATE TABLE IF NOT EXISTS channel_member_role (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
permissions TEXT[] NOT NULL,
|
|
assignable BOOLEAN NOT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_member_role_channel_id ON channel_member_role (channel_id);
|
|
|
|
-- models/channels/channel_repo_links.rs → channel_repo_link
|
|
CREATE TABLE IF NOT EXISTS channel_repo_link (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
link_type TEXT NOT NULL,
|
|
notify_events TEXT[] NOT NULL,
|
|
active BOOLEAN NOT NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_channel_repo_link UNIQUE (repo_id, channel_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_repo_link_channel_id ON channel_repo_link (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_repo_link_repo_id ON channel_repo_link (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_repo_link_repo_created ON channel_repo_link (repo_id, created_at DESC);
|
|
|
|
-- models/channels/channel_slash_commands.rs → channel_slash_command
|
|
CREATE TABLE IF NOT EXISTS channel_slash_command (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
workspace_id UUID NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
command TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
request_url TEXT NOT NULL,
|
|
secret_ciphertext TEXT NULL,
|
|
scopes TEXT[] NOT NULL,
|
|
enabled BOOLEAN NOT NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_slash_command_channel_id ON channel_slash_command (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_slash_command_workspace_id ON channel_slash_command (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_slash_command_ws_created ON channel_slash_command (workspace_id, created_at DESC);
|
|
|
|
-- models/channels/channel_stats.rs → channel_stats
|
|
CREATE TABLE IF NOT EXISTS channel_stats (
|
|
PRIMARY KEY (channel_id),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
members_count BIGINT NOT NULL,
|
|
messages_count BIGINT NOT NULL,
|
|
threads_count BIGINT NOT NULL,
|
|
reactions_count BIGINT NOT NULL,
|
|
mentions_count BIGINT NOT NULL,
|
|
files_count BIGINT NOT NULL,
|
|
last_activity_at TIMESTAMPTZ NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/channels/channel_webhooks.rs → channel_webhook
|
|
CREATE TABLE IF NOT EXISTS channel_webhook (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
secret_ciphertext TEXT NULL,
|
|
events TEXT[] NOT NULL,
|
|
active BOOLEAN NOT NULL,
|
|
last_delivery_status TEXT NULL,
|
|
last_delivery_at TIMESTAMPTZ NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_webhook_channel_id ON channel_webhook (channel_id);
|
|
|
|
-- models/channels/message.rs → message
|
|
CREATE TABLE IF NOT EXISTS message (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
thread_id UUID NULL,
|
|
reply_to_message_id UUID NULL,
|
|
message_type TEXT NOT NULL,
|
|
body TEXT NOT NULL,
|
|
metadata JSONB NULL,
|
|
pinned BOOLEAN NOT NULL,
|
|
system BOOLEAN NOT NULL,
|
|
edited_at TIMESTAMPTZ NULL,
|
|
deleted_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_message_channel_id ON message (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_author_id ON message (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_thread_id ON message (thread_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_reply_to_message_id ON message (reply_to_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_deleted ON message (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/issues/issue_assignees.rs → issue_assignee
|
|
CREATE TABLE IF NOT EXISTS issue_assignee (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
assignee_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
assigned_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_issue_assignee UNIQUE (issue_id, assignee_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_assignee_issue_id ON issue_assignee (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_assignee_assignee_id ON issue_assignee (assignee_id);
|
|
|
|
-- models/issues/issue_comments.rs → issue_comment
|
|
CREATE TABLE IF NOT EXISTS issue_comment (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
body TEXT NOT NULL,
|
|
reply_to_comment_id UUID NULL,
|
|
edited_at TIMESTAMPTZ NULL,
|
|
deleted_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_comment_issue_id ON issue_comment (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_comment_author_id ON issue_comment (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_comment_reply_to_comment_id ON issue_comment (reply_to_comment_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_comment_deleted ON issue_comment (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/issues/issue_events.rs → issue_event
|
|
CREATE TABLE IF NOT EXISTS issue_event (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
actor_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
event_type TEXT NOT NULL,
|
|
old_value JSONB NULL,
|
|
new_value JSONB NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_event_issue_id ON issue_event (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_event_actor_id ON issue_event (actor_id);
|
|
|
|
-- models/issues/issue_reminder.rs → issue_reminder
|
|
CREATE TABLE IF NOT EXISTS issue_reminder (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
remind_at TIMESTAMPTZ NOT NULL,
|
|
message TEXT NULL,
|
|
sent_at TIMESTAMPTZ NULL,
|
|
canceled_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_reminder_issue_id ON issue_reminder (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_reminder_user_id ON issue_reminder (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_reminder_user_created ON issue_reminder (user_id, created_at DESC);
|
|
|
|
-- models/issues/issue_stats.rs → issue_stats
|
|
CREATE TABLE IF NOT EXISTS issue_stats (
|
|
PRIMARY KEY (issue_id),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
comments_count BIGINT NOT NULL,
|
|
reactions_count BIGINT NOT NULL,
|
|
assignees_count BIGINT NOT NULL,
|
|
labels_count BIGINT NOT NULL,
|
|
subscribers_count BIGINT NOT NULL,
|
|
last_commented_at TIMESTAMPTZ NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/issues/issue_subscribers.rs → issue_subscriber
|
|
CREATE TABLE IF NOT EXISTS issue_subscriber (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
reason TEXT NOT NULL,
|
|
muted BOOLEAN NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_issue_subscriber UNIQUE (issue_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_subscriber_issue_id ON issue_subscriber (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_subscriber_user_id ON issue_subscriber (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_subscriber_user_created ON issue_subscriber (user_id, created_at DESC);
|
|
|
|
-- models/issues/issue_label_relations.rs → issue_label_relation
|
|
CREATE TABLE IF NOT EXISTS issue_label_relation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
label_id UUID NOT NULL REFERENCES issue_label(id) ON DELETE CASCADE,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_label_relation_issue_id ON issue_label_relation (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_label_relation_label_id ON issue_label_relation (label_id);
|
|
|
|
-- models/agents/agent_executions.rs → agent_execution
|
|
CREATE TABLE IF NOT EXISTS agent_execution (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
agent_id UUID NOT NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
agent_version_id UUID NULL REFERENCES agent_version(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
issue_id UUID NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
pull_request_id UUID NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
triggered_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
trigger_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
input JSONB NULL,
|
|
output JSONB NULL,
|
|
error_message TEXT NULL,
|
|
started_at TIMESTAMPTZ NULL,
|
|
finished_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_agent_id ON agent_execution (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_agent_version_id ON agent_execution (agent_version_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_workspace_id ON agent_execution (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_repo_id ON agent_execution (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_issue_id ON agent_execution (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_pull_request_id ON agent_execution (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_repo_created ON agent_execution (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_ws_created ON agent_execution (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_execution_status_created ON agent_execution (status, created_at DESC);
|
|
|
|
-- models/issues/issue_pr_relations.rs → issue_pr_relation
|
|
CREATE TABLE IF NOT EXISTS issue_pr_relation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
relation_type TEXT NOT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_issue_pr_relation UNIQUE (issue_id, pull_request_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_pr_relation_issue_id ON issue_pr_relation (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_pr_relation_pull_request_id ON issue_pr_relation (pull_request_id);
|
|
|
|
-- models/prs/pr_assignees.rs → pr_assignee
|
|
CREATE TABLE IF NOT EXISTS pr_assignee (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
assignee_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
assigned_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_pr_assignee UNIQUE (pull_request_id, assignee_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_assignee_pull_request_id ON pr_assignee (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_assignee_assignee_id ON pr_assignee (assignee_id);
|
|
|
|
-- models/prs/pr_check_runs.rs → pr_check_run
|
|
CREATE TABLE IF NOT EXISTS pr_check_run (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
commit_sha TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
conclusion TEXT NULL,
|
|
details_url TEXT NULL,
|
|
external_id TEXT NULL,
|
|
started_at TIMESTAMPTZ NULL,
|
|
completed_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_check_run_pull_request_id ON pr_check_run (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_check_run_external_id ON pr_check_run (external_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_check_run_status_created ON pr_check_run (status, created_at DESC);
|
|
|
|
-- models/prs/pr_commits.rs → pr_commit
|
|
CREATE TABLE IF NOT EXISTS pr_commit (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
commit_sha TEXT NOT NULL,
|
|
position INTEGER NOT NULL,
|
|
authored_at TIMESTAMPTZ NULL,
|
|
committed_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_commit_pull_request_id ON pr_commit (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_commit_repo_id ON pr_commit (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_commit_repo_created ON pr_commit (repo_id, created_at DESC);
|
|
|
|
-- models/prs/pr_events.rs → pr_event
|
|
CREATE TABLE IF NOT EXISTS pr_event (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
actor_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
event_type TEXT NOT NULL,
|
|
old_value JSONB NULL,
|
|
new_value JSONB NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_event_pull_request_id ON pr_event (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_event_actor_id ON pr_event (actor_id);
|
|
|
|
-- models/prs/pr_files.rs → pr_file
|
|
CREATE TABLE IF NOT EXISTS pr_file (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
path TEXT NOT NULL,
|
|
old_path TEXT NULL,
|
|
status TEXT NOT NULL,
|
|
additions INTEGER NOT NULL,
|
|
deletions INTEGER NOT NULL,
|
|
changes INTEGER NOT NULL,
|
|
patch TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_file_pull_request_id ON pr_file (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_file_status_created ON pr_file (status, created_at DESC);
|
|
|
|
-- models/prs/pr_label_relations.rs → pr_label_relation
|
|
CREATE TABLE IF NOT EXISTS pr_label_relation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
label_id UUID NOT NULL REFERENCES pr_label(id) ON DELETE CASCADE,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_label_relation_pull_request_id ON pr_label_relation (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_label_relation_label_id ON pr_label_relation (label_id);
|
|
|
|
-- models/prs/pr_merge_strategy.rs → pr_merge_strategy
|
|
CREATE TABLE IF NOT EXISTS pr_merge_strategy (
|
|
PRIMARY KEY (pull_request_id),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
strategy TEXT NOT NULL,
|
|
auto_merge BOOLEAN NOT NULL,
|
|
squash_title TEXT NULL,
|
|
squash_message TEXT NULL,
|
|
delete_source_branch BOOLEAN NOT NULL,
|
|
merge_when_checks_pass BOOLEAN NOT NULL,
|
|
selected_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/prs/pr_reactions.rs → pr_reaction
|
|
CREATE TABLE IF NOT EXISTS pr_reaction (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
content TEXT NOT NULL,
|
|
target_type TEXT NOT NULL,
|
|
target_id UUID NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_reaction_pull_request_id ON pr_reaction (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_reaction_user_id ON pr_reaction (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_reaction_target_id ON pr_reaction (target_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_reaction_user_created ON pr_reaction (user_id, created_at DESC);
|
|
|
|
-- models/prs/pr_status.rs → pr_status
|
|
CREATE TABLE IF NOT EXISTS pr_status (
|
|
PRIMARY KEY (pull_request_id),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
head_commit_sha TEXT NOT NULL,
|
|
checks_state TEXT NOT NULL,
|
|
mergeable_state TEXT NOT NULL,
|
|
conflicts BOOLEAN NOT NULL,
|
|
approvals_count INTEGER NOT NULL,
|
|
requested_reviews_count INTEGER NOT NULL,
|
|
changed_files_count INTEGER NOT NULL,
|
|
additions_count INTEGER NOT NULL,
|
|
deletions_count INTEGER NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
|
|
-- models/prs/pr_subscriptions.rs → pr_subscription
|
|
CREATE TABLE IF NOT EXISTS pr_subscription (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pull_request_id UUID NOT NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
reason TEXT NOT NULL,
|
|
muted BOOLEAN NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_pr_subscription UNIQUE (pull_request_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_subscription_pull_request_id ON pr_subscription (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_subscription_user_id ON pr_subscription (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_pr_subscription_user_created ON pr_subscription (user_id, created_at DESC);
|
|
|
|
-- models/issues/issue_commit_relations.rs → issue_commit_relation
|
|
CREATE TABLE IF NOT EXISTS issue_commit_relation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
issue_id UUID NOT NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
push_commit_id UUID NULL REFERENCES repo_push_commit(id) ON DELETE CASCADE,
|
|
commit_sha TEXT NOT NULL,
|
|
relation_type TEXT NOT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_commit_relation_issue_id ON issue_commit_relation (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_commit_relation_repo_id ON issue_commit_relation (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_commit_relation_push_commit_id ON issue_commit_relation (push_commit_id);
|
|
CREATE INDEX IF NOT EXISTS idx_issue_commit_relation_repo_created ON issue_commit_relation (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_branches.rs → repo_branch
|
|
CREATE TABLE IF NOT EXISTS repo_branch (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
commit_sha TEXT NOT NULL,
|
|
protected BOOLEAN NOT NULL,
|
|
default_branch BOOLEAN NOT NULL,
|
|
created_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
last_push_id UUID NULL REFERENCES repo_push_commit(id) ON DELETE CASCADE,
|
|
last_push_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_branch_repo_id ON repo_branch (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_branch_last_push_id ON repo_branch (last_push_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_branch_repo_created ON repo_branch (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_commit_comments.rs → repo_commit_comment
|
|
CREATE TABLE IF NOT EXISTS repo_commit_comment (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
push_commit_id UUID NOT NULL REFERENCES repo_push_commit(id) ON DELETE CASCADE,
|
|
commit_sha TEXT NOT NULL,
|
|
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
body TEXT NOT NULL,
|
|
path TEXT NULL,
|
|
line INTEGER NULL,
|
|
resolved BOOLEAN NOT NULL,
|
|
resolved_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
resolved_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_comment_repo_id ON repo_commit_comment (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_comment_push_commit_id ON repo_commit_comment (push_commit_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_comment_author_id ON repo_commit_comment (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_comment_repo_created ON repo_commit_comment (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_comment_deleted ON repo_commit_comment (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/repos/repo_commit_statuses.rs → repo_commit_status
|
|
CREATE TABLE IF NOT EXISTS repo_commit_status (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
push_commit_id UUID NOT NULL REFERENCES repo_push_commit(id) ON DELETE CASCADE,
|
|
latest_commit_sha TEXT NOT NULL,
|
|
context TEXT NOT NULL,
|
|
state TEXT NOT NULL,
|
|
target_url TEXT NULL,
|
|
description TEXT NULL,
|
|
reported_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
reported_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_status_repo_id ON repo_commit_status (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_status_push_commit_id ON repo_commit_status (push_commit_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_commit_status_repo_created ON repo_commit_status (repo_id, created_at DESC);
|
|
|
|
-- models/repos/repo_releases.rs → repo_release
|
|
CREATE TABLE IF NOT EXISTS repo_release (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
repo_id UUID NOT NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
tag_id UUID NULL REFERENCES repo_tag(id) ON DELETE CASCADE,
|
|
tag_name TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
body TEXT NULL,
|
|
draft BOOLEAN NOT NULL,
|
|
prerelease BOOLEAN NOT NULL,
|
|
author_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
published_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_release_repo_id ON repo_release (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_release_tag_id ON repo_release (tag_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_release_author_id ON repo_release (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_release_repo_created ON repo_release (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_repo_release_deleted ON repo_release (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/channels/channel_members.rs → channel_member
|
|
CREATE TABLE IF NOT EXISTS channel_member (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
muted BOOLEAN NOT NULL,
|
|
pinned BOOLEAN NOT NULL,
|
|
last_read_message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
last_read_at TIMESTAMPTZ NULL,
|
|
joined_at TIMESTAMPTZ NULL,
|
|
left_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
,
|
|
CONSTRAINT uq_channel_member UNIQUE (channel_id, user_id)
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_member_channel_id ON channel_member (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_member_user_id ON channel_member (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_member_last_read_message_id ON channel_member (last_read_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_member_user_created ON channel_member (user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_member_status_created ON channel_member (status, created_at DESC);
|
|
|
|
-- models/channels/message_bookmarks.rs → message_bookmark
|
|
CREATE TABLE IF NOT EXISTS message_bookmark (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_message_bookmark_message_id ON message_bookmark (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_bookmark_channel_id ON message_bookmark (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_bookmark_user_id ON message_bookmark (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_bookmark_user_created ON message_bookmark (user_id, created_at DESC);
|
|
|
|
-- models/channels/message_mentions.rs → message_mention
|
|
CREATE TABLE IF NOT EXISTS message_mention (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
mentioned_user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
mentioned_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
read_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_message_mention_message_id ON message_mention (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_mention_channel_id ON message_mention (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_mention_mentioned_user_id ON message_mention (mentioned_user_id);
|
|
|
|
-- models/channels/message_reactions.rs → message_reaction
|
|
CREATE TABLE IF NOT EXISTS message_reaction (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
content TEXT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_message_reaction_message_id ON message_reaction (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_reaction_channel_id ON message_reaction (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_reaction_user_id ON message_reaction (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_reaction_user_created ON message_reaction (user_id, created_at DESC);
|
|
|
|
-- models/channels/message_threads.rs → message_thread
|
|
CREATE TABLE IF NOT EXISTS message_thread (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel_id UUID NOT NULL REFERENCES channel(id) ON DELETE CASCADE,
|
|
root_message_id UUID NOT NULL,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
replies_count BIGINT NOT NULL,
|
|
participants_count BIGINT NOT NULL,
|
|
last_reply_message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
last_reply_at TIMESTAMPTZ NULL,
|
|
resolved BOOLEAN NOT NULL,
|
|
resolved_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
resolved_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_message_thread_channel_id ON message_thread (channel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_thread_root_message_id ON message_thread (root_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_message_thread_last_reply_message_id ON message_thread (last_reply_message_id);
|
|
|
|
-- models/conversations/conversation.rs → conversation
|
|
CREATE TABLE IF NOT EXISTS conversation (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
repo_id UUID NULL REFERENCES repo(id) ON DELETE CASCADE,
|
|
issue_id UUID NULL REFERENCES issue(id) ON DELETE CASCADE,
|
|
pull_request_id UUID NULL REFERENCES pull_request(id) ON DELETE CASCADE,
|
|
agent_id UUID NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
created_by UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
description TEXT NULL,
|
|
conversation_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
visibility TEXT NOT NULL,
|
|
pinned BOOLEAN NOT NULL,
|
|
archived BOOLEAN NOT NULL,
|
|
metadata JSONB NULL,
|
|
last_message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
last_message_at TIMESTAMPTZ NULL,
|
|
archived_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_workspace_id ON conversation (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_repo_id ON conversation (repo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_issue_id ON conversation (issue_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_pull_request_id ON conversation (pull_request_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_agent_id ON conversation (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_last_message_id ON conversation (last_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_repo_created ON conversation (repo_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_ws_created ON conversation (workspace_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_status_created ON conversation (status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_deleted ON conversation (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/notifications/notification.rs → notification
|
|
CREATE TABLE IF NOT EXISTS notification (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
actor_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
workspace_id UUID NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
notification_type TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
body TEXT NULL,
|
|
read_at TIMESTAMPTZ NULL,
|
|
dismissed_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_user_id ON notification (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_actor_id ON notification (actor_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_workspace_id ON notification (workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notification_user_created ON notification (user_id, created_at DESC);
|
|
|
|
-- models/agents/agent_feedback.rs → agent_feedback
|
|
CREATE TABLE IF NOT EXISTS agent_feedback (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
agent_id UUID NOT NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
execution_id UUID NULL REFERENCES agent_execution(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
rating INTEGER NOT NULL,
|
|
feedback_type TEXT NOT NULL,
|
|
comment TEXT NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_feedback_agent_id ON agent_feedback (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_feedback_execution_id ON agent_feedback (execution_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_feedback_user_id ON agent_feedback (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_feedback_user_created ON agent_feedback (user_id, created_at DESC);
|
|
|
|
-- models/conversations/conversation_attachments.rs → conversation_attachment
|
|
CREATE TABLE IF NOT EXISTS conversation_attachment (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversation(id) ON DELETE CASCADE,
|
|
message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
file_id UUID NULL REFERENCES conversation_attachment(id) ON DELETE CASCADE,
|
|
uploaded_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
content_type TEXT NULL,
|
|
size_bytes BIGINT NOT NULL,
|
|
storage_path TEXT NULL,
|
|
url TEXT NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_attachment_conversation_id ON conversation_attachment (conversation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_attachment_message_id ON conversation_attachment (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_attachment_file_id ON conversation_attachment (file_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_attachment_deleted ON conversation_attachment (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/conversations/conversation_bookmarks.rs → conversation_bookmark
|
|
CREATE TABLE IF NOT EXISTS conversation_bookmark (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversation(id) ON DELETE CASCADE,
|
|
message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
title TEXT NULL,
|
|
note TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_bookmark_conversation_id ON conversation_bookmark (conversation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_bookmark_message_id ON conversation_bookmark (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_bookmark_user_id ON conversation_bookmark (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_bookmark_user_created ON conversation_bookmark (user_id, created_at DESC);
|
|
|
|
-- models/conversations/conversation_messages.rs → conversation_message
|
|
CREATE TABLE IF NOT EXISTS conversation_message (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversation(id) ON DELETE CASCADE,
|
|
parent_message_id UUID NULL,
|
|
author_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
agent_id UUID NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
ai_model_id UUID NULL REFERENCES ai_model(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL,
|
|
message_type TEXT NOT NULL,
|
|
content TEXT NOT NULL,
|
|
content_format TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
metadata JSONB NULL,
|
|
token_input_count INTEGER NULL,
|
|
token_output_count INTEGER NULL,
|
|
edited_at TIMESTAMPTZ NULL,
|
|
deleted_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_conversation_id ON conversation_message (conversation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_parent_message_id ON conversation_message (parent_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_author_id ON conversation_message (author_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_agent_id ON conversation_message (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_ai_model_id ON conversation_message (ai_model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_status_created ON conversation_message (status, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_message_deleted ON conversation_message (deleted_at) WHERE deleted_at IS NOT NULL;
|
|
|
|
-- models/conversations/conversation_participants.rs → conversation_participant
|
|
CREATE TABLE IF NOT EXISTS conversation_participant (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversation(id) ON DELETE CASCADE,
|
|
user_id UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
agent_id UUID NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL,
|
|
participant_type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
muted BOOLEAN NOT NULL,
|
|
last_read_message_id UUID NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
last_read_at TIMESTAMPTZ NULL,
|
|
joined_at TIMESTAMPTZ NULL,
|
|
left_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_participant_conversation_id ON conversation_participant (conversation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_participant_user_id ON conversation_participant (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_participant_agent_id ON conversation_participant (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_participant_last_read_message_id ON conversation_participant (last_read_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_participant_user_created ON conversation_participant (user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_participant_status_created ON conversation_participant (status, created_at DESC);
|
|
|
|
-- models/conversations/conversation_tool_calls.rs → conversation_tool_call
|
|
CREATE TABLE IF NOT EXISTS conversation_tool_call (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversation(id) ON DELETE CASCADE,
|
|
message_id UUID NOT NULL REFERENCES message(id) ON DELETE CASCADE,
|
|
agent_id UUID NULL REFERENCES agent(id) ON DELETE CASCADE,
|
|
tool_name TEXT NOT NULL,
|
|
call_id TEXT NULL,
|
|
status TEXT NOT NULL,
|
|
arguments JSONB NULL,
|
|
result JSONB NULL,
|
|
error_message TEXT NULL,
|
|
started_at TIMESTAMPTZ NULL,
|
|
finished_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_tool_call_conversation_id ON conversation_tool_call (conversation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_tool_call_message_id ON conversation_tool_call (message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_tool_call_agent_id ON conversation_tool_call (agent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_tool_call_call_id ON conversation_tool_call (call_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_tool_call_status_created ON conversation_tool_call (status, created_at DESC);
|
|
|
|
-- models/conversations/conversation_summaries.rs → conversation_summary
|
|
CREATE TABLE IF NOT EXISTS conversation_summary (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversation(id) ON DELETE CASCADE,
|
|
ai_model_id UUID NULL REFERENCES ai_model(id) ON DELETE CASCADE,
|
|
generated_by UUID NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
|
from_message_id UUID NULL REFERENCES conversation_message(id) ON DELETE CASCADE,
|
|
to_message_id UUID NULL REFERENCES conversation_message(id) ON DELETE CASCADE,
|
|
summary_type TEXT NOT NULL,
|
|
content TEXT NOT NULL,
|
|
token_count INTEGER NULL,
|
|
metadata JSONB NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL
|
|
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_summary_conversation_id ON conversation_summary (conversation_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_summary_ai_model_id ON conversation_summary (ai_model_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_summary_from_message_id ON conversation_summary (from_message_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conversation_summary_to_message_id ON conversation_summary (to_message_id);
|
|
|
|
|
|
-- PHASE B: Deferred FKs (circular / self-referencing)
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_agent_execution_step_execution_id') THEN
|
|
ALTER TABLE agent_execution_step ADD CONSTRAINT fk_agent_execution_step_execution_id
|
|
FOREIGN KEY (execution_id) REFERENCES agent_execution(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_agent_current_version_id') THEN
|
|
ALTER TABLE agent ADD CONSTRAINT fk_agent_current_version_id
|
|
FOREIGN KEY (current_version_id) REFERENCES agent_version(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_channel_last_message_id') THEN
|
|
ALTER TABLE channel ADD CONSTRAINT fk_channel_last_message_id
|
|
FOREIGN KEY (last_message_id) REFERENCES message(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_message_thread_id') THEN
|
|
ALTER TABLE message ADD CONSTRAINT fk_message_thread_id
|
|
FOREIGN KEY (thread_id) REFERENCES message_thread(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_message_reply_to_message_id') THEN
|
|
ALTER TABLE message ADD CONSTRAINT fk_message_reply_to_message_id
|
|
FOREIGN KEY (reply_to_message_id) REFERENCES message(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_issue_comment_reply_to_comment_id') THEN
|
|
ALTER TABLE issue_comment ADD CONSTRAINT fk_issue_comment_reply_to_comment_id
|
|
FOREIGN KEY (reply_to_comment_id) REFERENCES issue_comment(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_message_thread_root_message_id') THEN
|
|
ALTER TABLE message_thread ADD CONSTRAINT fk_message_thread_root_message_id
|
|
FOREIGN KEY (root_message_id) REFERENCES message(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_conversation_message_parent_message_id') THEN
|
|
ALTER TABLE conversation_message ADD CONSTRAINT fk_conversation_message_parent_message_id
|
|
FOREIGN KEY (parent_message_id) REFERENCES conversation_message(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
COMMIT; |