🐸 Frogue(Fed)

~ololduck/Frogue

Raw

// SPDX-FileCopyrightText: 2026 Ololduck <ololduck@vit.am> and contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later

use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        // Recreate ap_actors without the XOR constraint to allow instance actor
        // (both user_id and repo_id NULL)
        manager
            .get_connection()
            .execute_unprepared(
                "CREATE TABLE IF NOT EXISTS ap_actors_new (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    user_id INTEGER,
                    repo_id INTEGER,
                    private_key_pem TEXT NOT NULL,
                    public_key_pem TEXT NOT NULL,
                    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
                    FOREIGN KEY (repo_id) REFERENCES repositories(id) ON DELETE CASCADE,
                    UNIQUE (user_id),
                    UNIQUE (repo_id)
                )",
            )
            .await?;

        // Copy data from old table
        manager
            .get_connection()
            .execute_unprepared("INSERT INTO ap_actors_new SELECT * FROM ap_actors")
            .await?;

        // Drop old table and rename new one
        manager
            .get_connection()
            .execute_unprepared("DROP TABLE ap_actors")
            .await?;

        manager
            .get_connection()
            .execute_unprepared("ALTER TABLE ap_actors_new RENAME TO ap_actors")
            .await?;

        Ok(())
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        // Recreate with XOR constraint
        manager
            .get_connection()
            .execute_unprepared(
                "CREATE TABLE IF NOT EXISTS ap_actors_old (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    user_id INTEGER UNIQUE,
                    repo_id INTEGER UNIQUE,
                    private_key_pem TEXT NOT NULL,
                    public_key_pem TEXT NOT NULL,
                    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
                    FOREIGN KEY (repo_id) REFERENCES repositories(id) ON DELETE CASCADE,
                    CHECK ((user_id IS NOT NULL) != (repo_id IS NOT NULL))
                )",
            )
            .await?;

        // Copy data (excluding instance actor if it exists)
        manager
            .get_connection()
            .execute_unprepared(
                "INSERT INTO ap_actors_old 
                 SELECT * FROM ap_actors 
                 WHERE user_id IS NOT NULL OR repo_id IS NOT NULL",
            )
            .await?;

        manager
            .get_connection()
            .execute_unprepared("DROP TABLE ap_actors")
            .await?;

        manager
            .get_connection()
            .execute_unprepared("ALTER TABLE ap_actors_old RENAME TO ap_actors")
            .await?;

        Ok(())
    }
}