🐸 Frogue(Fed)

~ololduck/Frogue

Raw

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> {
        manager
            .get_connection()
            .execute_unprepared(
                r#"
ALTER TABLE ap_reactions ADD COLUMN remote_note_id INTEGER;
ALTER TABLE ap_reactions ADD COLUMN remote_note_object_url TEXT;

-- Add foreign key for remote_note_id
-- SQLite doesn't support ADD CONSTRAINT, so we just add the column
-- The relationship will be enforced at the application level

-- Make note_id nullable (SQLite requires recreating the table)
CREATE TABLE ap_reactions_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    note_id INTEGER,
    remote_note_id INTEGER,
    remote_note_object_url TEXT,
    kind TEXT NOT NULL,
    emoji TEXT,
    remote_actor_id INTEGER,
    local_user_id INTEGER,
    activity_id TEXT,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (note_id) REFERENCES ap_notes(id) ON DELETE CASCADE,
    FOREIGN KEY (remote_note_id) REFERENCES ap_remote_notes(id) ON DELETE CASCADE,
    FOREIGN KEY (remote_actor_id) REFERENCES ap_remote_actors(id) ON DELETE CASCADE,
    FOREIGN KEY (local_user_id) REFERENCES users(id) ON DELETE CASCADE,
    CHECK (
        ((note_id IS NOT NULL) AND (remote_note_id IS NULL)) OR
        ((note_id IS NULL) AND (remote_note_id IS NOT NULL))
    ),
    CHECK ((remote_actor_id IS NOT NULL) != (local_user_id IS NOT NULL))
);

INSERT INTO ap_reactions_new (id, note_id, kind, emoji, remote_actor_id, local_user_id, activity_id, created_at)
SELECT id, note_id, kind, emoji, remote_actor_id, local_user_id, activity_id, created_at
FROM ap_reactions;

DROP TABLE ap_reactions;
ALTER TABLE ap_reactions_new RENAME TO ap_reactions;

-- Recreate indexes with updated logic
CREATE UNIQUE INDEX unique_ap_remote_reaction_local_note ON ap_reactions (note_id, remote_actor_id, kind, COALESCE(emoji, '')) WHERE note_id IS NOT NULL;
CREATE UNIQUE INDEX unique_ap_local_reaction ON ap_reactions (note_id, local_user_id, kind) WHERE note_id IS NOT NULL;
CREATE UNIQUE INDEX unique_ap_remote_reaction_remote_note ON ap_reactions (remote_note_id, remote_actor_id, kind, COALESCE(emoji, '')) WHERE remote_note_id IS NOT NULL;
CREATE UNIQUE INDEX unique_ap_local_reaction_remote_note ON ap_reactions (remote_note_id, local_user_id, kind) WHERE remote_note_id IS NOT NULL;
"#,
            )
            .await?;
        Ok(())
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .get_connection()
            .execute_unprepared(
                r#"
CREATE TABLE ap_reactions_old (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    note_id INTEGER NOT NULL,
    kind TEXT NOT NULL,
    emoji TEXT,
    remote_actor_id INTEGER,
    local_user_id INTEGER,
    activity_id TEXT,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (note_id) REFERENCES ap_notes(id) ON DELETE CASCADE,
    FOREIGN KEY (remote_actor_id) REFERENCES ap_remote_actors(id) ON DELETE CASCADE,
    FOREIGN KEY (local_user_id) REFERENCES users(id) ON DELETE CASCADE,
    CHECK ((remote_actor_id IS NOT NULL) != (local_user_id IS NOT NULL))
);

INSERT INTO ap_reactions_old
SELECT id, note_id, kind, emoji, remote_actor_id, local_user_id, activity_id, created_at
FROM ap_reactions
WHERE note_id IS NOT NULL;

DROP TABLE ap_reactions;
ALTER TABLE ap_reactions_old RENAME TO ap_reactions;

CREATE UNIQUE INDEX unique_ap_remote_reaction ON ap_reactions (note_id, remote_actor_id, kind, COALESCE(emoji, ''));
CREATE UNIQUE INDEX unique_ap_local_reaction ON ap_reactions (note_id, local_user_id, kind);
"#,
            )
            .await?;
        Ok(())
    }
}