diff --git a/scripts/migrate.ts b/scripts/migrate.ts index 15d5f6d..cd784d7 100644 --- a/scripts/migrate.ts +++ b/scripts/migrate.ts @@ -1,14 +1,20 @@ +import { sql } from 'drizzle-orm'; import { migrate } from 'drizzle-orm/tursodatabase/migrator'; -import { db } from '../src/db'; +import { closeDb, db } from '../src/db'; async function main() { - const result = await migrate(db, { migrationsFolder: './drizzle' }); + try { + const result = await migrate(db, { migrationsFolder: './drizzle' }); - if (result) { - throw new Error(`Database migration failed: ${JSON.stringify(result)}`); + if (result) { + throw new Error(`Database migration failed: ${JSON.stringify(result)}`); + } + + await db.run(sql.raw('PRAGMA wal_checkpoint(TRUNCATE)')); + console.log('Database migrations are up to date.'); + } finally { + await closeDb(); } - - console.log('Database migrations are up to date.'); } main().catch((error) => { diff --git a/src/app/api/admin/nodes/route.ts b/src/app/api/admin/nodes/route.ts index 3c1c2e8..32b9189 100644 --- a/src/app/api/admin/nodes/route.ts +++ b/src/app/api/admin/nodes/route.ts @@ -16,7 +16,7 @@ export async function GET() { await requireAdmin(); const nodes = await db.query.swarmNodes.findMany({ - orderBy: () => [desc(swarmNodes.isBlocked), desc(swarmNodes.blockedAt), desc(swarmNodes.lastSeenAt)], + orderBy: (swarmNodes, { desc }) => [desc(swarmNodes.isBlocked), desc(swarmNodes.blockedAt), desc(swarmNodes.lastSeenAt)], }); return NextResponse.json({ diff --git a/src/app/api/admin/posts/route.ts b/src/app/api/admin/posts/route.ts index 53f1bb6..8810df8 100644 --- a/src/app/api/admin/posts/route.ts +++ b/src/app/api/admin/posts/route.ts @@ -24,7 +24,7 @@ export async function GET(request: Request) { with: { author: true, }, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); diff --git a/src/app/api/admin/reports/route.ts b/src/app/api/admin/reports/route.ts index 352eec0..11100cd 100644 --- a/src/app/api/admin/reports/route.ts +++ b/src/app/api/admin/reports/route.ts @@ -17,7 +17,7 @@ export async function GET(request: Request) { const reportRows = await db.query.reports.findMany({ where: status === 'all' ? undefined : { status: status }, - orderBy: () => [desc(reports.createdAt)], + orderBy: (reports, { desc }) => [desc(reports.createdAt)], limit, with: { reporter: true, diff --git a/src/app/api/notifications/route.ts b/src/app/api/notifications/route.ts index 041614f..229d6d7 100644 --- a/src/app/api/notifications/route.ts +++ b/src/app/api/notifications/route.ts @@ -50,7 +50,7 @@ export async function GET(request: Request) { userId: user.id, ...(unreadOnly ? { readAt: { isNull: true as const } } : {}), }, - orderBy: () => [desc(notifications.createdAt)], + orderBy: (notifications, { desc }) => [desc(notifications.createdAt)], limit, }); diff --git a/src/app/api/posts/[id]/route.ts b/src/app/api/posts/[id]/route.ts index 43b9b83..03a49ec 100644 --- a/src/app/api/posts/[id]/route.ts +++ b/src/app/api/posts/[id]/route.ts @@ -178,7 +178,7 @@ export async function GET( const replies = await db.query.posts.findMany({ where: { AND: [{ replyToId: id }, { isRemoved: false }] }, with: postDetailRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], }); mainPost = { diff --git a/src/app/api/posts/route.ts b/src/app/api/posts/route.ts index 056bc62..64ba7dc 100644 --- a/src/app/api/posts/route.ts +++ b/src/app/api/posts/route.ts @@ -631,7 +631,7 @@ export async function GET(request: Request) { feedPosts = await db.query.posts.findMany({ where: whereCondition, with: feedPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); } else if (type === 'public') { @@ -639,13 +639,13 @@ export async function GET(request: Request) { const localPosts = await db.query.posts.findMany({ where: baseFilter, with: feedPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit: limit * 2, }); // Get all cached remote posts const remotePostsData = await db.query.remotePosts.findMany({ - orderBy: () => [desc(remotePosts.publishedAt)], + orderBy: (remotePosts, { desc }) => [desc(remotePosts.publishedAt)], limit: limit, }); @@ -676,7 +676,7 @@ export async function GET(request: Request) { feedPosts = await db.query.posts.findMany({ where: whereCondition, with: feedPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); } else if (type === 'replies' && userId) { @@ -700,7 +700,7 @@ export async function GET(request: Request) { feedPosts = await db.query.posts.findMany({ where: whereCondition, with: feedPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); } else if (type === 'curated') { @@ -868,7 +868,7 @@ export async function GET(request: Request) { const localPosts = await db.query.posts.findMany({ where: whereCondition, with: feedPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit: cursor ? limit : limit * 2, // Get more on first load to account for mixing with remote }); @@ -878,7 +878,7 @@ export async function GET(request: Request) { }; const swarmRepostRows = await db.query.userSwarmReposts.findMany({ where: swarmRepostWhere, - orderBy: () => [desc(userSwarmReposts.repostedAt)], + orderBy: (userSwarmReposts, { desc }) => [desc(userSwarmReposts.repostedAt)], limit: cursor ? limit : limit * 2, }); @@ -967,7 +967,7 @@ export async function GET(request: Request) { feedPosts = await db.query.posts.findMany({ where: baseFilter, with: feedPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); } diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index 1d09d3a..2f9f859 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -194,7 +194,7 @@ export async function GET(request: Request) { ...(moderatedIds.length ? { userId: { notIn: moderatedIds } } : {}), }, with: searchPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); searchPosts = postResults; diff --git a/src/app/api/swarm/chat/conversations/route.ts b/src/app/api/swarm/chat/conversations/route.ts index 896551a..bc3a2a6 100644 --- a/src/app/api/swarm/chat/conversations/route.ts +++ b/src/app/api/swarm/chat/conversations/route.ts @@ -23,10 +23,10 @@ export async function GET(request: NextRequest) { // Get all conversations for this user const conversations = await db.query.chatConversations.findMany({ where: { participant1Id: session.user.id }, - orderBy: () => [desc(chatConversations.lastMessageAt)], + orderBy: (chatConversations, { desc }) => [desc(chatConversations.lastMessageAt)], with: { messages: { - orderBy: () => [desc(chatMessages.createdAt)], + orderBy: (chatMessages, { desc }) => [desc(chatMessages.createdAt)], limit: 1, }, }, diff --git a/src/app/api/swarm/chat/messages/route.ts b/src/app/api/swarm/chat/messages/route.ts index 4fadd9a..5753ab0 100644 --- a/src/app/api/swarm/chat/messages/route.ts +++ b/src/app/api/swarm/chat/messages/route.ts @@ -67,7 +67,7 @@ export async function GET(request: NextRequest) { // Get messages const messages = await db.query.chatMessages.findMany({ where: whereCondition, - orderBy: () => [desc(chatMessages.createdAt)], + orderBy: (chatMessages, { desc }) => [desc(chatMessages.createdAt)], limit, }); diff --git a/src/app/api/swarm/posts/[id]/route.ts b/src/app/api/swarm/posts/[id]/route.ts index c326ba1..f4a6072 100644 --- a/src/app/api/swarm/posts/[id]/route.ts +++ b/src/app/api/swarm/posts/[id]/route.ts @@ -109,7 +109,7 @@ export async function GET(request: NextRequest, context: RouteContext) { author: true, media: true, }, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit: 50, }); diff --git a/src/app/api/swarm/users/[handle]/route.ts b/src/app/api/swarm/users/[handle]/route.ts index 0ad4b76..7a0f582 100644 --- a/src/app/api/swarm/users/[handle]/route.ts +++ b/src/app/api/swarm/users/[handle]/route.ts @@ -224,13 +224,13 @@ export async function GET(request: NextRequest, context: RouteContext) { const localPosts = await db.query.posts.findMany({ where: { AND: [{ userId: user.id }, { isRemoved: false }, { replyToId: { isNull: true } }, { swarmReplyToId: { isNull: true } }] }, with: profilePostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit: limit * 2, }); const remoteRepostRows = await db.query.userSwarmReposts.findMany({ where: { userId: user.id }, - orderBy: () => [desc(userSwarmReposts.repostedAt)], + orderBy: (userSwarmReposts, { desc }) => [desc(userSwarmReposts.repostedAt)], limit: limit * 2, }); diff --git a/src/app/api/users/[handle]/likes/route.ts b/src/app/api/users/[handle]/likes/route.ts index ab4d1a6..87b0b35 100644 --- a/src/app/api/users/[handle]/likes/route.ts +++ b/src/app/api/users/[handle]/likes/route.ts @@ -147,7 +147,7 @@ export async function GET(request: Request, context: RouteContext) { with: likedPostRelations, }, }, - orderBy: () => [desc(likes.createdAt)], + orderBy: (likes, { desc }) => [desc(likes.createdAt)], limit, }); @@ -157,7 +157,7 @@ export async function GET(request: Request, context: RouteContext) { const swarmLikedRows = await db.query.userSwarmLikes.findMany({ where: { userId: user.id }, - orderBy: () => [desc(userSwarmLikes.likedAt)], + orderBy: (userSwarmLikes, { desc }) => [desc(userSwarmLikes.likedAt)], limit, }); diff --git a/src/app/api/users/[handle]/posts/route.ts b/src/app/api/users/[handle]/posts/route.ts index 823d3df..93372a3 100644 --- a/src/app/api/users/[handle]/posts/route.ts +++ b/src/app/api/users/[handle]/posts/route.ts @@ -341,7 +341,7 @@ export async function GET(request: Request, context: RouteContext) { const localPosts = await db.query.posts.findMany({ where: whereConditions, with: userPostRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit: cursor ? limit : limit * 2, }); @@ -351,7 +351,7 @@ export async function GET(request: Request, context: RouteContext) { }; const swarmRepostRows = await db.query.userSwarmReposts.findMany({ where: swarmRepostWhere, - orderBy: () => [desc(userSwarmReposts.repostedAt)], + orderBy: (userSwarmReposts, { desc }) => [desc(userSwarmReposts.repostedAt)], limit: cursor ? limit : limit * 2, }); let userPosts: any[] = [ diff --git a/src/app/api/users/[handle]/replies/route.ts b/src/app/api/users/[handle]/replies/route.ts index 19bd57c..50541d4 100644 --- a/src/app/api/users/[handle]/replies/route.ts +++ b/src/app/api/users/[handle]/replies/route.ts @@ -143,7 +143,7 @@ export async function GET(request: Request, context: RouteContext) { ...(cursorDate ? { createdAt: { lt: cursorDate } } : {}), }, with: replyRelations, - orderBy: () => [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit, }); diff --git a/src/db/index.ts b/src/db/index.ts index fbf5112..afe665f 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -10,7 +10,25 @@ if (databasePath !== ':memory:') { mkdirSync(dirname(databasePath), { recursive: true }); } -export const db = drizzle(databasePath, { relations }); +const createDb = () => drizzle(databasePath, { relations }); +type SynapsisDatabase = ReturnType; + +const globalForDb = globalThis as typeof globalThis & { + synapsisDb?: SynapsisDatabase; +}; + +export const db = globalForDb.synapsisDb ?? createDb(); + +globalForDb.synapsisDb = db; + +export async function closeDb(): Promise { + if (!globalForDb.synapsisDb) { + return; + } + + await globalForDb.synapsisDb.$client.close(); + delete globalForDb.synapsisDb; +} // Embedded Turso is always available; DATABASE_PATH only changes its location. export const isDbAvailable = () => true; diff --git a/src/lib/bots/activityLogger.ts b/src/lib/bots/activityLogger.ts index a6147e7..4294c67 100644 --- a/src/lib/bots/activityLogger.ts +++ b/src/lib/bots/activityLogger.ts @@ -107,7 +107,7 @@ export async function getLogsForBot( }, } : {}), }, - orderBy: () => [desc(botActivityLogs.createdAt)], // Reverse chronological + orderBy: (botActivityLogs, { desc }) => [desc(botActivityLogs.createdAt)], // Reverse chronological limit: options.limit || 100, offset: options.offset || 0, }); @@ -140,7 +140,7 @@ export async function getErrorLogs( ): Promise { const logs = await db.query.botActivityLogs.findMany({ where: { AND: [{ botId: botId }, { success: false }] }, - orderBy: () => [desc(botActivityLogs.createdAt)], + orderBy: (botActivityLogs, { desc }) => [desc(botActivityLogs.createdAt)], limit, }); diff --git a/src/lib/bots/mentionHandler.ts b/src/lib/bots/mentionHandler.ts index df6c0b1..d93c4f4 100644 --- a/src/lib/bots/mentionHandler.ts +++ b/src/lib/bots/mentionHandler.ts @@ -150,7 +150,7 @@ export async function detectMentions(botId: string): Promise [desc(posts.createdAt)], + orderBy: (posts, { desc }) => [desc(posts.createdAt)], limit: 1000, // Reasonable limit for scanning }); @@ -217,7 +217,7 @@ export async function getUnprocessedMentions(botId: string): Promise try { const mentions = await db.query.botMentions.findMany({ where: { AND: [{ botId: botId }, { isProcessed: false }] }, - orderBy: () => [asc(botMentions.createdAt)], // Chronological order (oldest first) + orderBy: (botMentions, { asc }) => [asc(botMentions.createdAt)], // Chronological order (oldest first) }); return mentions.map(m => ({ @@ -252,7 +252,7 @@ export async function getAllMentions(botId: string): Promise { try { const mentions = await db.query.botMentions.findMany({ where: { botId: botId }, - orderBy: () => [desc(botMentions.createdAt)], + orderBy: (botMentions, { desc }) => [desc(botMentions.createdAt)], }); return mentions.map(m => ({ diff --git a/src/lib/swarm/gossip.ts b/src/lib/swarm/gossip.ts index e6b95c8..876e27c 100644 --- a/src/lib/swarm/gossip.ts +++ b/src/lib/swarm/gossip.ts @@ -57,7 +57,7 @@ export async function buildGossipPayload(since?: string): Promise [desc(handleRegistry.updatedAt)], + orderBy: (handleRegistry, { desc }) => [desc(handleRegistry.updatedAt)], limit: SWARM_CONFIG.maxHandlesPerGossip, }); diff --git a/src/lib/swarm/registry.ts b/src/lib/swarm/registry.ts index 921d720..e0cf176 100644 --- a/src/lib/swarm/registry.ts +++ b/src/lib/swarm/registry.ts @@ -111,7 +111,7 @@ export async function getActiveSwarmNodes(limit = 100): Promise const nodes = await db.query.swarmNodes.findMany({ where: { AND: [{ isActive: true }, { isBlocked: false }] }, - orderBy: () => [desc(swarmNodes.lastSeenAt)], + orderBy: (swarmNodes, { desc }) => [desc(swarmNodes.lastSeenAt)], limit, }); @@ -146,7 +146,7 @@ export async function getNodesSince(since: Date, limit = 100): Promise [desc(swarmNodes.updatedAt)], + orderBy: (swarmNodes, { desc }) => [desc(swarmNodes.updatedAt)], limit, }); @@ -265,7 +265,7 @@ export async function getSeedNodes(): Promise { const seeds = await db.query.swarmSeeds.findMany({ where: { isEnabled: true }, - orderBy: () => [swarmSeeds.priority], + orderBy: (swarmSeeds) => [swarmSeeds.priority], }); if (seeds.length === 0) {