242 lines
6.6 KiB
TypeScript
242 lines
6.6 KiB
TypeScript
import path from "node:path";
|
|
import Database from "better-sqlite3";
|
|
import { createChildLogger } from "../src/logger";
|
|
import * as postgres from "../src/database/postgres";
|
|
|
|
const logger = createChildLogger("migrate-data");
|
|
|
|
interface MuxerJob {
|
|
id: string;
|
|
data: string;
|
|
status: string;
|
|
attempts: number;
|
|
maxAttempts: number;
|
|
createdAt: number;
|
|
updatedAt: number;
|
|
error?: string;
|
|
}
|
|
|
|
interface Message {
|
|
id: string;
|
|
guild_id: string;
|
|
channel_id: string;
|
|
thread_id?: string;
|
|
user_id: string;
|
|
username: string;
|
|
avatar_url?: string;
|
|
content: string;
|
|
edited_content?: string;
|
|
created_at: number;
|
|
edited_at?: number;
|
|
deleted_at?: number;
|
|
type: string;
|
|
metadata?: string;
|
|
ai_status: string;
|
|
ai_moderation_flags?: string;
|
|
ai_moderation_score?: number;
|
|
ai_moderation_raw?: string;
|
|
ai_analysis?: string;
|
|
ai_analyzed_at?: number;
|
|
ai_error?: string;
|
|
}
|
|
|
|
interface Attachment {
|
|
id: string;
|
|
message_id: string;
|
|
guild_id: string;
|
|
channel_id: string;
|
|
thread_id?: string;
|
|
user_id: string;
|
|
filename: string;
|
|
size: number;
|
|
type: string;
|
|
discord_url: string;
|
|
uploaded_url?: string;
|
|
upload_status: string;
|
|
upload_error?: string;
|
|
created_at: number;
|
|
uploaded_at?: number;
|
|
}
|
|
|
|
interface UiState {
|
|
key: string;
|
|
value: string;
|
|
updated_at: number;
|
|
}
|
|
|
|
async function migrateData(): Promise<void> {
|
|
let sqliteDb: Database.Database | null = null;
|
|
|
|
try {
|
|
logger.info("Starting data migration from SQLite to PostgreSQL");
|
|
|
|
// Open SQLite database
|
|
const dbPath = path.join(process.cwd(), ".muxer-queue.db");
|
|
sqliteDb = new Database(dbPath);
|
|
logger.info({ dbPath }, "SQLite database opened");
|
|
|
|
// Initialize PostgreSQL pool
|
|
const pool = postgres.getPool();
|
|
logger.info("PostgreSQL connection pool initialized");
|
|
|
|
// Migrate muxer_jobs table
|
|
logger.info("Migrating muxer_jobs table...");
|
|
const muxerJobsStmt = sqliteDb.prepare("SELECT * FROM muxer_jobs");
|
|
const muxerJobs = muxerJobsStmt.all() as MuxerJob[];
|
|
|
|
for (const job of muxerJobs) {
|
|
await postgres.query(
|
|
`INSERT INTO muxer_jobs (id, data, status, attempts, maxAttempts, createdAt, updatedAt, error)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
ON CONFLICT (id) DO NOTHING`,
|
|
[
|
|
job.id,
|
|
job.data,
|
|
job.status,
|
|
job.attempts,
|
|
job.maxAttempts,
|
|
job.createdAt,
|
|
job.updatedAt,
|
|
job.error || null,
|
|
],
|
|
);
|
|
}
|
|
logger.info({ count: muxerJobs.length }, "Migrated muxer_jobs");
|
|
|
|
// Migrate messages table
|
|
logger.info("Migrating messages table...");
|
|
const messagesStmt = sqliteDb.prepare("SELECT * FROM messages");
|
|
const messages = messagesStmt.all() as Message[];
|
|
|
|
for (const msg of messages) {
|
|
await postgres.query(
|
|
`INSERT INTO messages (
|
|
id, guild_id, channel_id, thread_id, user_id, username, avatar_url,
|
|
content, edited_content, created_at, edited_at, deleted_at, type,
|
|
metadata, ai_status, ai_moderation_flags, ai_moderation_score,
|
|
ai_moderation_raw, ai_analysis, ai_analyzed_at, ai_error
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,
|
|
$16, $17, $18, $19, $20, $21
|
|
)
|
|
ON CONFLICT (id) DO NOTHING`,
|
|
[
|
|
msg.id,
|
|
msg.guild_id,
|
|
msg.channel_id,
|
|
msg.thread_id || null,
|
|
msg.user_id,
|
|
msg.username,
|
|
msg.avatar_url || null,
|
|
msg.content,
|
|
msg.edited_content || null,
|
|
msg.created_at,
|
|
msg.edited_at || null,
|
|
msg.deleted_at || null,
|
|
msg.type,
|
|
msg.metadata || null,
|
|
msg.ai_status,
|
|
msg.ai_moderation_flags || null,
|
|
msg.ai_moderation_score || null,
|
|
msg.ai_moderation_raw || null,
|
|
msg.ai_analysis || null,
|
|
msg.ai_analyzed_at || null,
|
|
msg.ai_error || null,
|
|
],
|
|
);
|
|
}
|
|
logger.info({ count: messages.length }, "Migrated messages");
|
|
|
|
// Migrate attachments table
|
|
logger.info("Migrating attachments table...");
|
|
const attachmentsStmt = sqliteDb.prepare("SELECT * FROM attachments");
|
|
const attachments = attachmentsStmt.all() as Attachment[];
|
|
|
|
for (const att of attachments) {
|
|
await postgres.query(
|
|
`INSERT INTO attachments (
|
|
id, message_id, guild_id, channel_id, thread_id, user_id, filename,
|
|
size, type, discord_url, uploaded_url, upload_status, upload_error,
|
|
created_at, uploaded_at
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
|
|
)
|
|
ON CONFLICT (id) DO NOTHING`,
|
|
[
|
|
att.id,
|
|
att.message_id,
|
|
att.guild_id,
|
|
att.channel_id,
|
|
att.thread_id || null,
|
|
att.user_id,
|
|
att.filename,
|
|
att.size,
|
|
att.type,
|
|
att.discord_url,
|
|
att.uploaded_url || null,
|
|
att.upload_status,
|
|
att.upload_error || null,
|
|
att.created_at,
|
|
att.uploaded_at || null,
|
|
],
|
|
);
|
|
}
|
|
logger.info({ count: attachments.length }, "Migrated attachments");
|
|
|
|
// Migrate ui_state table
|
|
logger.info("Migrating ui_state table...");
|
|
const uiStateStmt = sqliteDb.prepare("SELECT * FROM ui_state");
|
|
const uiStates = uiStateStmt.all() as UiState[];
|
|
|
|
for (const state of uiStates) {
|
|
await postgres.query(
|
|
`INSERT INTO ui_state (key, value, updated_at)
|
|
VALUES ($1, $2, $3)
|
|
ON CONFLICT (key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`,
|
|
[state.key, state.value, state.updated_at],
|
|
);
|
|
}
|
|
logger.info({ count: uiStates.length }, "Migrated ui_state");
|
|
|
|
logger.info(
|
|
{
|
|
muxerJobs: muxerJobs.length,
|
|
messages: messages.length,
|
|
attachments: attachments.length,
|
|
uiState: uiStates.length,
|
|
},
|
|
"Data migration completed successfully",
|
|
);
|
|
} catch (error) {
|
|
logger.error(
|
|
{
|
|
error: error instanceof Error ? error.message : String(error),
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
},
|
|
"Data migration failed",
|
|
);
|
|
process.exit(1);
|
|
} finally {
|
|
// Close SQLite connection
|
|
if (sqliteDb) {
|
|
sqliteDb.close();
|
|
logger.info("SQLite database closed");
|
|
}
|
|
|
|
// Close PostgreSQL pool
|
|
await postgres.closePool();
|
|
logger.info("PostgreSQL connection pool closed");
|
|
}
|
|
}
|
|
|
|
// Run migration
|
|
migrateData().catch((error) => {
|
|
logger.error(
|
|
{
|
|
error: error instanceof Error ? error.message : String(error),
|
|
},
|
|
"Unhandled error in migration",
|
|
);
|
|
process.exit(1);
|
|
});
|