style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
+51
-51
@@ -1,46 +1,44 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { realpathSync } from 'node:fs'
|
||||
import { serve } from '@hono/node-server'
|
||||
import { serveStatic } from '@hono/node-server/serve-static'
|
||||
import { Hono } from 'hono'
|
||||
import { healthRouter } from './routes/health.js'
|
||||
import { meRouter } from './routes/me.js'
|
||||
import { eventsRouter } from './routes/events.js'
|
||||
import { sseRouter } from './routes/sse.js'
|
||||
import { listsRouter, listItemsRouter } from './routes/lists.js'
|
||||
import { pushRouter } from './routes/push.js'
|
||||
import { oidcAuthMiddleware, processOAuthCallback } from './auth/middleware.js'
|
||||
import { devAuthBypass } from './auth/devBypass.js'
|
||||
import { persistSessionCookie } from './auth/persistSessionCookie.js'
|
||||
import { startBrokerPoller } from './broker/poller.js'
|
||||
import { startOutboxWorker } from './broker/outboxWorker.js'
|
||||
import { startReminderScheduler } from './broker/reminderScheduler.js'
|
||||
import webpush from 'web-push'
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { realpathSync } from 'node:fs';
|
||||
import { serve } from '@hono/node-server';
|
||||
import { serveStatic } from '@hono/node-server/serve-static';
|
||||
import { Hono } from 'hono';
|
||||
import { healthRouter } from './routes/health.js';
|
||||
import { meRouter } from './routes/me.js';
|
||||
import { eventsRouter } from './routes/events.js';
|
||||
import { sseRouter } from './routes/sse.js';
|
||||
import { listsRouter, listItemsRouter } from './routes/lists.js';
|
||||
import { pushRouter } from './routes/push.js';
|
||||
import { oidcAuthMiddleware, processOAuthCallback } from './auth/middleware.js';
|
||||
import { devAuthBypass } from './auth/devBypass.js';
|
||||
import { persistSessionCookie } from './auth/persistSessionCookie.js';
|
||||
import { startBrokerPoller } from './broker/poller.js';
|
||||
import { startOutboxWorker } from './broker/outboxWorker.js';
|
||||
import { startReminderScheduler } from './broker/reminderScheduler.js';
|
||||
import webpush from 'web-push';
|
||||
|
||||
export const app = new Hono()
|
||||
export const app = new Hono();
|
||||
|
||||
// Compute once at startup: bypass is active only in non-production with explicit opt-in.
|
||||
// In production NODE_ENV='production' → devBypassActive=false → OIDC is always mounted.
|
||||
const devBypassActive =
|
||||
process.env.NODE_ENV !== 'production' && process.env.DEV_AUTH_BYPASS === 'true'
|
||||
process.env.NODE_ENV !== 'production' && process.env.DEV_AUTH_BYPASS === 'true';
|
||||
|
||||
if (devBypassActive) {
|
||||
console.warn(
|
||||
'⚠ DEV_AUTH_BYPASS active — OIDC guard DISABLED. Never use in production.',
|
||||
)
|
||||
console.warn('⚠ DEV_AUTH_BYPASS active — OIDC guard DISABLED. Never use in production.');
|
||||
}
|
||||
|
||||
// OIDC callback — must be registered BEFORE oidcAuthMiddleware so the
|
||||
// authorization-code exchange is not itself intercepted by the auth check (T-02-02)
|
||||
app.get('/callback', (c) => processOAuthCallback(c))
|
||||
app.get('/callback', (c) => processOAuthCallback(c));
|
||||
|
||||
// GET /health — unauthenticated, mounted BEFORE the OIDC guard (T-01-03, T-02-05)
|
||||
app.route('/health', healthRouter)
|
||||
app.route('/health', healthRouter);
|
||||
|
||||
// Dev-auth bypass — no-op passthrough unless DEV_AUTH_BYPASS=true AND NODE_ENV!='production'.
|
||||
// When active, injects DEV_USER into the Hono context and the OIDC guard is NOT mounted.
|
||||
// Must be mounted BEFORE oidcAuthMiddleware (T-02-01 mitigation; see auth/devBypass.ts).
|
||||
app.use('/api/*', devAuthBypass())
|
||||
app.use('/api/*', devAuthBypass());
|
||||
|
||||
// Protect all /api/* routes with OIDC session middleware (AUTH-01, T-02-05).
|
||||
// Skipped entirely when devBypassActive so that local dev works without Authelia.
|
||||
@@ -49,9 +47,9 @@ app.use('/api/*', devAuthBypass())
|
||||
// OIDC_AUTH_EXTERNAL_URL is MANDATORY behind Pangolin to construct the correct
|
||||
// redirect_uri (Pitfall 1). Set it to https://familysync.<domain>.
|
||||
if (!devBypassActive) {
|
||||
app.use('/api/*', oidcAuthMiddleware())
|
||||
app.use('/api/*', oidcAuthMiddleware());
|
||||
// Re-issues the session-scoped oidc-auth cookie as persistent so PWA sessions survive close (AUTH-02).
|
||||
app.use('/api/*', persistSessionCookie())
|
||||
app.use('/api/*', persistSessionCookie());
|
||||
}
|
||||
|
||||
// Protected API routes (behind oidcAuthMiddleware)
|
||||
@@ -63,14 +61,14 @@ if (!devBypassActive) {
|
||||
// browser follows it here — now authenticated. The handler then redirects to /
|
||||
// so the SPA boots with a valid session. Under DEV_AUTH_BYPASS the guard is not
|
||||
// mounted, so /api/login reaches this handler directly and still redirects to /.
|
||||
app.get('/api/login', (c) => c.redirect('/'))
|
||||
app.get('/api/login', (c) => c.redirect('/'));
|
||||
|
||||
app.route('/api/me', meRouter)
|
||||
app.route('/api/events', eventsRouter)
|
||||
app.route('/api/sse', sseRouter)
|
||||
app.route('/api/lists', listsRouter)
|
||||
app.route('/api/list-items', listItemsRouter)
|
||||
app.route('/api/push', pushRouter)
|
||||
app.route('/api/me', meRouter);
|
||||
app.route('/api/events', eventsRouter);
|
||||
app.route('/api/sse', sseRouter);
|
||||
app.route('/api/lists', listsRouter);
|
||||
app.route('/api/list-items', listItemsRouter);
|
||||
app.route('/api/push', pushRouter);
|
||||
|
||||
// WR-04: background worker startup (cron schedules) moved into the isMainModule()
|
||||
// guard below. Calling them at top level registered real node-cron schedules whenever
|
||||
@@ -85,8 +83,8 @@ app.route('/api/push', pushRouter)
|
||||
// apple-touch-icon.png) live at the root. serveStatic calls next() when a file
|
||||
// is not found, so SPA routes fall through to the index.html catch-all below.
|
||||
// (Registered AFTER /health, /api/*, and /callback, so those win.)
|
||||
app.use('/*', serveStatic({ root: './public' }))
|
||||
app.get('*', serveStatic({ path: './public/index.html' }))
|
||||
app.use('/*', serveStatic({ root: './public' }));
|
||||
app.get('*', serveStatic({ path: './public/index.html' }));
|
||||
|
||||
/**
|
||||
* True only when this module is the process entrypoint (run directly), not when it
|
||||
@@ -100,11 +98,11 @@ app.get('*', serveStatic({ path: './public/index.html' }))
|
||||
* resolves symlinks on argv[1]; fileURLToPath turns the module URL into a real path.
|
||||
*/
|
||||
function isMainModule(): boolean {
|
||||
if (!process.argv[1]) return false
|
||||
if (!process.argv[1]) return false;
|
||||
try {
|
||||
return fileURLToPath(import.meta.url) === realpathSync(process.argv[1])
|
||||
return fileURLToPath(import.meta.url) === realpathSync(process.argv[1]);
|
||||
} catch {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,32 +113,34 @@ if (isMainModule()) {
|
||||
// Configure VAPID credentials for web-push before starting background workers.
|
||||
// VAPID_SUBJECT must be a mailto: or https: URL identifying the operator.
|
||||
// The private key is NEVER served to clients; it signs push requests server-side only.
|
||||
const vapidSubject = process.env.VAPID_SUBJECT ?? ''
|
||||
const vapidPublicKey = process.env.VAPID_PUBLIC_KEY ?? ''
|
||||
const vapidPrivateKey = process.env.VAPID_PRIVATE_KEY ?? ''
|
||||
const vapidSubject = process.env.VAPID_SUBJECT ?? '';
|
||||
const vapidPublicKey = process.env.VAPID_PUBLIC_KEY ?? '';
|
||||
const vapidPrivateKey = process.env.VAPID_PRIVATE_KEY ?? '';
|
||||
if (vapidSubject && vapidPublicKey && vapidPrivateKey) {
|
||||
try {
|
||||
webpush.setVapidDetails(vapidSubject, vapidPublicKey, vapidPrivateKey)
|
||||
webpush.setVapidDetails(vapidSubject, vapidPublicKey, vapidPrivateKey);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'[startup] setVapidDetails failed — push notifications will not work:',
|
||||
err instanceof Error ? err.message : String(err),
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.warn('[startup] VAPID env vars not set — push notifications will fail. Set VAPID_SUBJECT, VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY.')
|
||||
console.warn(
|
||||
'[startup] VAPID env vars not set — push notifications will fail. Set VAPID_SUBJECT, VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY.',
|
||||
);
|
||||
}
|
||||
|
||||
// Start the CalDAV broker poller (5-min cron, D-13 ctag change-detection).
|
||||
// Runs in the background — errors are caught and logged per-credential (T-03-04).
|
||||
startBrokerPoller()
|
||||
startBrokerPoller();
|
||||
// Drain the D-05 outbox every 15s: dispatches pending CalDAV writes to Fastmail.
|
||||
startOutboxWorker()
|
||||
startOutboxWorker();
|
||||
// Start the 1-min reminder scan for shared timed events starting in ~15 min (NOTIF-01).
|
||||
// VAPID must be configured (above) before this starts or push sends will fail.
|
||||
startReminderScheduler()
|
||||
startReminderScheduler();
|
||||
|
||||
serve({ fetch: app.fetch, port: 3000 }, (info) => {
|
||||
console.log(`FamilySync API running on http://localhost:${info.port}`)
|
||||
})
|
||||
console.log(`FamilySync API running on http://localhost:${info.port}`);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user