refactor(migrate): replace hand-written migrations with SQL-file macro system

Replace individual Rust migration modules with a define_sql_migrations
macro that reads up/down SQL files via include_str!. Consolidate all
legacy single-file SQL into per-table directories and add full schema
migration coverage for 90+ tables.
This commit is contained in:
ZhenYi 2026-05-18 20:42:47 +08:00
parent 86d7cd2fe1
commit b413edccaf
224 changed files with 2341 additions and 2057 deletions

28
libs/migrate/bootstrap.rs Normal file
View File

@ -0,0 +1,28 @@
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"bootstrap"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(include_str!("sql/bootstrap/bootstrap_up_01.sql"))
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(include_str!("sql/bootstrap/bootstrap_down_01.sql"))
.await?;
Ok(())
}
}

View File

@ -1,54 +0,0 @@
//! SeaORM migration: create room_notifications table
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"init"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let sql = include_str!("sql/init.sql");
super::execute_sql(manager, sql).await?;
let e = "
CREATE OR REPLACE FUNCTION room_message_tsv_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
NEW.content_tsv := to_tsvector('english', NEW.content);
RETURN NEW;
END;
$function$;
create or replace trigger room_message_tsv_update
before insert or update
on room_message
for each row
execute procedure room_message_tsv_trigger();
DO
$$
BEGIN
IF NOT EXISTS (SELECT 1
FROM pg_type
WHERE typname = 'notification_type'
AND typtype = 'e') THEN
CREATE TYPE notification_type AS ENUM (
'mention',
'invitation',
'role_change',
'room_created',
'room_deleted',
'system_announcement'
);
END IF;
END
$$;";
manager.get_connection().execute_unprepared(e).await?;
Ok(())
}
}

View File

