From 3f7fec37a6cff2d218df99b6d0a945828f608026 Mon Sep 17 00:00:00 2001 From: Guido X Jansen Date: Fri, 20 Feb 2026 21:46:38 +0100 Subject: [PATCH] chore: pre-P3 cleanup batch - Consolidate getCollectionFromUri into lib/at-uri.ts (was duplicated in firehose/indexers/reaction.ts and routes/reactions.ts) - Add .catch() to fire-and-forget profileSync promise in auth callback - Convert health.ts from default export to named export for consistency - Add standardized "Drizzle execute() returns untyped rows" comments above all as-unknown casts (9 locations) - Annotate TODOs with tracking issue numbers (#35-#38) - Add didRegex mirror comment in block-mute.ts - Remove empty directories: src/mocks/, src/plugins/, src/lib/api/ --- src/app.ts | 2 +- src/auth/middleware.ts | 2 +- src/auth/oauth-client.ts | 2 +- src/auth/session.ts | 2 +- src/firehose/indexers/reaction.ts | 9 +-------- src/lib/at-uri.ts | 9 +++++++++ src/routes/admin-settings.ts | 3 ++- src/routes/auth.ts | 4 +++- src/routes/global-filters.ts | 1 + src/routes/health.ts | 4 +--- src/routes/reactions.ts | 11 +---------- src/routes/search.ts | 6 ++++++ src/services/ban-propagation.ts | 1 + src/validation/block-mute.ts | 1 + 14 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/app.ts b/src/app.ts index fe45f81..7c655dd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -18,7 +18,7 @@ import { createSessionService } from './auth/session.js' import type { SessionService } from './auth/session.js' import { createAuthMiddleware } from './auth/middleware.js' import type { AuthMiddleware, RequestUser } from './auth/middleware.js' -import healthRoutes from './routes/health.js' +import { healthRoutes } from './routes/health.js' import { oauthMetadataRoutes } from './routes/oauth-metadata.js' import { authRoutes } from './routes/auth.js' import { setupRoutes } from './routes/setup.js' diff --git a/src/auth/middleware.ts b/src/auth/middleware.ts index bfe34c4..dbc6ba3 100644 --- a/src/auth/middleware.ts +++ b/src/auth/middleware.ts @@ -56,7 +56,7 @@ function extractBearerToken(request: FastifyRequest): string | undefined { // Factory // --------------------------------------------------------------------------- -// TODO(self-hosting): Add DID document verification with 1-hour cache TTL. +// TODO(self-hosting): Add DID document verification with 1-hour cache TTL. (#37) // Currently trusts the DID from the session. Full verification requires // PLC directory / DNS resolution (see standards/backend.md). // Not needed for single-instance MVP (trusted Valkey on same host). diff --git a/src/auth/oauth-client.ts b/src/auth/oauth-client.ts index 1c17d6b..ffe602a 100644 --- a/src/auth/oauth-client.ts +++ b/src/auth/oauth-client.ts @@ -57,7 +57,7 @@ function createRequestLock(cache: Cache, logger: Logger): RuntimeLock { try { return await fn() } finally { - // TODO(multi-instance): Use Redlock or check-and-delete Lua script for multi-instance safety. + // TODO(multi-instance): Use Redlock or check-and-delete Lua script for multi-instance safety. (#35) // Current simple DEL does not verify lock ownership; safe for single-instance MVP. // Only needed when SaaS tier runs multiple API instances against shared Valkey. try { diff --git a/src/auth/session.ts b/src/auth/session.ts index 1dc4228..254ffb9 100644 --- a/src/auth/session.ts +++ b/src/auth/session.ts @@ -257,7 +257,7 @@ export function createSessionService( } // Delete each session individually (cleans up access token lookups too) - // TODO(phase-3): Pipeline deletes for performance when moving to multi-instance + // TODO(phase-3): Pipeline deletes for performance when moving to multi-instance (#36) for (const sid of sids) { await deleteSession(sid) } diff --git a/src/firehose/indexers/reaction.ts b/src/firehose/indexers/reaction.ts index be43d7b..259f7dc 100644 --- a/src/firehose/indexers/reaction.ts +++ b/src/firehose/indexers/reaction.ts @@ -5,6 +5,7 @@ import { replies } from '../../db/schema/replies.js' import type { Database } from '../../db/index.js' import type { Logger } from '../../lib/logger.js' import { clampCreatedAt } from '../clamp-timestamp.js' +import { getCollectionFromUri } from '../../lib/at-uri.js' const TOPIC_COLLECTION = 'forum.barazo.topic.post' const REPLY_COLLECTION = 'forum.barazo.topic.reply' @@ -28,14 +29,6 @@ interface DeleteParams { subjectUri: string } -function getCollectionFromUri(uri: string): string | undefined { - // AT URI format: at://did/collection/rkey - const parts = uri.split('/') - // parts: ["at:", "", "did", "collection", "rkey"] for at://did/collection/rkey - // But NSID collections have dots, so we need index 3 - return parts[3] -} - export class ReactionIndexer { constructor( private db: Database, diff --git a/src/lib/at-uri.ts b/src/lib/at-uri.ts index d588b98..b240939 100644 --- a/src/lib/at-uri.ts +++ b/src/lib/at-uri.ts @@ -12,3 +12,12 @@ export function extractRkey(uri: string): string { } return rkey } + +/** + * Extract the collection NSID from an AT URI. + * Format: at://did/collection/rkey -> returns "collection" + */ +export function getCollectionFromUri(uri: string): string | undefined { + const parts = uri.split('/') + return parts[3] +} diff --git a/src/routes/admin-settings.ts b/src/routes/admin-settings.ts index 6359918..91f4f5e 100644 --- a/src/routes/admin-settings.ts +++ b/src/routes/admin-settings.ts @@ -363,7 +363,7 @@ export function adminSettingsRoutes(): FastifyPluginCallback { throw notFound('Community settings not found after update') } - // TODO: Write to admin_audit_log table when implemented (standards/backend.md audit logging) + // TODO: Write to admin_audit_log table when implemented (standards/backend.md audit logging) (#38) app.log.info( { event: 'settings_updated', @@ -420,6 +420,7 @@ export function adminSettingsRoutes(): FastifyPluginCallback { recent_users: string } + // Drizzle execute() returns untyped rows — cast to expected shape const rows = result as unknown as StatsRow[] const row = rows[0] if (!row) { diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 4b9e0a6..7aa36b0 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -134,7 +134,9 @@ export function authRoutes(oauthClient: NodeOAuthClient): FastifyPluginCallback const session = await sessionService.createSession(did, handle) // Fire-and-forget profile sync from PDS (never blocks auth flow) - void app.profileSync.syncProfile(did) + void app.profileSync.syncProfile(did).catch((err: unknown) => { + app.log.error({ err, did }, 'Profile sync failed') + }) // Detect cross-post scope grant and persist to user preferences. // The tokenSet scope field reflects what the PDS actually granted. diff --git a/src/routes/global-filters.ts b/src/routes/global-filters.ts index e06e6a9..b7ad8d8 100644 --- a/src/routes/global-filters.ts +++ b/src/routes/global-filters.ts @@ -515,6 +515,7 @@ export function globalFilterRoutes(): FastifyPluginCallback { GROUP BY r.community_did, t.topic_count ORDER BY report_count DESC LIMIT ${limit} + // Drizzle execute() returns untyped rows — cast to expected shape `)) as unknown as CommunityReportRow[] return reply.status(200).send({ diff --git a/src/routes/health.ts b/src/routes/health.ts index 7f1ad3b..a035bc7 100644 --- a/src/routes/health.ts +++ b/src/routes/health.ts @@ -1,7 +1,7 @@ import type { FastifyPluginCallback } from 'fastify' import { sql } from 'drizzle-orm' -const healthRoutes: FastifyPluginCallback = (fastify, _opts, done) => { +export const healthRoutes: FastifyPluginCallback = (fastify, _opts, done) => { fastify.get('/api/health', async (_request, reply) => { return reply.send({ status: 'healthy', @@ -54,5 +54,3 @@ const healthRoutes: FastifyPluginCallback = (fastify, _opts, done) => { done() } - -export default healthRoutes diff --git a/src/routes/reactions.ts b/src/routes/reactions.ts index ea5f4b0..274bdad 100644 --- a/src/routes/reactions.ts +++ b/src/routes/reactions.ts @@ -17,7 +17,7 @@ import { replies } from '../db/schema/replies.js' import { communitySettings } from '../db/schema/community-settings.js' import { checkOnboardingComplete } from '../lib/onboarding-gate.js' import { createNotificationService } from '../services/notification.js' -import { extractRkey } from '../lib/at-uri.js' +import { extractRkey, getCollectionFromUri } from '../lib/at-uri.js' // --------------------------------------------------------------------------- // Constants @@ -89,15 +89,6 @@ function decodeCursor(cursor: string): { createdAt: string; uri: string } | null } } -/** - * Get the collection NSID from an AT URI. - * Format: at://did/collection/rkey -> returns "collection" - */ -function getCollectionFromUri(uri: string): string | undefined { - const parts = uri.split('/') - return parts[3] -} - // --------------------------------------------------------------------------- // Reaction routes plugin // --------------------------------------------------------------------------- diff --git a/src/routes/search.ts b/src/routes/search.ts index 0e752b0..a5153ab 100644 --- a/src/routes/search.ts +++ b/src/routes/search.ts @@ -562,6 +562,7 @@ async function searchTopicsFulltext( LIMIT ${fetchLimit} `) + // Drizzle execute() returns untyped rows — cast to expected shape return result as unknown as TopicSearchRow[] } @@ -613,6 +614,7 @@ async function searchRepliesFulltext( LIMIT ${fetchLimit} `) + // Drizzle execute() returns untyped rows — cast to expected shape return result as unknown as ReplySearchRow[] } @@ -664,6 +666,7 @@ async function searchTopicsVector( LIMIT ${fetchLimit} `) + // Drizzle execute() returns untyped rows — cast to expected shape return result as unknown as TopicSearchRow[] } @@ -713,6 +716,7 @@ async function searchRepliesVector( LIMIT ${fetchLimit} `) + // Drizzle execute() returns untyped rows — cast to expected shape return result as unknown as ReplySearchRow[] } @@ -764,6 +768,7 @@ async function countSearchResults( WHERE ${whereClause} `) + // Drizzle execute() returns untyped rows — cast to expected shape const rows = result as unknown as CountRow[] total += Number(rows[0]?.count ?? 0) } @@ -795,6 +800,7 @@ async function countSearchResults( WHERE ${whereClause} `) + // Drizzle execute() returns untyped rows — cast to expected shape const rows = result as unknown as CountRow[] total += Number(rows[0]?.count ?? 0) } diff --git a/src/services/ban-propagation.ts b/src/services/ban-propagation.ts index 4f42e30..4aa4450 100644 --- a/src/services/ban-propagation.ts +++ b/src/services/ban-propagation.ts @@ -40,6 +40,7 @@ export async function checkBanPropagation( SELECT count(*)::int AS ban_count FROM latest_actions WHERE action = 'ban' + // Drizzle execute() returns untyped rows — cast to expected shape `)) as unknown as BanCountRow[] const banCount = result[0]?.ban_count ?? 0 diff --git a/src/validation/block-mute.ts b/src/validation/block-mute.ts index 46b18e6..2047342 100644 --- a/src/validation/block-mute.ts +++ b/src/validation/block-mute.ts @@ -4,6 +4,7 @@ import { z } from 'zod/v4' // Param schemas for block/mute action endpoints // --------------------------------------------------------------------------- +// Mirrors didRegex from barazo-lexicons/src/validation/patterns.ts const didRegex = /^did:[a-z]+:[a-zA-Z0-9._:%-]+$/ /** Schema for validating :did route parameter. */ -- 2.51.2