From d1159d3af9a8d7d8896b3a1f87f50de61de3b761 Mon Sep 17 00:00:00 2001 From: argusagent Date: Tue, 17 Mar 2026 20:22:05 -0400 Subject: [PATCH 1/4] fix(share): guard against uninitialized DB connection on /share routes (#5677) --- apps/server/src/share/sql.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/server/src/share/sql.ts b/apps/server/src/share/sql.ts index 36cca4b5ec..5a127e791f 100644 --- a/apps/server/src/share/sql.ts +++ b/apps/server/src/share/sql.ts @@ -5,12 +5,14 @@ import dataDir from "../services/data_dir.js"; import sql_init from "../services/sql_init.js"; let dbConnection!: Database.Database; +let dbConnectionReady = false; sql_init.dbReady.then(() => { dbConnection = new Database(dataDir.DOCUMENT_PATH, { readonly: true, nativeBinding: process.env.BETTERSQLITE3_NATIVE_PATH || undefined }); + dbConnectionReady = true; [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `SIGTERM`].forEach((eventType) => { process.on(eventType, () => { @@ -23,18 +25,31 @@ sql_init.dbReady.then(() => { }); }); +function assertDbReady(): void { + if (!dbConnectionReady) { + throw new Error("Share database connection is not yet ready. The application may still be initializing."); + } +} + function getRawRows(query: string, params = []): T[] { + assertDbReady(); return dbConnection.prepare(query).raw().all(params) as T[]; } function getRow(query: string, params: string[] = []): T { + assertDbReady(); return dbConnection.prepare(query).get(params) as T; } function getColumn(query: string, params: string[] = []): T[] { + assertDbReady(); return dbConnection.prepare(query).pluck().all(params) as T[]; } +export function isShareDbReady(): boolean { + return dbConnectionReady; +} + export default { getRawRows, getRow, From 1990a990c337ef0cf90287701c4fae81290d7be9 Mon Sep 17 00:00:00 2001 From: argusagent Date: Tue, 17 Mar 2026 20:22:06 -0400 Subject: [PATCH 2/4] fix(share): return 503 when app is still initializing (#5677) --- apps/server/src/share/routes.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/server/src/share/routes.ts b/apps/server/src/share/routes.ts index 80544fd998..6c41d368de 100644 --- a/apps/server/src/share/routes.ts +++ b/apps/server/src/share/routes.ts @@ -10,7 +10,15 @@ import type SNote from "./shaca/entities/snote.js"; import type SAttachment from "./shaca/entities/sattachment.js"; import { getDefaultTemplatePath, renderNoteContent } from "./content_renderer.js"; import utils from "../services/utils.js"; +import { isShareDbReady } from "./sql.js"; +function assertShareDbReady(res: Response): boolean { + if (!isShareDbReady()) { + res.status(503).send("The application is still initializing. Please try again in a moment."); + return false; + } + return true; +} function addNoIndexHeader(note: SNote, res: Response) { if (note.isLabelTruthy("shareDisallowRobotIndexing")) { res.setHeader("X-Robots-Tag", "noindex"); From e9987b40e6b867683452516842d42530abcf5b85 Mon Sep 17 00:00:00 2001 From: argusagent Date: Tue, 17 Mar 2026 20:27:04 -0400 Subject: [PATCH 3/4] =?UTF-8?q?fix(share):=20wire=20assertShareDbReady=20i?= =?UTF-8?q?nto=20router=20middleware=20=E2=80=94=20address=20code=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/server/src/share/routes.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/server/src/share/routes.ts b/apps/server/src/share/routes.ts index 6c41d368de..6494f099a7 100644 --- a/apps/server/src/share/routes.ts +++ b/apps/server/src/share/routes.ts @@ -123,6 +123,13 @@ function render404(res: Response) { } function register(router: Router) { + // Guard: if the share DB is not yet initialized, return 503 for all /share routes. + router.use((_req: Request, res: Response, next) => { + if (!assertShareDbReady(res)) { + return; + } + next(); + }); function renderNote(note: SNote, req: Request, res: Response) { if (!note) { From b29ab93fd522407d090025bf24c5e4abce02a29c Mon Sep 17 00:00:00 2001 From: JYC333 <22962980+JYC333@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:46:35 +0000 Subject: [PATCH 4/4] fix: limit the scope of DB check only for share page --- apps/server/src/share/routes.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/apps/server/src/share/routes.ts b/apps/server/src/share/routes.ts index 6494f099a7..69489c5522 100644 --- a/apps/server/src/share/routes.ts +++ b/apps/server/src/share/routes.ts @@ -1,6 +1,6 @@ import safeCompare from "safe-compare"; -import type { Request, Response, Router } from "express"; +import type { NextFunction, Request, Response, Router } from "express"; import shaca from "./shaca/shaca.js"; import shacaLoader from "./shaca/shaca_loader.js"; @@ -12,13 +12,15 @@ import { getDefaultTemplatePath, renderNoteContent } from "./content_renderer.js import utils from "../services/utils.js"; import { isShareDbReady } from "./sql.js"; -function assertShareDbReady(res: Response): boolean { +function assertShareDbReady(_req: Request, res: Response, next: NextFunction) { if (!isShareDbReady()) { res.status(503).send("The application is still initializing. Please try again in a moment."); - return false; + return; } - return true; + + next(); } + function addNoIndexHeader(note: SNote, res: Response) { if (note.isLabelTruthy("shareDisallowRobotIndexing")) { res.setHeader("X-Robots-Tag", "noindex"); @@ -124,12 +126,7 @@ function render404(res: Response) { function register(router: Router) { // Guard: if the share DB is not yet initialized, return 503 for all /share routes. - router.use((_req: Request, res: Response, next) => { - if (!assertShareDbReady(res)) { - return; - } - next(); - }); + router.use("/share", assertShareDbReady); function renderNote(note: SNote, req: Request, res: Response) { if (!note) {