@ -35,23 +35,267 @@ mod tests {
}
}
pub struct Migrator;
macro_rules! define_sql_migrations {
($( $module:ident => { name: $name:literal, up: $up:literal, down: $down:literal } ),+ $(,)?) => {
$(
pub mod $module {
use sea_orm_migration::prelude::*;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(init::Migration),
Box::new(room_compact_summary::Migration),
Box::new(user_billing_history::Migration),
Box::new(project_message_favorite::Migration),
Box::new(ai_subagent_session::Migration),
]
}
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
$name
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let sql = include_str!($up);
super::execute_sql(manager, sql).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let sql = include_str!($down);
super::execute_sql(manager, sql).await?;
Ok(())
}
}
}
)+
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(user::Migration),
Box::new(user_password::Migration),
Box::new(user_email::Migration),
Box::new(user_2fa::Migration),
Box::new(user_notification::Migration),
Box::new(user_preferences::Migration),
Box::new(user_password_reset::Migration),
Box::new(user_relation::Migration),
Box::new(user_ssh_key::Migration),
Box::new(user_token::Migration),
Box::new(user_activity_log::Migration),
Box::new(project_access_log::Migration),
Box::new(project_audit_log::Migration),
Box::new(project_billing::Migration),
Box::new(project_billing_history::Migration),
Box::new(project_follow::Migration),
Box::new(project_history_name::Migration),
Box::new(project_label::Migration),
Box::new(project_like::Migration),
Box::new(project_member_invitations::Migration),
Box::new(project_member_join_answers::Migration),
Box::new(project_member_join_request::Migration),
Box::new(project_member_join_settings::Migration),
Box::new(project_members::Migration),
Box::new(project_watch::Migration),
Box::new(repo::Migration),
Box::new(repo_branch::Migration),
Box::new(repo_branch_protect::Migration),
Box::new(repo_collaborator::Migration),
Box::new(repo_commit::Migration),
Box::new(repo_fork::Migration),
Box::new(repo_history_name::Migration),
Box::new(repo_hook::Migration),
Box::new(repo_lfs_lock::Migration),
Box::new(repo_lfs_object::Migration),
Box::new(repo_lock::Migration),
Box::new(repo_star::Migration),
Box::new(repo_tag::Migration),
Box::new(repo_upstream::Migration),
Box::new(repo_watch::Migration),
Box::new(repo_webhook::Migration),
Box::new(issue::Migration),
Box::new(issue_assignee::Migration),
Box::new(issue_comment::Migration),
Box::new(issue_comment_reaction::Migration),
Box::new(issue_label::Migration),
Box::new(issue_pull_request::Migration),
Box::new(issue_reaction::Migration),
Box::new(issue_repo::Migration),
Box::new(issue_subscriber::Migration),
Box::new(pull_request::Migration),
Box::new(pull_request_commit::Migration),
Box::new(pull_request_review::Migration),
Box::new(pull_request_review_comment::Migration),
Box::new(room_category::Migration),
Box::new(room::Migration),
Box::new(room_ai::Migration),
Box::new(room_message::Migration),
Box::new(room_pin::Migration),
Box::new(room_thread::Migration),
Box::new(ai_model_provider::Migration),
Box::new(ai_model::Migration),
Box::new(ai_model_version::Migration),
Box::new(ai_model_capability::Migration),
Box::new(ai_model_parameter_profile::Migration),
Box::new(ai_model_pricing::Migration),
Box::new(ai_session::Migration),
Box::new(ai_tool_call::Migration),
Box::new(ai_tool_auth::Migration),
Box::new(label::Migration),
Box::new(notify::Migration),
Box::new(room_notifications::Migration),
Box::new(user_email_change::Migration),
Box::new(project_activity::Migration),
Box::new(room_message_reaction::Migration),
Box::new(room_message_edit_history::Migration),
Box::new(project_board::Migration),
Box::new(project_board_column::Migration),
Box::new(project_board_card::Migration),
Box::new(pull_request_review_request::Migration),
Box::new(workspace::Migration),
Box::new(project::Migration),
Box::new(workspace_membership::Migration),
Box::new(workspace_billing::Migration),
Box::new(workspace_billing_history::Migration),
Box::new(project_skill::Migration),
Box::new(agent_task::Migration),
Box::new(admin_user::Migration),
Box::new(admin_role::Migration),
Box::new(admin_permission::Migration),
Box::new(admin_user_role::Migration),
Box::new(admin_role_permission::Migration),
Box::new(admin_audit_log::Migration),
Box::new(admin_api_token::Migration),
Box::new(workspace_alert_config::Migration),
Box::new(room_attachment::Migration),
Box::new(room_access::Migration),
Box::new(room_user_state::Migration),
Box::new(project_role_priority::Migration),
Box::new(ai_conversation::Migration),
Box::new(ai_message::Migration),
Box::new(ai_message_fork::Migration),
Box::new(ai_shared_conversation::Migration),
Box::new(ai_token_usage::Migration),
Box::new(room_compact_summary::Migration),
Box::new(user_billing_history::Migration),
Box::new(project_message_favorite::Migration),
Box::new(ai_subagent_session::Migration),
Box::new(bootstrap::Migration),
]
}
}
};
}
pub mod ai_subagent_session;
pub mod init;
pub mod project_message_favorite;
pub mod room_compact_summary;
pub mod user_billing_history;
define_sql_migrations! {
user => { name: "user", up: "sql/user/user_up_01.sql", down: "sql/user/user_down_01.sql" },
user_password => { name: "user_password", up: "sql/user_password/user_password_up_01.sql", down: "sql/user_password/user_password_down_01.sql" },
user_email => { name: "user_email", up: "sql/user_email/user_email_up_01.sql", down: "sql/user_email/user_email_down_01.sql" },
user_2fa => { name: "user_2fa", up: "sql/user_2fa/user_2fa_up_01.sql", down: "sql/user_2fa/user_2fa_down_01.sql" },
user_notification => { name: "user_notification", up: "sql/user_notification/user_notification_up_01.sql", down: "sql/user_notification/user_notification_down_01.sql" },
user_preferences => { name: "user_preferences", up: "sql/user_preferences/user_preferences_up_01.sql", down: "sql/user_preferences/user_preferences_down_01.sql" },
user_password_reset => { name: "user_password_reset", up: "sql/user_password_reset/user_password_reset_up_01.sql", down: "sql/user_password_reset/user_password_reset_down_01.sql" },
user_relation => { name: "user_relation", up: "sql/user_relation/user_relation_up_01.sql", down: "sql/user_relation/user_relation_down_01.sql" },
user_ssh_key => { name: "user_ssh_key", up: "sql/user_ssh_key/user_ssh_key_up_01.sql", down: "sql/user_ssh_key/user_ssh_key_down_01.sql" },
user_token => { name: "user_token", up: "sql/user_token/user_token_up_01.sql", down: "sql/user_token/user_token_down_01.sql" },
user_activity_log => { name: "user_activity_log", up: "sql/user_activity_log/user_activity_log_up_01.sql", down: "sql/user_activity_log/user_activity_log_down_01.sql" },
project_access_log => { name: "project_access_log", up: "sql/project_access_log/project_access_log_up_01.sql", down: "sql/project_access_log/project_access_log_down_01.sql" },
project_audit_log => { name: "project_audit_log", up: "sql/project_audit_log/project_audit_log_up_01.sql", down: "sql/project_audit_log/project_audit_log_down_01.sql" },
project_billing => { name: "project_billing", up: "sql/project_billing/project_billing_up_01.sql", down: "sql/project_billing/project_billing_down_01.sql" },
project_billing_history => { name: "project_billing_history", up: "sql/project_billing_history/project_billing_history_up_01.sql", down: "sql/project_billing_history/project_billing_history_down_01.sql" },
project_follow => { name: "project_follow", up: "sql/project_follow/project_follow_up_01.sql", down: "sql/project_follow/project_follow_down_01.sql" },
project_history_name => { name: "project_history_name", up: "sql/project_history_name/project_history_name_up_01.sql", down: "sql/project_history_name/project_history_name_down_01.sql" },
project_label => { name: "project_label", up: "sql/project_label/project_label_up_01.sql", down: "sql/project_label/project_label_down_01.sql" },
project_like => { name: "project_like", up: "sql/project_like/project_like_up_01.sql", down: "sql/project_like/project_like_down_01.sql" },
project_member_invitations => { name: "project_member_invitations", up: "sql/project_member_invitations/project_member_invitations_up_01.sql", down: "sql/project_member_invitations/project_member_invitations_down_01.sql" },
project_member_join_answers => { name: "project_member_join_answers", up: "sql/project_member_join_answers/project_member_join_answers_up_01.sql", down: "sql/project_member_join_answers/project_member_join_answers_down_01.sql" },
project_member_join_request => { name: "project_member_join_request", up: "sql/project_member_join_request/project_member_join_request_up_01.sql", down: "sql/project_member_join_request/project_member_join_request_down_01.sql" },
project_member_join_settings => { name: "project_member_join_settings", up: "sql/project_member_join_settings/project_member_join_settings_up_01.sql", down: "sql/project_member_join_settings/project_member_join_settings_down_01.sql" },
project_members => { name: "project_members", up: "sql/project_members/project_members_up_01.sql", down: "sql/project_members/project_members_down_01.sql" },
project_watch => { name: "project_watch", up: "sql/project_watch/project_watch_up_01.sql", down: "sql/project_watch/project_watch_down_01.sql" },
repo => { name: "repo", up: "sql/repo/repo_up_01.sql", down: "sql/repo/repo_down_01.sql" },
repo_branch => { name: "repo_branch", up: "sql/repo_branch/repo_branch_up_01.sql", down: "sql/repo_branch/repo_branch_down_01.sql" },
repo_branch_protect => { name: "repo_branch_protect", up: "sql/repo_branch_protect/repo_branch_protect_up_01.sql", down: "sql/repo_branch_protect/repo_branch_protect_down_01.sql" },
repo_collaborator => { name: "repo_collaborator", up: "sql/repo_collaborator/repo_collaborator_up_01.sql", down: "sql/repo_collaborator/repo_collaborator_down_01.sql" },
repo_commit => { name: "repo_commit", up: "sql/repo_commit/repo_commit_up_01.sql", down: "sql/repo_commit/repo_commit_down_01.sql" },
repo_fork => { name: "repo_fork", up: "sql/repo_fork/repo_fork_up_01.sql", down: "sql/repo_fork/repo_fork_down_01.sql" },
repo_history_name => { name: "repo_history_name", up: "sql/repo_history_name/repo_history_name_up_01.sql", down: "sql/repo_history_name/repo_history_name_down_01.sql" },
repo_hook => { name: "repo_hook", up: "sql/repo_hook/repo_hook_up_01.sql", down: "sql/repo_hook/repo_hook_down_01.sql" },
repo_lfs_lock => { name: "repo_lfs_lock", up: "sql/repo_lfs_lock/repo_lfs_lock_up_01.sql", down: "sql/repo_lfs_lock/repo_lfs_lock_down_01.sql" },
repo_lfs_object => { name: "repo_lfs_object", up: "sql/repo_lfs_object/repo_lfs_object_up_01.sql", down: "sql/repo_lfs_object/repo_lfs_object_down_01.sql" },
repo_lock => { name: "repo_lock", up: "sql/repo_lock/repo_lock_up_01.sql", down: "sql/repo_lock/repo_lock_down_01.sql" },
repo_star => { name: "repo_star", up: "sql/repo_star/repo_star_up_01.sql", down: "sql/repo_star/repo_star_down_01.sql" },
repo_tag => { name: "repo_tag", up: "sql/repo_tag/repo_tag_up_01.sql", down: "sql/repo_tag/repo_tag_down_01.sql" },
repo_upstream => { name: "repo_upstream", up: "sql/repo_upstream/repo_upstream_up_01.sql", down: "sql/repo_upstream/repo_upstream_down_01.sql" },
repo_watch => { name: "repo_watch", up: "sql/repo_watch/repo_watch_up_01.sql", down: "sql/repo_watch/repo_watch_down_01.sql" },
repo_webhook => { name: "repo_webhook", up: "sql/repo_webhook/repo_webhook_up_01.sql", down: "sql/repo_webhook/repo_webhook_down_01.sql" },
issue => { name: "issue", up: "sql/issue/issue_up_01.sql", down: "sql/issue/issue_down_01.sql" },
issue_assignee => { name: "issue_assignee", up: "sql/issue_assignee/issue_assignee_up_01.sql", down: "sql/issue_assignee/issue_assignee_down_01.sql" },
issue_comment => { name: "issue_comment", up: "sql/issue_comment/issue_comment_up_01.sql", down: "sql/issue_comment/issue_comment_down_01.sql" },
issue_comment_reaction => { name: "issue_comment_reaction", up: "sql/issue_comment_reaction/issue_comment_reaction_up_01.sql", down: "sql/issue_comment_reaction/issue_comment_reaction_down_01.sql" },
issue_label => { name: "issue_label", up: "sql/issue_label/issue_label_up_01.sql", down: "sql/issue_label/issue_label_down_01.sql" },
issue_pull_request => { name: "issue_pull_request", up: "sql/issue_pull_request/issue_pull_request_up_01.sql", down: "sql/issue_pull_request/issue_pull_request_down_01.sql" },
issue_reaction => { name: "issue_reaction", up: "sql/issue_reaction/issue_reaction_up_01.sql", down: "sql/issue_reaction/issue_reaction_down_01.sql" },
issue_repo => { name: "issue_repo", up: "sql/issue_repo/issue_repo_up_01.sql", down: "sql/issue_repo/issue_repo_down_01.sql" },
issue_subscriber => { name: "issue_subscriber", up: "sql/issue_subscriber/issue_subscriber_up_01.sql", down: "sql/issue_subscriber/issue_subscriber_down_01.sql" },
pull_request => { name: "pull_request", up: "sql/pull_request/pull_request_up_01.sql", down: "sql/pull_request/pull_request_down_01.sql" },
pull_request_commit => { name: "pull_request_commit", up: "sql/pull_request_commit/pull_request_commit_up_01.sql", down: "sql/pull_request_commit/pull_request_commit_down_01.sql" },
pull_request_review => { name: "pull_request_review", up: "sql/pull_request_review/pull_request_review_up_01.sql", down: "sql/pull_request_review/pull_request_review_down_01.sql" },
pull_request_review_comment => { name: "pull_request_review_comment", up: "sql/pull_request_review_comment/pull_request_review_comment_up_01.sql", down: "sql/pull_request_review_comment/pull_request_review_comment_down_01.sql" },
room_category => { name: "room_category", up: "sql/room_category/room_category_up_01.sql", down: "sql/room_category/room_category_down_01.sql" },
room => { name: "room", up: "sql/room/room_up_01.sql", down: "sql/room/room_down_01.sql" },
room_ai => { name: "room_ai", up: "sql/room_ai/room_ai_up_01.sql", down: "sql/room_ai/room_ai_down_01.sql" },
room_message => { name: "room_message", up: "sql/room_message/room_message_up_01.sql", down: "sql/room_message/room_message_down_01.sql" },
room_pin => { name: "room_pin", up: "sql/room_pin/room_pin_up_01.sql", down: "sql/room_pin/room_pin_down_01.sql" },
room_thread => { name: "room_thread", up: "sql/room_thread/room_thread_up_01.sql", down: "sql/room_thread/room_thread_down_01.sql" },
ai_model_provider => { name: "ai_model_provider", up: "sql/ai_model_provider/ai_model_provider_up_01.sql", down: "sql/ai_model_provider/ai_model_provider_down_01.sql" },
ai_model => { name: "ai_model", up: "sql/ai_model/ai_model_up_01.sql", down: "sql/ai_model/ai_model_down_01.sql" },
ai_model_version => { name: "ai_model_version", up: "sql/ai_model_version/ai_model_version_up_01.sql", down: "sql/ai_model_version/ai_model_version_down_01.sql" },
ai_model_capability => { name: "ai_model_capability", up: "sql/ai_model_capability/ai_model_capability_up_01.sql", down: "sql/ai_model_capability/ai_model_capability_down_01.sql" },
ai_model_parameter_profile => { name: "ai_model_parameter_profile", up: "sql/ai_model_parameter_profile/ai_model_parameter_profile_up_01.sql", down: "sql/ai_model_parameter_profile/ai_model_parameter_profile_down_01.sql" },
ai_model_pricing => { name: "ai_model_pricing", up: "sql/ai_model_pricing/ai_model_pricing_up_01.sql", down: "sql/ai_model_pricing/ai_model_pricing_down_01.sql" },
ai_session => { name: "ai_session", up: "sql/ai_session/ai_session_up_01.sql", down: "sql/ai_session/ai_session_down_01.sql" },
ai_tool_call => { name: "ai_tool_call", up: "sql/ai_tool_call/ai_tool_call_up_01.sql", down: "sql/ai_tool_call/ai_tool_call_down_01.sql" },
ai_tool_auth => { name: "ai_tool_auth", up: "sql/ai_tool_auth/ai_tool_auth_up_01.sql", down: "sql/ai_tool_auth/ai_tool_auth_down_01.sql" },
label => { name: "label", up: "sql/label/label_up_01.sql", down: "sql/label/label_down_01.sql" },
notify => { name: "notify", up: "sql/notify/notify_up_01.sql", down: "sql/notify/notify_down_01.sql" },
room_notifications => { name: "room_notifications", up: "sql/room_notifications/room_notifications_up_01.sql", down: "sql/room_notifications/room_notifications_down_01.sql" },
user_email_change => { name: "user_email_change", up: "sql/user_email_change/user_email_change_up_01.sql", down: "sql/user_email_change/user_email_change_down_01.sql" },
project_activity => { name: "project_activity", up: "sql/project_activity/project_activity_up_01.sql", down: "sql/project_activity/project_activity_down_01.sql" },
room_message_reaction => { name: "room_message_reaction", up: "sql/room_message_reaction/room_message_reaction_up_01.sql", down: "sql/room_message_reaction/room_message_reaction_down_01.sql" },
room_message_edit_history => { name: "room_message_edit_history", up: "sql/room_message_edit_history/room_message_edit_history_up_01.sql", down: "sql/room_message_edit_history/room_message_edit_history_down_01.sql" },
project_board => { name: "project_board", up: "sql/project_board/project_board_up_01.sql", down: "sql/project_board/project_board_down_01.sql" },
project_board_column => { name: "project_board_column", up: "sql/project_board_column/project_board_column_up_01.sql", down: "sql/project_board_column/project_board_column_down_01.sql" },
project_board_card => { name: "project_board_card", up: "sql/project_board_card/project_board_card_up_01.sql", down: "sql/project_board_card/project_board_card_down_01.sql" },
pull_request_review_request => { name: "pull_request_review_request", up: "sql/pull_request_review_request/pull_request_review_request_up_01.sql", down: "sql/pull_request_review_request/pull_request_review_request_down_01.sql" },
workspace => { name: "workspace", up: "sql/workspace/workspace_up_01.sql", down: "sql/workspace/workspace_down_01.sql" },
project => { name: "project", up: "sql/project/project_up_01.sql", down: "sql/project/project_down_01.sql" },
workspace_membership => { name: "workspace_membership", up: "sql/workspace_membership/workspace_membership_up_01.sql", down: "sql/workspace_membership/workspace_membership_down_01.sql" },
workspace_billing => { name: "workspace_billing", up: "sql/workspace_billing/workspace_billing_up_01.sql", down: "sql/workspace_billing/workspace_billing_down_01.sql" },
workspace_billing_history => { name: "workspace_billing_history", up: "sql/workspace_billing_history/workspace_billing_history_up_01.sql", down: "sql/workspace_billing_history/workspace_billing_history_down_01.sql" },
project_skill => { name: "project_skill", up: "sql/project_skill/project_skill_up_01.sql", down: "sql/project_skill/project_skill_down_01.sql" },
agent_task => { name: "agent_task", up: "sql/agent_task/agent_task_up_01.sql", down: "sql/agent_task/agent_task_down_01.sql" },
admin_user => { name: "admin_user", up: "sql/admin_user/admin_user_up_01.sql", down: "sql/admin_user/admin_user_down_01.sql" },
admin_role => { name: "admin_role", up: "sql/admin_role/admin_role_up_01.sql", down: "sql/admin_role/admin_role_down_01.sql" },
admin_permission => { name: "admin_permission", up: "sql/admin_permission/admin_permission_up_01.sql", down: "sql/admin_permission/admin_permission_down_01.sql" },
admin_user_role => { name: "admin_user_role", up: "sql/admin_user_role/admin_user_role_up_01.sql", down: "sql/admin_user_role/admin_user_role_down_01.sql" },
admin_role_permission => { name: "admin_role_permission", up: "sql/admin_role_permission/admin_role_permission_up_01.sql", down: "sql/admin_role_permission/admin_role_permission_down_01.sql" },
admin_audit_log => { name: "admin_audit_log", up: "sql/admin_audit_log/admin_audit_log_up_01.sql", down: "sql/admin_audit_log/admin_audit_log_down_01.sql" },
admin_api_token => { name: "admin_api_token", up: "sql/admin_api_token/admin_api_token_up_01.sql", down: "sql/admin_api_token/admin_api_token_down_01.sql" },
workspace_alert_config => { name: "workspace_alert_config", up: "sql/workspace_alert_config/workspace_alert_config_up_01.sql", down: "sql/workspace_alert_config/workspace_alert_config_down_01.sql" },
room_attachment => { name: "room_attachment", up: "sql/room_attachment/room_attachment_up_01.sql", down: "sql/room_attachment/room_attachment_down_01.sql" },
room_access => { name: "room_access", up: "sql/room_access/room_access_up_01.sql", down: "sql/room_access/room_access_down_01.sql" },
room_user_state => { name: "room_user_state", up: "sql/room_user_state/room_user_state_up_01.sql", down: "sql/room_user_state/room_user_state_down_01.sql" },
project_role_priority => { name: "project_role_priority", up: "sql/project_role_priority/project_role_priority_up_01.sql", down: "sql/project_role_priority/project_role_priority_down_01.sql" },
ai_conversation => { name: "ai_conversation", up: "sql/ai_conversation/ai_conversation_up_01.sql", down: "sql/ai_conversation/ai_conversation_down_01.sql" },
ai_message => { name: "ai_message", up: "sql/ai_message/ai_message_up_01.sql", down: "sql/ai_message/ai_message_down_01.sql" },
ai_message_fork => { name: "ai_message_fork", up: "sql/ai_message_fork/ai_message_fork_up_01.sql", down: "sql/ai_message_fork/ai_message_fork_down_01.sql" },
ai_shared_conversation => { name: "ai_shared_conversation", up: "sql/ai_shared_conversation/ai_shared_conversation_up_01.sql", down: "sql/ai_shared_conversation/ai_shared_conversation_down_01.sql" },
ai_token_usage => { name: "ai_token_usage", up: "sql/ai_token_usage/ai_token_usage_up_01.sql", down: "sql/ai_token_usage/ai_token_usage_down_01.sql" },
room_compact_summary => { name: "room_compact_summary", up: "sql/room_compact_summary/room_compact_summary_up_01.sql", down: "sql/room_compact_summary/room_compact_summary_down_01.sql" },
user_billing_history => { name: "user_billing_history", up: "sql/user_billing_history/user_billing_history_up_01.sql", down: "sql/user_billing_history/user_billing_history_down_01.sql" },
project_message_favorite => { name: "project_message_favorite", up: "sql/project_message_favorite/project_message_favorite_up_01.sql", down: "sql/project_message_favorite/project_message_favorite_down_01.sql" },
ai_subagent_session => { name: "ai_subagent_session", up: "sql/ai_subagent_session/ai_subagent_session_up_01.sql", down: "sql/ai_subagent_session/ai_subagent_session_down_01.sql" },
}
pub mod bootstrap;

