Reconcile validated Turso runtime fix with republished main history

Hop-State: A_06FP6A2CG8PVBRDVQ5WGCN0
Hop-Proposal: R_06FP6A0YW23RY0N7Z5M8DW0
Hop-Task: T_06FP67JVF4BHPYKKC37XKQ8
Hop-Attempt: AT_06FP69RZ96ZMNF9Y5S02MT0
This commit is contained in:
2026-07-14 17:23:33 -07:00
committed by Hop
20 changed files with 65 additions and 41 deletions
+12 -6
View File
@@ -1,14 +1,20 @@
import { sql } from 'drizzle-orm';
import { migrate } from 'drizzle-orm/tursodatabase/migrator'; import { migrate } from 'drizzle-orm/tursodatabase/migrator';
import { db } from '../src/db'; import { closeDb, db } from '../src/db';
async function main() { async function main() {
const result = await migrate(db, { migrationsFolder: './drizzle' }); try {
const result = await migrate(db, { migrationsFolder: './drizzle' });
if (result) { if (result) {
throw new Error(`Database migration failed: ${JSON.stringify(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) => { main().catch((error) => {
+1 -1
View File
@@ -16,7 +16,7 @@ export async function GET() {
await requireAdmin(); await requireAdmin();
const nodes = await db.query.swarmNodes.findMany({ 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({ return NextResponse.json({
+1 -1
View File
@@ -24,7 +24,7 @@ export async function GET(request: Request) {
with: { with: {
author: true, author: true,
}, },
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
+1 -1
View File
@@ -17,7 +17,7 @@ export async function GET(request: Request) {
const reportRows = await db.query.reports.findMany({ const reportRows = await db.query.reports.findMany({
where: status === 'all' ? undefined : { status: status }, where: status === 'all' ? undefined : { status: status },
orderBy: () => [desc(reports.createdAt)], orderBy: (reports, { desc }) => [desc(reports.createdAt)],
limit, limit,
with: { with: {
reporter: true, reporter: true,
+1 -1
View File
@@ -50,7 +50,7 @@ export async function GET(request: Request) {
userId: user.id, userId: user.id,
...(unreadOnly ? { readAt: { isNull: true as const } } : {}), ...(unreadOnly ? { readAt: { isNull: true as const } } : {}),
}, },
orderBy: () => [desc(notifications.createdAt)], orderBy: (notifications, { desc }) => [desc(notifications.createdAt)],
limit, limit,
}); });
+1 -1
View File
@@ -178,7 +178,7 @@ export async function GET(
const replies = await db.query.posts.findMany({ const replies = await db.query.posts.findMany({
where: { AND: [{ replyToId: id }, { isRemoved: false }] }, where: { AND: [{ replyToId: id }, { isRemoved: false }] },
with: postDetailRelations, with: postDetailRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
}); });
mainPost = { mainPost = {
+8 -8
View File
@@ -631,7 +631,7 @@ export async function GET(request: Request) {
feedPosts = await db.query.posts.findMany({ feedPosts = await db.query.posts.findMany({
where: whereCondition, where: whereCondition,
with: feedPostRelations, with: feedPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
} else if (type === 'public') { } else if (type === 'public') {
@@ -639,13 +639,13 @@ export async function GET(request: Request) {
const localPosts = await db.query.posts.findMany({ const localPosts = await db.query.posts.findMany({
where: baseFilter, where: baseFilter,
with: feedPostRelations, with: feedPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit: limit * 2, limit: limit * 2,
}); });
// Get all cached remote posts // Get all cached remote posts
const remotePostsData = await db.query.remotePosts.findMany({ const remotePostsData = await db.query.remotePosts.findMany({
orderBy: () => [desc(remotePosts.publishedAt)], orderBy: (remotePosts, { desc }) => [desc(remotePosts.publishedAt)],
limit: limit, limit: limit,
}); });
@@ -676,7 +676,7 @@ export async function GET(request: Request) {
feedPosts = await db.query.posts.findMany({ feedPosts = await db.query.posts.findMany({
where: whereCondition, where: whereCondition,
with: feedPostRelations, with: feedPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
} else if (type === 'replies' && userId) { } else if (type === 'replies' && userId) {
@@ -700,7 +700,7 @@ export async function GET(request: Request) {
feedPosts = await db.query.posts.findMany({ feedPosts = await db.query.posts.findMany({
where: whereCondition, where: whereCondition,
with: feedPostRelations, with: feedPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
} else if (type === 'curated') { } else if (type === 'curated') {
@@ -868,7 +868,7 @@ export async function GET(request: Request) {
const localPosts = await db.query.posts.findMany({ const localPosts = await db.query.posts.findMany({
where: whereCondition, where: whereCondition,
with: feedPostRelations, 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 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({ const swarmRepostRows = await db.query.userSwarmReposts.findMany({
where: swarmRepostWhere, where: swarmRepostWhere,
orderBy: () => [desc(userSwarmReposts.repostedAt)], orderBy: (userSwarmReposts, { desc }) => [desc(userSwarmReposts.repostedAt)],
limit: cursor ? limit : limit * 2, limit: cursor ? limit : limit * 2,
}); });
@@ -967,7 +967,7 @@ export async function GET(request: Request) {
feedPosts = await db.query.posts.findMany({ feedPosts = await db.query.posts.findMany({
where: baseFilter, where: baseFilter,
with: feedPostRelations, with: feedPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
} }
+1 -1
View File
@@ -194,7 +194,7 @@ export async function GET(request: Request) {
...(moderatedIds.length ? { userId: { notIn: moderatedIds } } : {}), ...(moderatedIds.length ? { userId: { notIn: moderatedIds } } : {}),
}, },
with: searchPostRelations, with: searchPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
searchPosts = postResults; searchPosts = postResults;
@@ -23,10 +23,10 @@ export async function GET(request: NextRequest) {
// Get all conversations for this user // Get all conversations for this user
const conversations = await db.query.chatConversations.findMany({ const conversations = await db.query.chatConversations.findMany({
where: { participant1Id: session.user.id }, where: { participant1Id: session.user.id },
orderBy: () => [desc(chatConversations.lastMessageAt)], orderBy: (chatConversations, { desc }) => [desc(chatConversations.lastMessageAt)],
with: { with: {
messages: { messages: {
orderBy: () => [desc(chatMessages.createdAt)], orderBy: (chatMessages, { desc }) => [desc(chatMessages.createdAt)],
limit: 1, limit: 1,
}, },
}, },
+1 -1
View File
@@ -67,7 +67,7 @@ export async function GET(request: NextRequest) {
// Get messages // Get messages
const messages = await db.query.chatMessages.findMany({ const messages = await db.query.chatMessages.findMany({
where: whereCondition, where: whereCondition,
orderBy: () => [desc(chatMessages.createdAt)], orderBy: (chatMessages, { desc }) => [desc(chatMessages.createdAt)],
limit, limit,
}); });
+1 -1
View File
@@ -109,7 +109,7 @@ export async function GET(request: NextRequest, context: RouteContext) {
author: true, author: true,
media: true, media: true,
}, },
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit: 50, limit: 50,
}); });
+2 -2
View File
@@ -224,13 +224,13 @@ export async function GET(request: NextRequest, context: RouteContext) {
const localPosts = await db.query.posts.findMany({ const localPosts = await db.query.posts.findMany({
where: { AND: [{ userId: user.id }, { isRemoved: false }, { replyToId: { isNull: true } }, { swarmReplyToId: { isNull: true } }] }, where: { AND: [{ userId: user.id }, { isRemoved: false }, { replyToId: { isNull: true } }, { swarmReplyToId: { isNull: true } }] },
with: profilePostRelations, with: profilePostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit: limit * 2, limit: limit * 2,
}); });
const remoteRepostRows = await db.query.userSwarmReposts.findMany({ const remoteRepostRows = await db.query.userSwarmReposts.findMany({
where: { userId: user.id }, where: { userId: user.id },
orderBy: () => [desc(userSwarmReposts.repostedAt)], orderBy: (userSwarmReposts, { desc }) => [desc(userSwarmReposts.repostedAt)],
limit: limit * 2, limit: limit * 2,
}); });
+2 -2
View File
@@ -147,7 +147,7 @@ export async function GET(request: Request, context: RouteContext) {
with: likedPostRelations, with: likedPostRelations,
}, },
}, },
orderBy: () => [desc(likes.createdAt)], orderBy: (likes, { desc }) => [desc(likes.createdAt)],
limit, limit,
}); });
@@ -157,7 +157,7 @@ export async function GET(request: Request, context: RouteContext) {
const swarmLikedRows = await db.query.userSwarmLikes.findMany({ const swarmLikedRows = await db.query.userSwarmLikes.findMany({
where: { userId: user.id }, where: { userId: user.id },
orderBy: () => [desc(userSwarmLikes.likedAt)], orderBy: (userSwarmLikes, { desc }) => [desc(userSwarmLikes.likedAt)],
limit, limit,
}); });
+2 -2
View File
@@ -341,7 +341,7 @@ export async function GET(request: Request, context: RouteContext) {
const localPosts = await db.query.posts.findMany({ const localPosts = await db.query.posts.findMany({
where: whereConditions, where: whereConditions,
with: userPostRelations, with: userPostRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit: cursor ? limit : limit * 2, limit: cursor ? limit : limit * 2,
}); });
@@ -351,7 +351,7 @@ export async function GET(request: Request, context: RouteContext) {
}; };
const swarmRepostRows = await db.query.userSwarmReposts.findMany({ const swarmRepostRows = await db.query.userSwarmReposts.findMany({
where: swarmRepostWhere, where: swarmRepostWhere,
orderBy: () => [desc(userSwarmReposts.repostedAt)], orderBy: (userSwarmReposts, { desc }) => [desc(userSwarmReposts.repostedAt)],
limit: cursor ? limit : limit * 2, limit: cursor ? limit : limit * 2,
}); });
let userPosts: any[] = [ let userPosts: any[] = [
+1 -1
View File
@@ -143,7 +143,7 @@ export async function GET(request: Request, context: RouteContext) {
...(cursorDate ? { createdAt: { lt: cursorDate } } : {}), ...(cursorDate ? { createdAt: { lt: cursorDate } } : {}),
}, },
with: replyRelations, with: replyRelations,
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit, limit,
}); });
+19 -1
View File
@@ -10,7 +10,25 @@ if (databasePath !== ':memory:') {
mkdirSync(dirname(databasePath), { recursive: true }); mkdirSync(dirname(databasePath), { recursive: true });
} }
export const db = drizzle(databasePath, { relations }); const createDb = () => drizzle(databasePath, { relations });
type SynapsisDatabase = ReturnType<typeof createDb>;
const globalForDb = globalThis as typeof globalThis & {
synapsisDb?: SynapsisDatabase;
};
export const db = globalForDb.synapsisDb ?? createDb();
globalForDb.synapsisDb = db;
export async function closeDb(): Promise<void> {
if (!globalForDb.synapsisDb) {
return;
}
await globalForDb.synapsisDb.$client.close();
delete globalForDb.synapsisDb;
}
// Embedded Turso is always available; DATABASE_PATH only changes its location. // Embedded Turso is always available; DATABASE_PATH only changes its location.
export const isDbAvailable = () => true; export const isDbAvailable = () => true;
+2 -2
View File
@@ -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, limit: options.limit || 100,
offset: options.offset || 0, offset: options.offset || 0,
}); });
@@ -140,7 +140,7 @@ export async function getErrorLogs(
): Promise<ActivityLog[]> { ): Promise<ActivityLog[]> {
const logs = await db.query.botActivityLogs.findMany({ const logs = await db.query.botActivityLogs.findMany({
where: { AND: [{ botId: botId }, { success: false }] }, where: { AND: [{ botId: botId }, { success: false }] },
orderBy: () => [desc(botActivityLogs.createdAt)], orderBy: (botActivityLogs, { desc }) => [desc(botActivityLogs.createdAt)],
limit, limit,
}); });
+3 -3
View File
@@ -150,7 +150,7 @@ export async function detectMentions(botId: string): Promise<MentionDetectionRes
}, },
}, },
}, },
orderBy: () => [desc(posts.createdAt)], orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit: 1000, // Reasonable limit for scanning limit: 1000, // Reasonable limit for scanning
}); });
@@ -217,7 +217,7 @@ export async function getUnprocessedMentions(botId: string): Promise<Mention[]>
try { try {
const mentions = await db.query.botMentions.findMany({ const mentions = await db.query.botMentions.findMany({
where: { AND: [{ botId: botId }, { isProcessed: false }] }, 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 => ({ return mentions.map(m => ({
@@ -252,7 +252,7 @@ export async function getAllMentions(botId: string): Promise<Mention[]> {
try { try {
const mentions = await db.query.botMentions.findMany({ const mentions = await db.query.botMentions.findMany({
where: { botId: botId }, where: { botId: botId },
orderBy: () => [desc(botMentions.createdAt)], orderBy: (botMentions, { desc }) => [desc(botMentions.createdAt)],
}); });
return mentions.map(m => ({ return mentions.map(m => ({
+1 -1
View File
@@ -57,7 +57,7 @@ export async function buildGossipPayload(since?: string): Promise<SwarmGossipPay
const sinceDate = since ? new Date(since) : undefined; const sinceDate = since ? new Date(since) : undefined;
const handleEntries = await db.query.handleRegistry.findMany({ const handleEntries = await db.query.handleRegistry.findMany({
where: sinceDate ? { updatedAt: { gt: sinceDate } } : undefined, where: sinceDate ? { updatedAt: { gt: sinceDate } } : undefined,
orderBy: () => [desc(handleRegistry.updatedAt)], orderBy: (handleRegistry, { desc }) => [desc(handleRegistry.updatedAt)],
limit: SWARM_CONFIG.maxHandlesPerGossip, limit: SWARM_CONFIG.maxHandlesPerGossip,
}); });
+3 -3
View File
@@ -111,7 +111,7 @@ export async function getActiveSwarmNodes(limit = 100): Promise<SwarmNodeInfo[]>
const nodes = await db.query.swarmNodes.findMany({ const nodes = await db.query.swarmNodes.findMany({
where: { AND: [{ isActive: true }, { isBlocked: false }] }, where: { AND: [{ isActive: true }, { isBlocked: false }] },
orderBy: () => [desc(swarmNodes.lastSeenAt)], orderBy: (swarmNodes, { desc }) => [desc(swarmNodes.lastSeenAt)],
limit, limit,
}); });
@@ -146,7 +146,7 @@ export async function getNodesSince(since: Date, limit = 100): Promise<SwarmNode
const nodes = await db.query.swarmNodes.findMany({ const nodes = await db.query.swarmNodes.findMany({
where: { AND: [{ updatedAt: { gt: since } }, { isBlocked: false }] }, where: { AND: [{ updatedAt: { gt: since } }, { isBlocked: false }] },
orderBy: () => [desc(swarmNodes.updatedAt)], orderBy: (swarmNodes, { desc }) => [desc(swarmNodes.updatedAt)],
limit, limit,
}); });
@@ -265,7 +265,7 @@ export async function getSeedNodes(): Promise<string[]> {
const seeds = await db.query.swarmSeeds.findMany({ const seeds = await db.query.swarmSeeds.findMany({
where: { isEnabled: true }, where: { isEnabled: true },
orderBy: () => [swarmSeeds.priority], orderBy: (swarmSeeds) => [swarmSeeds.priority],
}); });
if (seeds.length === 0) { if (seeds.length === 0) {