add safeRead

This commit is contained in:
miloschwartz
2026-02-21 16:38:51 -08:00
parent f07f0092ad
commit 58ac499f30
6 changed files with 65 additions and 15 deletions

View File

@@ -3,7 +3,14 @@ import {
encodeHexLowerCase
} from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { resourceSessions, Session, sessions, User, users } from "@server/db";
import {
resourceSessions,
safeRead,
Session,
sessions,
User,
users
} from "@server/db";
import { db } from "@server/db";
import { eq, inArray } from "drizzle-orm";
import config from "@server/lib/config";
@@ -54,11 +61,15 @@ export async function validateSessionToken(
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token))
);
const result = await db
.select({ user: users, session: sessions })
.from(sessions)
.innerJoin(users, eq(sessions.userId, users.userId))
.where(eq(sessions.sessionId, sessionId));
const result = await safeRead((db) =>
db
.select({ user: users, session: sessions })
.from(sessions)
.innerJoin(users, eq(sessions.userId, users.userId))
.where(eq(sessions.sessionId, sessionId))
);
if (result.length < 1) {
return { session: null, user: null };
}

View File

@@ -1,7 +1,7 @@
import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { resourceSessions, ResourceSession } from "@server/db";
import { db } from "@server/db";
import { db, safeRead } from "@server/db";
import { eq, and } from "drizzle-orm";
import config from "@server/lib/config";
@@ -66,15 +66,17 @@ export async function validateResourceSessionToken(
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token))
);
const result = await db
.select()
.from(resourceSessions)
.where(
and(
eq(resourceSessions.sessionId, sessionId),
eq(resourceSessions.resourceId, resourceId)
const result = await safeRead((db) =>
db
.select()
.from(resourceSessions)
.where(
and(
eq(resourceSessions.sessionId, sessionId),
eq(resourceSessions.resourceId, resourceId)
)
)
);
);
if (result.length < 1) {
return { resourceSession: null };

View File

@@ -1,4 +1,5 @@
export * from "./driver";
export * from "./safeRead";
export * from "./schema/schema";
export * from "./schema/privateSchema";
export * from "./migrate";

24
server/db/pg/safeRead.ts Normal file
View File

@@ -0,0 +1,24 @@
import { db, primaryDb } from "./driver";
/**
* Runs a read query with replica fallback for Postgres.
* Executes the query against the replica first (when replicas exist).
* If the query throws or returns no data (null, undefined, or empty array),
* runs the same query against the primary.
*/
export async function safeRead<T>(
query: (d: typeof db | typeof primaryDb) => Promise<T>
): Promise<T> {
try {
const result = await query(db);
if (result === undefined || result === null) {
return query(primaryDb);
}
if (Array.isArray(result) && result.length === 0) {
return query(primaryDb);
}
return result;
} catch {
return query(primaryDb);
}
}

View File

@@ -1,4 +1,5 @@
export * from "./driver";
export * from "./safeRead";
export * from "./schema/schema";
export * from "./schema/privateSchema";
export * from "./migrate";

View File

@@ -0,0 +1,11 @@
import { db } from "./driver";
/**
* Runs a read query. For SQLite there is no replica/primary distinction,
* so the query is executed once against the database.
*/
export async function safeRead<T>(
query: (d: typeof db) => Promise<T>
): Promise<T> {
return query(db);
}