View File

@ -1,57 +0,0 @@
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"project_message_favorite"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(
r#"
create table if not exists project_message_favorite
(
uid uuid not null
primary key,
project uuid not null,
room uuid not null,
message uuid not null,
user_uuid uuid not null,
created_at timestamp with time zone not null
);
create unique index if not exists idx_project_message_favorite_user_message
on project_message_favorite (user_uuid, message);
create index if not exists idx_project_message_favorite_project_user
on project_message_favorite (project, user_uuid, created_at desc);
create index if not exists idx_project_message_favorite_room
on project_message_favorite (room);
"#,
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(
r#"
drop index if exists idx_project_message_favorite_room;
drop index if exists idx_project_message_favorite_project_user;
drop index if exists idx_project_message_favorite_user_message;
drop table if exists project_message_favorite;
"#,
)
.await?;
Ok(())
}
}

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_admin_api_token_hash;
DROP TABLE IF EXISTS admin_api_token;

View File

@ -0,0 +1,22 @@
create table if not exists admin_api_token
(
id serial
primary key,
name varchar(255) not null,
token_hash varchar(64) not null
unique,
token_prefix varchar(32) not null,
permissions text[] default '{}'::text[] not null,
created_by integer not null
references admin_user
on delete set null,
created_at timestamp with time zone default now() not null,
last_used_at timestamp with time zone,
expires_at timestamp with time zone,
is_active boolean default true not null
);
comment on table admin_api_token is 'Admin API Token for programmatic access';
create index if not exists idx_admin_api_token_hash
on admin_api_token (token_hash);

View File

@ -0,0 +1,5 @@
DROP INDEX IF EXISTS idx_admin_audit_log_resource;
DROP INDEX IF EXISTS idx_admin_audit_log_action;
DROP INDEX IF EXISTS idx_admin_audit_log_created_at;
DROP INDEX IF EXISTS idx_admin_audit_log_user_id;
DROP TABLE IF EXISTS admin_audit_log;

View File

@ -0,0 +1,28 @@
create table if not exists admin_audit_log
(
id bigserial
primary key,
user_id integer not null,
username varchar(255) not null,
action varchar(50) not null,
resource varchar(255) not null,
resource_id varchar(255),
request_params jsonb,
ip_address varchar(255),
user_agent text,
result varchar(20) default 'success'::character varying not null,
error_message text,
created_at timestamp with time zone default now() not null
);
create index if not exists idx_admin_audit_log_user_id
on admin_audit_log (user_id);
create index if not exists idx_admin_audit_log_created_at
on admin_audit_log (created_at desc);
create index if not exists idx_admin_audit_log_action
on admin_audit_log (action);
create index if not exists idx_admin_audit_log_resource
on admin_audit_log (resource);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS admin_permission;

View File

@ -0,0 +1,10 @@
create table if not exists admin_permission
(
id serial
primary key,
name varchar(255) not null,
code varchar(255) not null
unique,
description text,
created_at timestamp with time zone default now() not null
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS admin_role;

View File

@ -0,0 +1,9 @@
create table if not exists admin_role
(
id serial
primary key,
name varchar(255) not null
unique,
description text,
created_at timestamp with time zone default now() not null
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS admin_role_permission;

View File

@ -0,0 +1,10 @@
create table if not exists admin_role_permission
(
role_id integer not null
references admin_role
on delete cascade,
permission_id integer not null
references admin_permission
on delete cascade,
primary key (role_id, permission_id)
);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_admin_user_username;
DROP TABLE IF EXISTS admin_user;

View File

@ -0,0 +1,14 @@
create table if not exists admin_user
(
id serial
primary key,
username varchar(255) not null
unique,
password_hash varchar(255) not null,
is_active boolean default true not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
create index if not exists idx_admin_user_username
on admin_user (username);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS admin_user_role;

View File

@ -0,0 +1,10 @@
create table if not exists admin_user_role
(
user_id integer not null
references admin_user
on delete cascade,
role_id integer not null
references admin_role
on delete cascade,
primary key (user_id, role_id)
);

View File

@ -0,0 +1,8 @@
DROP INDEX IF EXISTS idx_agent_task_retry_count;
DROP INDEX IF EXISTS idx_agent_task_issue;
DROP INDEX IF EXISTS idx_agent_task_created_at;
DROP INDEX IF EXISTS idx_agent_task_created_by;
DROP INDEX IF EXISTS idx_agent_task_status;
DROP INDEX IF EXISTS idx_agent_task_parent;
DROP INDEX IF EXISTS idx_agent_task_project;
DROP TABLE IF EXISTS agent_task;

View File

@ -0,0 +1,45 @@
create table if not exists agent_task
(
id bigserial
primary key,
project_uuid uuid not null,
parent_id bigint
constraint fk_agent_task_parent
references agent_task
on delete set null,
agent_type varchar(20) default 'react'::character varying not null,
status varchar(20) default 'pending'::character varying not null,
title varchar(255),
input text not null,
output text,
error text,
created_by uuid,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null,
started_at timestamp with time zone,
done_at timestamp with time zone,
progress varchar(255),
issue_id uuid,
retry_count integer default 0
);
create index if not exists idx_agent_task_project
on agent_task (project_uuid);
create index if not exists idx_agent_task_parent
on agent_task (parent_id);
create index if not exists idx_agent_task_status
on agent_task (status);
create index if not exists idx_agent_task_created_by
on agent_task (created_by);
create index if not exists idx_agent_task_created_at
on agent_task (created_at);
create index if not exists idx_agent_task_issue
on agent_task (issue_id);
create index if not exists idx_agent_task_retry_count
on agent_task (retry_count);

View File

@ -0,0 +1,8 @@
DROP INDEX IF EXISTS idx_ai_conv_project_uid;
DROP INDEX IF EXISTS idx_ai_conv_access_vis;
DROP INDEX IF EXISTS idx_ai_conv_project_created;
DROP INDEX IF EXISTS idx_ai_conv_user_created;
DROP INDEX IF EXISTS idx_ai_conv_scope;
DROP INDEX IF EXISTS idx_ai_conv_project_id;
DROP INDEX IF EXISTS idx_ai_conv_user_id;
DROP TABLE IF EXISTS ai_conversation;

View File

@ -0,0 +1,49 @@
create table if not exists ai_conversation
(
id uuid default gen_random_uuid() not null
primary key,
user_id uuid not null,
project_id uuid
constraint fk_ai_conv_project
references project
on delete cascade,
scope varchar(16) not null,
title varchar(512),
model varchar(128) default 'gpt-4'::character varying not null,
model_config jsonb,
status varchar(32) default 'active'::character varying not null,
root_message_id uuid,
fork_count integer default 0 not null,
is_shared boolean default false not null,
message_count integer default 0 not null,
token_usage_total integer,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null,
access_visibility varchar(32) default 'owner'::character varying not null,
can_ask varchar(32) default 'owner'::character varying not null,
project_uid integer,
model_uid uuid,
model_name varchar(256)
);
create index if not exists idx_ai_conv_user_id
on ai_conversation (user_id);
create index if not exists idx_ai_conv_project_id
on ai_conversation (project_id);
create index if not exists idx_ai_conv_scope
on ai_conversation (scope);
create index if not exists idx_ai_conv_user_created
on ai_conversation (user_id asc, created_at desc);
create index if not exists idx_ai_conv_project_created
on ai_conversation (project_id asc, created_at desc);
create index if not exists idx_ai_conv_access_vis
on ai_conversation (access_visibility);
create index if not exists idx_ai_conv_project_uid
on ai_conversation (project_id, project_uid)
where (project_uid IS NOT NULL);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_ai_msg_parent;
DROP INDEX IF EXISTS idx_ai_msg_conv;
DROP TABLE IF EXISTS ai_message;

View File

@ -0,0 +1,30 @@
create table if not exists ai_message
(
id uuid default gen_random_uuid() not null
primary key,
conversation_id uuid not null
constraint fk_ai_msg_conv
references ai_conversation
on delete cascade,
parent_message_id uuid
constraint fk_ai_msg_parent
references ai_message
on delete set null,
role varchar(16) not null,
content jsonb not null,
model varchar(128),
is_fork_origin boolean default false not null,
stop_reason varchar(32),
input_tokens integer,
output_tokens integer,
latency_ms integer,
metadata jsonb,
room_id uuid,
created_at timestamp with time zone default now() not null
);
create index if not exists idx_ai_msg_conv
on ai_message (conversation_id, created_at);
create index if not exists idx_ai_msg_parent
on ai_message (parent_message_id);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_ai_fork_fork;
DROP INDEX IF EXISTS idx_ai_fork_source;
DROP TABLE IF EXISTS ai_message_fork;

View File

@ -0,0 +1,20 @@
create table if not exists ai_message_fork
(
id uuid default gen_random_uuid() not null
primary key,
source_message_id uuid not null
constraint fk_ai_fork_source
references ai_message
on delete cascade,
fork_message_id uuid not null
constraint fk_ai_fork_fork
references ai_message
on delete cascade,
created_at timestamp with time zone default now() not null
);
create index if not exists idx_ai_fork_source
on ai_message_fork (source_message_id);
create index if not exists idx_ai_fork_fork
on ai_message_fork (fork_message_id);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_ai_model_provider_id;
DROP TABLE IF EXISTS ai_model;

View File

@ -0,0 +1,19 @@
create table if not exists ai_model
(
id uuid not null
primary key,
provider_id uuid not null,
name varchar(255) not null,
modality varchar(255) not null,
capability varchar(255) not null,
context_length bigint not null,
max_output_tokens bigint,
training_cutoff timestamp with time zone,
is_open_source boolean default false not null,
status varchar(255) not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);
create index if not exists idx_ai_model_provider_id
on ai_model (provider_id);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_ai_model_capability_model_version_id;
DROP TABLE IF EXISTS ai_model_capability;

View File

@ -0,0 +1,12 @@
create table if not exists ai_model_capability
(
id bigserial
primary key,
model_version_id uuid not null,
capability varchar(255) not null,
is_supported boolean default false not null,
created_at timestamp with time zone not null
);
create index if not exists idx_ai_model_capability_model_version_id
on ai_model_capability (model_version_id);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_ai_model_parameter_profile_model_version_id;
DROP TABLE IF EXISTS ai_model_parameter_profile;

View File

@ -0,0 +1,16 @@
create table if not exists ai_model_parameter_profile
(
id bigserial
primary key,
model_version_id uuid not null
unique,
temperature_min double precision not null,
temperature_max double precision not null,
top_p_min double precision not null,
top_p_max double precision not null,
frequency_penalty_supported boolean default false not null,
presence_penalty_supported boolean default false not null
);
create unique index if not exists idx_ai_model_parameter_profile_model_version_id
on ai_model_parameter_profile (model_version_id);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_ai_model_pricing_model_version_id;
DROP TABLE IF EXISTS ai_model_pricing;

View File

@ -0,0 +1,13 @@
create table if not exists ai_model_pricing
(
id bigserial
primary key,
model_version_id uuid not null,
input_price_per_1k_tokens varchar(255) not null,
output_price_per_1k_tokens varchar(255) not null,
currency varchar(255) not null,
effective_from timestamp with time zone not null
);
create index if not exists idx_ai_model_pricing_model_version_id
on ai_model_pricing (model_version_id);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS ai_model_provider;

View File

@ -0,0 +1,11 @@
create table if not exists ai_model_provider
(
id uuid not null
primary key,
name varchar(255) not null,
display_name varchar(255) not null,
website varchar(255),
status varchar(255) not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_ai_model_version_model_id;
DROP TABLE IF EXISTS ai_model_version;

View File

@ -0,0 +1,15 @@
create table if not exists ai_model_version
(
id uuid not null
primary key,
model_id uuid not null,
version varchar(255) not null,
release_date timestamp with time zone,
change_log text,
is_default boolean default false not null,
status varchar(255) not null,
created_at timestamp with time zone not null
);
create index if not exists idx_ai_model_version_model_id
on ai_model_version (model_id);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_ai_session_room;
DROP TABLE IF EXISTS ai_session;

View File

@ -0,0 +1,19 @@
create table if not exists ai_session
(
id uuid not null
primary key,
room uuid not null,
model uuid not null,
version uuid not null,
token_input bigint default 0 not null,
token_output bigint default 0 not null,
latency_ms bigint,
cost double precision,
currency varchar(255),
error_message text,
error_code varchar(255),
created_at timestamp with time zone not null
);
create index if not exists idx_ai_session_room
on ai_session (room);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_ai_share_token;
DROP INDEX IF EXISTS idx_ai_share_conv;
DROP TABLE IF EXISTS ai_shared_conversation;

View File

@ -0,0 +1,21 @@
create table if not exists ai_shared_conversation
(
id uuid default gen_random_uuid() not null
primary key,
conversation_id uuid not null
constraint fk_ai_share_conv
references ai_conversation
on delete cascade,
share_token varchar(128) not null
unique,
created_by uuid not null,
view_count integer default 0 not null,
created_at timestamp with time zone default now() not null,
expires_at timestamp with time zone
);
create index if not exists idx_ai_share_conv
on ai_shared_conversation (conversation_id);
create index if not exists idx_ai_share_token
on ai_shared_conversation (share_token);

View File

@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_ai_subagent_session_message;
DROP INDEX IF EXISTS idx_ai_subagent_session_children;
DROP INDEX IF EXISTS idx_ai_subagent_session_conv;
DROP TABLE IF EXISTS ai_subagent_session;

View File

@ -1,20 +1,3 @@
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"ai_subagent_session"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(
r#"
create table if not exists ai_subagent_session
(
id uuid not null primary key,
@ -40,24 +23,3 @@ create index if not exists idx_ai_subagent_session_children
create index if not exists idx_ai_subagent_session_message
on ai_subagent_session (message_id);
"#,
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(
r#"
drop index if exists idx_ai_subagent_session_message;
drop index if exists idx_ai_subagent_session_children;
drop index if exists idx_ai_subagent_session_conv;
drop table if exists ai_subagent_session;
"#,
)
.await?;
Ok(())
}
}

View File

@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_ai_token_recorded;
DROP INDEX IF EXISTS idx_ai_token_conv;
DROP INDEX IF EXISTS idx_ai_token_user;
DROP TABLE IF EXISTS ai_token_usage;

View File

@ -0,0 +1,21 @@
create table if not exists ai_token_usage
(
id uuid default gen_random_uuid() not null
primary key,
user_id uuid not null,
conversation_id uuid,
model varchar(128) not null,
input_tokens integer not null,
output_tokens integer not null,
cost_usd numeric(10, 6),
recorded_at timestamp with time zone default now() not null
);
create index if not exists idx_ai_token_user
on ai_token_usage (user_id);
create index if not exists idx_ai_token_conv
on ai_token_usage (conversation_id);
create index if not exists idx_ai_token_recorded
on ai_token_usage (recorded_at);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS ai_tool_auth;

View File

@ -0,0 +1,17 @@
create table if not exists ai_tool_auth
(
session uuid not null,
tool_call_id varchar(255) not null,
method varchar(255) not null,
arguments text not null,
decision boolean default false not null,
reason varchar(255) not null,
decision_by uuid not null,
decision_comment text,
logs jsonb not null,
expires_at timestamp with time zone,
authorized_at timestamp with time zone,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
primary key (session, tool_call_id)
);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_ai_tool_call_status;
DROP INDEX IF EXISTS idx_ai_tool_call_session;
DROP TABLE IF EXISTS ai_tool_call;

View File

@ -0,0 +1,24 @@
create table if not exists ai_tool_call
(
tool_call_id varchar(255) not null,
session uuid not null,
tool_name varchar(255) not null,
caller uuid not null,
arguments jsonb not null,
result jsonb not null,
status varchar(255) not null,
execution_time_ms bigint,
error_message text,
error_stack text,
retry_count integer default 0 not null,
created_at timestamp with time zone not null,
completed_at timestamp with time zone,
updated_at timestamp with time zone not null,
primary key (tool_call_id, session)
);
create index if not exists idx_ai_tool_call_session
on ai_tool_call (session);
create index if not exists idx_ai_tool_call_status
on ai_tool_call (status);

View File

@ -0,0 +1,3 @@
DROP TRIGGER IF EXISTS room_message_tsv_update ON room_message;
DROP FUNCTION IF EXISTS room_message_tsv_trigger();
DROP TYPE IF EXISTS notification_type;

View File

@ -0,0 +1,33 @@
CREATE OR REPLACE FUNCTION room_message_tsv_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
NEW.content_tsv := to_tsvector('english', NEW.content);
RETURN NEW;
END;
$function$;
CREATE OR REPLACE TRIGGER room_message_tsv_update
BEFORE INSERT OR UPDATE
ON room_message
FOR EACH ROW
EXECUTE PROCEDURE room_message_tsv_trigger();
DO
$$
BEGIN
IF NOT EXISTS (SELECT 1
FROM pg_type
WHERE typname = 'notification_type'
AND typtype = 'e') THEN
CREATE TYPE notification_type AS ENUM (
'mention',
'invitation',
'role_change',
'room_created',
'room_deleted',
'system_announcement'
);
END IF;
END
$$;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_issue_state;
DROP INDEX IF EXISTS idx_issue_author;
DROP INDEX IF EXISTS idx_issue_project;
DROP TABLE IF EXISTS issue;

View File

@ -0,0 +1,25 @@
create table if not exists issue
(
id uuid not null
primary key,
project uuid not null,
number bigint not null,
title varchar(255) not null,
body text,
state varchar(255) not null,
author uuid not null,
milestone varchar(255),
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
closed_at timestamp with time zone,
created_by_ai boolean default false not null
);
create index if not exists idx_issue_project
on issue (project);
create index if not exists idx_issue_author
on issue (author);
create index if not exists idx_issue_state
on issue (state);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_assignee;

View File

@ -0,0 +1,7 @@
create table if not exists issue_assignee
(
issue uuid not null,
"user" uuid not null,
assigned_at timestamp with time zone not null,
primary key (issue, "user")
);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_issue_comment_issue;
DROP TABLE IF EXISTS issue_comment;

View File

@ -0,0 +1,13 @@
create table if not exists issue_comment
(
id bigserial
primary key,
issue uuid not null,
author uuid not null,
body text not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);
create index if not exists idx_issue_comment_issue
on issue_comment (issue);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_comment_reaction;

View File

@ -0,0 +1,8 @@
create table if not exists issue_comment_reaction
(
comment_id bigint not null,
user_uuid uuid not null,
reaction varchar(255) not null,
created_at timestamp with time zone not null,
primary key (comment_id, user_uuid, reaction)
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_label;

View File

@ -0,0 +1,7 @@
create table if not exists issue_label
(
issue uuid not null,
label bigint not null,
relation_at timestamp with time zone not null,
primary key (issue, label)
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_pull_request;

View File

@ -0,0 +1,8 @@
create table if not exists issue_pull_request
(
issue uuid not null,
repo uuid not null,
number bigint not null,
relation_at timestamp with time zone not null,
primary key (issue, repo, number)
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_reaction;

View File

@ -0,0 +1,8 @@
create table if not exists issue_reaction
(
issue_uuid uuid not null,
user_uuid uuid not null,
reaction varchar(255) not null,
created_at timestamp with time zone not null,
primary key (issue_uuid, user_uuid, reaction)
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_repo;

View File

@ -0,0 +1,7 @@
create table if not exists issue_repo
(
issue uuid not null,
repo uuid not null,
relation_at timestamp with time zone not null,
primary key (issue, repo)
);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS issue_subscriber;

View File

@ -0,0 +1,8 @@
create table if not exists issue_subscriber
(
issue uuid not null,
"user" uuid not null,
subscribed boolean default true not null,
created_at timestamp with time zone not null,
primary key (issue, "user")
);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_label_project;
DROP TABLE IF EXISTS label;

View File

@ -0,0 +1,11 @@
create table if not exists label
(
id bigserial
primary key,
project_uuid uuid not null,
name varchar(255) not null,
color varchar(255) not null
);
create index if not exists idx_label_project
on label (project_uuid);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_notify_created_at;
DROP INDEX IF EXISTS idx_notify_user;
DROP TABLE IF EXISTS notify;

View File

@ -0,0 +1,20 @@
create table if not exists notify
(
id bigserial
primary key,
user_uuid uuid not null,
title varchar(255) not null,
description text,
content text not null,
url varchar(255),
kind integer not null,
read_at timestamp with time zone,
deleted_at timestamp with time zone,
created_at timestamp with time zone not null
);
create index if not exists idx_notify_user
on notify (user_uuid);
create index if not exists idx_notify_created_at
on notify (created_at);

View File

@ -0,0 +1,6 @@
DROP INDEX IF EXISTS idx_workspace_deleted_at;
DROP INDEX IF EXISTS idx_workspace_slug;
DROP INDEX IF EXISTS idx_project_workspace_id;
DROP INDEX IF EXISTS idx_project_created_by;
DROP INDEX IF EXISTS idx_project_name;
DROP TABLE IF EXISTS project;

View File

@ -0,0 +1,32 @@
create table if not exists project
(
id uuid not null
primary key,
name varchar(255) not null,
display_name varchar(255) not null,
avatar_url varchar(255),
description text,
is_public boolean default false not null,
created_by uuid not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null,
workspace_id uuid
references workspace
on delete set null
);
create index if not exists idx_project_name
on project (name);
create index if not exists idx_project_created_by
on project (created_by);
create index if not exists idx_project_workspace_id
on project (workspace_id)
where (workspace_id IS NOT NULL);
create unique index if not exists idx_workspace_slug
on workspace (slug);
create index if not exists idx_workspace_deleted_at
on workspace (deleted_at);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_project_access_log_created_at;
DROP INDEX IF EXISTS idx_project_access_log_project;
DROP TABLE IF EXISTS project_access_log;

View File

@ -0,0 +1,17 @@
create table if not exists project_access_log
(
id bigserial
primary key,
project uuid not null,
actor_uid uuid,
action varchar(255) not null,
ip_address varchar(255),
user_agent varchar(255),
created_at timestamp with time zone not null
);
create index if not exists idx_project_access_log_project
on project_access_log (project);
create index if not exists idx_project_access_log_created_at
on project_access_log (created_at);

View File

@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_project_activity_event_type;
DROP INDEX IF EXISTS idx_project_activity_created_at;
DROP INDEX IF EXISTS idx_project_activity_project;
DROP TABLE IF EXISTS project_activity;

View File

@ -0,0 +1,25 @@
create table if not exists project_activity
(
id bigserial
primary key,
project uuid not null,
repo uuid,
actor uuid not null,
event_type varchar(50) not null,
event_id uuid,
event_sub_id bigint,
title varchar(500) not null,
content text,
metadata jsonb,
is_private boolean default false not null,
created_at timestamp with time zone not null
);
create index if not exists idx_project_activity_project
on project_activity (project);
create index if not exists idx_project_activity_created_at
on project_activity (created_at desc);
create index if not exists idx_project_activity_event_type
on project_activity (event_type);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_project_audit_log_created_at;
DROP INDEX IF EXISTS idx_project_audit_log_project;
DROP TABLE IF EXISTS project_audit_log;

View File

@ -0,0 +1,18 @@
create table if not exists project_audit_log
(
id bigserial
primary key,
project uuid not null,
actor uuid not null,
action text not null,
details jsonb,
ip_address varchar(255),
user_agent varchar(255),
created_at timestamp with time zone not null
);
create index if not exists idx_project_audit_log_project
on project_audit_log (project);
create index if not exists idx_project_audit_log_created_at
on project_audit_log (created_at);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS project_billing;

View File

@ -0,0 +1,10 @@
create table if not exists project_billing
(
project_uuid uuid not null
primary key,
balance numeric default 0.0 not null,
currency text not null,
user_uuid uuid,
updated_at timestamp with time zone not null,
created_at timestamp with time zone not null
);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_project_billing_history_project;
DROP TABLE IF EXISTS project_billing_history;

View File

@ -0,0 +1,15 @@
create table if not exists project_billing_history
(
uid uuid not null
primary key,
project uuid not null,
"user" uuid,
amount numeric not null,
currency text not null,
reason text not null,
extra jsonb,
created_at timestamp with time zone not null
);
create index if not exists idx_project_billing_history_project
on project_billing_history (project);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_project_board_project;
DROP TABLE IF EXISTS project_board;

View File

@ -0,0 +1,14 @@
create table if not exists project_board
(
id uuid default gen_random_uuid() not null
primary key,
project_uuid uuid not null,
name varchar(255) not null,
description text,
created_by uuid not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
create index if not exists idx_project_board_project
on project_board (project_uuid);

View File

@ -0,0 +1,3 @@
DROP INDEX IF EXISTS idx_project_board_card_issue;
DROP INDEX IF EXISTS idx_project_board_card_column;
DROP TABLE IF EXISTS project_board_card;

View File

@ -0,0 +1,26 @@
create table if not exists project_board_card
(
id uuid default gen_random_uuid() not null
primary key,
column_uuid uuid not null
references project_board_column
on delete cascade,
issue_id bigint,
project uuid,
title varchar(500) not null,
description text,
position integer default 0 not null,
assignee_id uuid,
due_date timestamp with time zone,
priority varchar(10),
created_by uuid not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
create index if not exists idx_project_board_card_column
on project_board_card (column_uuid);
create index if not exists idx_project_board_card_issue
on project_board_card (issue_id)
where (issue_id IS NOT NULL);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_project_board_column_board;
DROP TABLE IF EXISTS project_board_column;

View File

@ -0,0 +1,15 @@
create table if not exists project_board_column
(
id uuid default gen_random_uuid() not null
primary key,
board_uuid uuid not null
references project_board
on delete cascade,
name varchar(255) not null,
position integer default 0 not null,
wip_limit integer,
color varchar(20)
);
create index if not exists idx_project_board_column_board
on project_board_column (board_uuid);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_project_follow_project_user;
DROP TABLE IF EXISTS project_follow;

View File

@ -0,0 +1,12 @@
create table if not exists project_follow
(
id bigserial
primary key,
project uuid not null,
"user" uuid not null,
created_at timestamp with time zone not null,
unique (project, "user")
);
create unique index if not exists idx_project_follow_project_user
on project_follow (project, "user");

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_project_history_name_project_uid;
DROP TABLE IF EXISTS project_history_name;

View File

@ -0,0 +1,11 @@
create table if not exists project_history_name
(
id bigserial
primary key,
project_uid uuid not null,
history_name varchar(255) not null,
changed_at timestamp with time zone not null
);
create index if not exists idx_project_history_name_project_uid
on project_history_name (project_uid);

View File

@ -0,0 +1,2 @@
DROP INDEX IF EXISTS idx_project_label_project;
DROP TABLE IF EXISTS project_label;

View File

@ -0,0 +1,11 @@
create table if not exists project_label
(
id bigserial
primary key,
project_uuid uuid not null,
label_id bigint not null,
relation_at timestamp with time zone not null
);
create index if not exists idx_project_label_project
on project_label (project_uuid);

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS project_like;

Some files were not shown because too many files have changed in this diff Show More