From 80cec7130853c2d5d2eeae3b44f4de4f0e8a01ea Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Sat, 20 Sep 2025 23:04:16 +0200 Subject: [PATCH] Use sync access for the temp JSON file database Replace all async reads and writes to the JSON database with the sync reads and writes to prevent a data corruption race condition where two requests are processed at the same time and write to the same file, or one reads while the other writes causing read of partially written data. --- server/api/admin/database-export.post.ts | 12 +- server/api/admin/database-import-demo.post.ts | 8 +- server/api/admin/database-import.post.ts | 18 +-- server/api/admin/delete-database.post.ts | 2 +- server/api/admin/user.patch.ts | 12 +- server/api/auth/account.delete.ts | 16 +-- server/api/auth/account.patch.ts | 4 +- server/api/auth/account.post.ts | 14 +-- server/api/auth/ap/demo-login.post.ts | 4 +- server/api/auth/ap/telegram-login.post.ts | 4 +- server/api/auth/session.delete.ts | 2 +- server/api/last-event-id.ts | 2 +- server/api/schedule.patch.ts | 6 +- server/api/schedule.ts | 2 +- server/api/subscribe.post.ts | 4 +- server/api/unsubscribe.post.ts | 4 +- server/api/users/[id]/details.get.ts | 2 +- server/api/users/index.get.ts | 2 +- server/database.ts | 110 +++++++++--------- server/streams.ts | 10 +- server/utils/schedule.ts | 4 +- server/utils/session.ts | 30 ++--- server/web-push.ts | 4 +- 23 files changed, 138 insertions(+), 138 deletions(-) diff --git a/server/api/admin/database-export.post.ts b/server/api/admin/database-export.post.ts index 61fd34f..2aff8c3 100644 --- a/server/api/admin/database-export.post.ts +++ b/server/api/admin/database-export.post.ts @@ -9,11 +9,11 @@ export default defineEventHandler(async (event) => { setHeader(event, "Content-Disposition", 'attachment; filename="database-dump.json"'); setHeader(event, "Content-Type", "application/json; charset=utf-8"); return { - nextUserId: await readNextUserId(), - users: await readUsers(), - nextSessionId: await readNextSessionId(), - sessions: await readSessions(), - subscriptions: await readSubscriptions(), - schedule: await readSchedule(), + nextUserId: readNextUserId(), + users: readUsers(), + nextSessionId: readNextSessionId(), + sessions: readSessions(), + subscriptions: readSubscriptions(), + schedule: readSchedule(), }; }) diff --git a/server/api/admin/database-import-demo.post.ts b/server/api/admin/database-import-demo.post.ts index cab0eae..f002fb9 100644 --- a/server/api/admin/database-import-demo.post.ts +++ b/server/api/admin/database-import-demo.post.ts @@ -7,14 +7,14 @@ import { generateDemoSchedule, generateDemoAccounts } from "~/server/generate-de export default defineEventHandler(async (event) => { await requireServerSessionWithAdmin(event); const accounts = generateDemoAccounts(); - await writeUsers(accounts); - await writeSchedule(generateDemoSchedule()); - await writeAuthenticationMethods(accounts.map((user, index) => ({ + writeUsers(accounts); + writeSchedule(generateDemoSchedule()); + writeAuthenticationMethods(accounts.map((user, index) => ({ id: index, userId: user.id, provider: "demo", slug: user.name!, name: user.name!, }))); - await writeNextAuthenticationMethodId(Math.max(await nextAuthenticationMethodId(), accounts.length)); + writeNextAuthenticationMethodId(Math.max(nextAuthenticationMethodId(), accounts.length)); }) diff --git a/server/api/admin/database-import.post.ts b/server/api/admin/database-import.post.ts index f4e6019..aad3560 100644 --- a/server/api/admin/database-import.post.ts +++ b/server/api/admin/database-import.post.ts @@ -29,20 +29,20 @@ export default defineEventHandler(async (event) => { }); } - const currentNextUserId = await readNextUserId(); - await writeNextUserId(Math.max(currentNextUserId, snapshot.nextUserId)); - await writeUsers(snapshot.users); - const currentNextSessionId = await readNextSessionId(); - await writeNextSessionId(Math.max(currentNextSessionId, snapshot.nextSessionId)); - const currentSessions = new Map((await readSessions()).map(session => [session.id, session])); - await writeSessions(snapshot.sessions.filter(session => { + const currentNextUserId = readNextUserId(); + writeNextUserId(Math.max(currentNextUserId, snapshot.nextUserId)); + writeUsers(snapshot.users); + const currentNextSessionId = readNextSessionId(); + writeNextSessionId(Math.max(currentNextSessionId, snapshot.nextSessionId)); + const currentSessions = new Map((readSessions()).map(session => [session.id, session])); + writeSessions(snapshot.sessions.filter(session => { const current = currentSessions.get(session.id); // Only keep sessions that match the account id in both sets to avoid // resurrecting deleted sessions. This will still cause session cross // pollution if a snapshot from another instance is loaded here. return current?.accountId !== undefined && current.accountId === session.accountId; })); - await writeSubscriptions(snapshot.subscriptions); - await writeSchedule(snapshot.schedule); + writeSubscriptions(snapshot.subscriptions); + writeSchedule(snapshot.schedule); await sendRedirect(event, "/"); }) diff --git a/server/api/admin/delete-database.post.ts b/server/api/admin/delete-database.post.ts index 395f607..fb2c836 100644 --- a/server/api/admin/delete-database.post.ts +++ b/server/api/admin/delete-database.post.ts @@ -6,5 +6,5 @@ import { deleteDatabase } from "~/server/database"; export default defineEventHandler(async (event) => { await requireServerSessionWithAdmin(event); - await deleteDatabase(); + deleteDatabase(); }) diff --git a/server/api/admin/user.patch.ts b/server/api/admin/user.patch.ts index 6f1c9a7..548f924 100644 --- a/server/api/admin/user.patch.ts +++ b/server/api/admin/user.patch.ts @@ -18,7 +18,7 @@ export default defineEventHandler(async (event) => { }); } - const users = await readUsers(); + const users = readUsers(); const user = users.find(user => user.id === patch.id); if (!user || user.deleted) { throw createError({ @@ -52,28 +52,28 @@ export default defineEventHandler(async (event) => { user.name = patch.name; } user.updatedAt = new Date().toISOString(); - await writeUsers(users); + writeUsers(users); broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "user-update", data: serverUserToApi(user), }); // Rotate sessions with the user in it if the access changed if (accessChanged) { - const sessions = await readSessions(); + const sessions = readSessions(); const nowMs = Date.now(); for (const session of sessions) { if (session.accountId === user.id) { session.rotatesAtMs = nowMs; broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "session-expired", sessionId: session.id, }); } } - await writeSessions(sessions); + writeSessions(sessions); } // Update Schedule counts. diff --git a/server/api/auth/account.delete.ts b/server/api/auth/account.delete.ts index 37a8770..fb916ea 100644 --- a/server/api/auth/account.delete.ts +++ b/server/api/auth/account.delete.ts @@ -11,11 +11,11 @@ import { broadcastEvent, cancelAccountStreams } from "~/server/streams"; export default defineEventHandler(async (event) => { const serverSession = await requireServerSessionWithUser(event); - let users = await readUsers(); + let users = readUsers(); // Expire sessions for this user const expiredSessionIds = new Set(); - let sessions = await readSessions(); + let sessions = readSessions(); const nowMs = Date.now(); for (const session of sessions) { if ( @@ -25,7 +25,7 @@ export default defineEventHandler(async (event) => { ) { session.expiresAtMs = nowMs; broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "session-expired", sessionId: session.id, }); @@ -33,24 +33,24 @@ export default defineEventHandler(async (event) => { } } cancelAccountStreams(serverSession.accountId); - await writeSessions(sessions); + writeSessions(sessions); await deleteCookie(event, "session"); // Remove subscriptions for this user - let subscriptions = await readSubscriptions(); + let subscriptions = readSubscriptions(); subscriptions = subscriptions.filter( subscription => !expiredSessionIds.has(subscription.sessionId) ); - await writeSubscriptions(subscriptions); + writeSubscriptions(subscriptions); // Remove the user const account = users.find(user => user.id === serverSession.accountId)!; const now = new Date(nowMs).toISOString(); account.deleted = true; account.updatedAt = now; - await writeUsers(users); + writeUsers(users); await broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "user-update", data: { id: account.id, diff --git a/server/api/auth/account.patch.ts b/server/api/auth/account.patch.ts index 75c65ea..d468dcc 100644 --- a/server/api/auth/account.patch.ts +++ b/server/api/auth/account.patch.ts @@ -38,7 +38,7 @@ export default defineEventHandler(async (event) => { } } - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => user.id === session.accountId); if (!account) { throw Error("Account does not exist"); @@ -70,7 +70,7 @@ export default defineEventHandler(async (event) => { else delete account.locale; } - await writeUsers(users); + writeUsers(users); // Update Schedule counts. await updateScheduleInterestedCounts(users); diff --git a/server/api/auth/account.post.ts b/server/api/auth/account.post.ts index 52ef502..6be7cd5 100644 --- a/server/api/auth/account.post.ts +++ b/server/api/auth/account.post.ts @@ -18,7 +18,7 @@ export default defineEventHandler(async (event): Promise => { const body = await readBody(event); const name = body?.name; - const users = await readUsers(); + const users = readUsers(); let user: ServerUser; if (typeof name === "string") { if (name === "") { @@ -36,7 +36,7 @@ export default defineEventHandler(async (event): Promise => { const firstUser = users.every(user => user.type === "anonymous"); user = { - id: await nextUserId(), + id: nextUserId(), updatedAt: new Date().toISOString(), type: firstUser ? "admin" : "regular", name, @@ -44,7 +44,7 @@ export default defineEventHandler(async (event): Promise => { } else if (name === undefined) { user = { - id: await nextUserId(), + id: nextUserId(), updatedAt: new Date().toISOString(), type: "anonymous", }; @@ -76,19 +76,19 @@ export default defineEventHandler(async (event): Promise => { }); } authMethods.push({ - id: await nextAuthenticationMethodId(), + id: nextAuthenticationMethodId(), userId: user.id, provider: session.authenticationProvider, slug: session.authenticationSlug!, name: session.authenticationName!, }) - await writeAuthenticationMethods(authMethods); + writeAuthenticationMethods(authMethods); } users.push(user); - await writeUsers(users); + writeUsers(users); await broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "user-update", data: user, }); diff --git a/server/api/auth/ap/demo-login.post.ts b/server/api/auth/ap/demo-login.post.ts index ffd07de..193c619 100644 --- a/server/api/auth/ap/demo-login.post.ts +++ b/server/api/auth/ap/demo-login.post.ts @@ -24,11 +24,11 @@ export default defineEventHandler(async (event) => { }); } - const authMethods = await readAuthenticationMethods(); + const authMethods = readAuthenticationMethods(); const method = authMethods.find(method => method.provider === "demo" && method.slug === slug); let session; if (method) { - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => !user.deleted && user.id === method.userId); session = await setServerSession(event, account); } else { diff --git a/server/api/auth/ap/telegram-login.post.ts b/server/api/auth/ap/telegram-login.post.ts index 25a610b..062b2d7 100644 --- a/server/api/auth/ap/telegram-login.post.ts +++ b/server/api/auth/ap/telegram-login.post.ts @@ -84,11 +84,11 @@ export default defineEventHandler(async (event): Promise => { } const slug = String(data.authData.id); - const authMethods = await readAuthenticationMethods(); + const authMethods = readAuthenticationMethods(); const method = authMethods.find(method => method.provider === "telegram" && method.slug === slug); let session; if (method) { - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => !user.deleted && user.id === method.userId); session = await setServerSession(event, account); } else { diff --git a/server/api/auth/session.delete.ts b/server/api/auth/session.delete.ts index df4bfd9..038476b 100644 --- a/server/api/auth/session.delete.ts +++ b/server/api/auth/session.delete.ts @@ -8,7 +8,7 @@ import { cancelSessionStreams } from "~/server/streams"; export default defineEventHandler(async (event) => { const session = await getServerSession(event, true); if (session) { - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => user.id === session.accountId); if (account?.type === "anonymous") { throw createError({ diff --git a/server/api/last-event-id.ts b/server/api/last-event-id.ts index 6fd39ac..b5193c0 100644 --- a/server/api/last-event-id.ts +++ b/server/api/last-event-id.ts @@ -6,6 +6,6 @@ import { readEvents } from "../database"; export default defineEventHandler(async (event) => { - const events = await readEvents(); + const events = readEvents(); return events[events.length - 1]?. id ?? 0; }); diff --git a/server/api/schedule.patch.ts b/server/api/schedule.patch.ts index 517b6f0..544f2f3 100644 --- a/server/api/schedule.patch.ts +++ b/server/api/schedule.patch.ts @@ -35,7 +35,7 @@ export default defineEventHandler(async (event) => { }); } - const schedule = await readSchedule(); + const schedule = readSchedule(); if (schedule.deleted) { throw createError({ @@ -85,9 +85,9 @@ export default defineEventHandler(async (event) => { applyUpdatesToArray(update.shifts, schedule.shifts = schedule.shifts ?? []); } - await writeSchedule(schedule); + writeSchedule(schedule); await broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "schedule-update", updatedFrom, data: update, diff --git a/server/api/schedule.ts b/server/api/schedule.ts index a9ab848..14235cf 100644 --- a/server/api/schedule.ts +++ b/server/api/schedule.ts @@ -6,6 +6,6 @@ import { readSchedule } from "~/server/database"; export default defineEventHandler(async (event) => { const session = await getServerSession(event, false); - const schedule = await readSchedule(); + const schedule = readSchedule(); return canSeeCrew(session?.access) ? schedule : filterSchedule(schedule); }); diff --git a/server/api/subscribe.post.ts b/server/api/subscribe.post.ts index 9c4a76a..a12a6c4 100644 --- a/server/api/subscribe.post.ts +++ b/server/api/subscribe.post.ts @@ -20,7 +20,7 @@ export default defineEventHandler(async (event) => { message: z.prettifyError(error), }); } - const subscriptions = await readSubscriptions(); + const subscriptions = readSubscriptions(); const existingIndex = subscriptions.findIndex( sub => sub.type === "push" && sub.sessionId === session.id ); @@ -34,7 +34,7 @@ export default defineEventHandler(async (event) => { } else { subscriptions.push(subscription); } - await writeSubscriptions(subscriptions); + writeSubscriptions(subscriptions); if (existingIndex !== -1) { return { message: "Existing subscription refreshed."}; } diff --git a/server/api/unsubscribe.post.ts b/server/api/unsubscribe.post.ts index 61a3d51..9e47e87 100644 --- a/server/api/unsubscribe.post.ts +++ b/server/api/unsubscribe.post.ts @@ -6,7 +6,7 @@ import { readSubscriptions, writeSubscriptions } from "~/server/database"; export default defineEventHandler(async (event) => { const session = await requireServerSessionWithUser(event); - const subscriptions = await readSubscriptions(); + const subscriptions = readSubscriptions(); const existingIndex = subscriptions.findIndex( sub => sub.type === "push" && sub.sessionId === session.id ); @@ -15,6 +15,6 @@ export default defineEventHandler(async (event) => { } else { return { message: "No subscription registered."}; } - await writeSubscriptions(subscriptions); + writeSubscriptions(subscriptions); return { message: "Existing subscription removed."}; }); diff --git a/server/api/users/[id]/details.get.ts b/server/api/users/[id]/details.get.ts index 4c6e07d..efc4c74 100644 --- a/server/api/users/[id]/details.get.ts +++ b/server/api/users/[id]/details.get.ts @@ -13,7 +13,7 @@ const detailsSchema = z.object({ export default defineEventHandler(async (event) => { await requireServerSessionWithAdmin(event); - const users = await readUsers(); + const users = readUsers(); const { success, error, data: params } = detailsSchema.safeParse(getRouterParams(event)); if (!success) { throw createError({ diff --git a/server/api/users/index.get.ts b/server/api/users/index.get.ts index f6365fd..1a3bb86 100644 --- a/server/api/users/index.get.ts +++ b/server/api/users/index.get.ts @@ -6,7 +6,7 @@ import { readUsers } from "~/server/database" export default defineEventHandler(async (event) => { const session = await requireServerSessionWithUser(event); - const users = await readUsers(); + const users = readUsers(); if (session.access === "admin") { return users.map(serverUserToApi); diff --git a/server/database.ts b/server/database.ts index 7bba61b..b3899a8 100644 --- a/server/database.ts +++ b/server/database.ts @@ -2,7 +2,7 @@ SPDX-FileCopyrightText: © 2025 Hornwitser SPDX-License-Identifier: AGPL-3.0-or-later */ -import { readFile, unlink, writeFile } from "node:fs/promises"; +import { readFileSync, writeFileSync, unlinkSync } from "node:fs"; import type { ApiAuthenticationProvider, ApiEvent, ApiSchedule, ApiSubscription, ApiUserType } from "~/shared/types/api"; import type { Id } from "~/shared/types/common"; @@ -53,9 +53,9 @@ const nextAuthenticationMethodIdPath = "data/auth-method-id.json" const nextEventIdPath = "data/next-event-id.json"; const eventsPath = "data/events.json"; -async function remove(path: string) { +function remove(path: string) { try { - await unlink(path); + unlinkSync(path); } catch (err: any) { if (err.code !== "ENOENT") { throw err; @@ -63,17 +63,17 @@ async function remove(path: string) { } } -export async function deleteDatabase() { - await remove(schedulePath); - await remove(subscriptionsPath); - await remove(usersPath); - await remove(sessionsPath); +export function deleteDatabase() { + remove(schedulePath); + remove(subscriptionsPath); + remove(usersPath); + remove(sessionsPath); } -async function readJson(filePath: string, fallback: T) { +function readJson(filePath: string, fallback: T) { let data: T extends () => infer R ? R : T; try { - data = JSON.parse(await readFile(filePath, "utf-8")); + data = JSON.parse(readFileSync(filePath, "utf-8")); } catch (err: any) { if (err.code !== "ENOENT") throw err; @@ -82,19 +82,19 @@ async function readJson(filePath: string, fallback: T) { return data; } -export async function readSchedule() { +export function readSchedule() { return readJson(schedulePath, (): ApiSchedule => ({ id: 111, updatedAt: new Date().toISOString(), })); } -export async function writeSchedule(schedule: ApiSchedule) { - await writeFile(schedulePath, JSON.stringify(schedule, undefined, "\t") + "\n", "utf-8"); +export function writeSchedule(schedule: ApiSchedule) { + writeFileSync(schedulePath, JSON.stringify(schedule, undefined, "\t") + "\n", "utf-8"); } -export async function readSubscriptions() { - let subscriptions = await readJson(subscriptionsPath, []); +export function readSubscriptions() { + let subscriptions = readJson(subscriptionsPath, []); if (subscriptions.length && "keys" in subscriptions[0]) { // Discard old format subscriptions = []; @@ -102,89 +102,89 @@ export async function readSubscriptions() { return subscriptions; } -export async function writeSubscriptions(subscriptions: ApiSubscription[]) { - await writeFile(subscriptionsPath, JSON.stringify(subscriptions, undefined, "\t") + "\n", "utf-8"); +export function writeSubscriptions(subscriptions: ApiSubscription[]) { + writeFileSync(subscriptionsPath, JSON.stringify(subscriptions, undefined, "\t") + "\n", "utf-8"); } -export async function readNextUserId() { - return await readJson(nextUserIdPath, 0); +export function readNextUserId() { + return readJson(nextUserIdPath, 0); } -export async function writeNextUserId(nextId: number) { - await writeFile(nextUserIdPath, String(nextId), "utf-8"); +export function writeNextUserId(nextId: number) { + writeFileSync(nextUserIdPath, String(nextId), "utf-8"); } -export async function nextUserId() { - let nextId = await readJson(nextUserIdPath, 0); +export function nextUserId() { + let nextId = readJson(nextUserIdPath, 0); if (nextId === 0) { - nextId = Math.max(...(await readUsers()).map(user => user.id), -1) + 1; + nextId = Math.max(...(readUsers()).map(user => user.id), -1) + 1; } - await writeFile(nextUserIdPath, String(nextId + 1), "utf-8"); + writeFileSync(nextUserIdPath, String(nextId + 1), "utf-8"); return nextId; } -export async function readUsers() { - return await readJson(usersPath, (): ServerUser[] => []); +export function readUsers() { + return readJson(usersPath, (): ServerUser[] => []); } -export async function writeUsers(users: ServerUser[]) { - await writeFile(usersPath, JSON.stringify(users, undefined, "\t") + "\n", "utf-8"); +export function writeUsers(users: ServerUser[]) { + writeFileSync(usersPath, JSON.stringify(users, undefined, "\t") + "\n", "utf-8"); } -export async function readNextSessionId() { - return await readJson(nextSessionIdPath, 0); +export function readNextSessionId() { + return readJson(nextSessionIdPath, 0); } -export async function writeNextSessionId(nextId: number) { - await writeFile(nextSessionIdPath, String(nextId), "utf-8"); +export function writeNextSessionId(nextId: number) { + writeFileSync(nextSessionIdPath, String(nextId), "utf-8"); } -export async function nextSessionId() { - const nextId = await readJson(nextSessionIdPath, 0); - await writeFile(nextSessionIdPath, String(nextId + 1), "utf-8"); +export function nextSessionId() { + const nextId = readJson(nextSessionIdPath, 0); + writeFileSync(nextSessionIdPath, String(nextId + 1), "utf-8"); return nextId; } -export async function readSessions() { +export function readSessions() { return readJson(sessionsPath, []) } -export async function writeSessions(sessions: ServerSession[]) { - await writeFile(sessionsPath, JSON.stringify(sessions, undefined, "\t") + "\n", "utf-8"); +export function writeSessions(sessions: ServerSession[]) { + writeFileSync(sessionsPath, JSON.stringify(sessions, undefined, "\t") + "\n", "utf-8"); } -export async function nextAuthenticationMethodId() { - const nextId = await readJson(nextAuthenticationMethodIdPath, 0); - await writeFile(nextAuthenticationMethodIdPath, String(nextId + 1), "utf-8"); +export function nextAuthenticationMethodId() { + const nextId = readJson(nextAuthenticationMethodIdPath, 0); + writeFileSync(nextAuthenticationMethodIdPath, String(nextId + 1), "utf-8"); return nextId; } -export async function writeNextAuthenticationMethodId(nextId: number) { - await writeFile(nextAuthenticationMethodIdPath, String(nextId), "utf-8"); +export function writeNextAuthenticationMethodId(nextId: number) { + writeFileSync(nextAuthenticationMethodIdPath, String(nextId), "utf-8"); } -export async function readAuthenticationMethods() { +export function readAuthenticationMethods() { return readJson(authMethodPath, []) } -export async function writeAuthenticationMethods(authMethods: ServerAuthenticationMethod[]) { - await writeFile(authMethodPath, JSON.stringify(authMethods, undefined, "\t") + "\n", "utf-8"); +export function writeAuthenticationMethods(authMethods: ServerAuthenticationMethod[]) { + writeFileSync(authMethodPath, JSON.stringify(authMethods, undefined, "\t") + "\n", "utf-8"); } -export async function nextEventId() { - const nextId = await readJson(nextEventIdPath, 0); - await writeFile(nextEventIdPath, String(nextId + 1), "utf-8"); +export function nextEventId() { + const nextId = readJson(nextEventIdPath, 0); + writeFileSync(nextEventIdPath, String(nextId + 1), "utf-8"); return nextId; } -export async function writeNextEventId(nextId: number) { - await writeFile(nextEventIdPath, String(nextId), "utf-8"); +export function writeNextEventId(nextId: number) { + writeFileSync(nextEventIdPath, String(nextId), "utf-8"); } -export async function readEvents() { +export function readEvents() { return readJson(eventsPath, []) } -export async function writeEvents(events: ApiEvent[]) { - await writeFile(eventsPath, JSON.stringify(events, undefined, "\t") + "\n", "utf-8"); +export function writeEvents(events: ApiEvent[]) { + writeFileSync(eventsPath, JSON.stringify(events, undefined, "\t") + "\n", "utf-8"); } diff --git a/server/streams.ts b/server/streams.ts index bc2dfa0..aca024a 100644 --- a/server/streams.ts +++ b/server/streams.ts @@ -33,8 +33,8 @@ export async function createEventStream( ) { const runtimeConfig = useRuntimeConfig(event); const now = Date.now(); - const events = (await readEvents()).filter(e => e.id > lastEventId); - const users = await readUsers(); + const events = (readEvents()).filter(e => e.id > lastEventId); + const users = readUsers(); const apiSession = session ? await serverSessionToApi(event, session) : undefined; let userType: ApiAccount["type"] | undefined; if (session?.accountId !== undefined) { @@ -182,9 +182,9 @@ function encodeEvent(event: ApiEvent, userType: ApiAccount["type"] | undefined) } export async function broadcastEvent(event: ApiEvent) { - const events = await readEvents(); + const events = readEvents(); events.push(event); - await writeEvents(events); + writeEvents(events); } function sendEventToStream(stream: EventStream, event: ApiEvent) { @@ -222,7 +222,7 @@ async function sendEventUpdates() { // Send events. const skipEventId = Math.min(...[...streams.values()].map(s => s.lastEventId)); - const events = (await readEvents()).filter(e => e.id > skipEventId); + const events = (readEvents()).filter(e => e.id > skipEventId); if (events.length) console.log(`broadcasting ${events.length} event(s) to ${streams.size} client(s)`); for (const stream of streams.values()) { diff --git a/server/utils/schedule.ts b/server/utils/schedule.ts index c42960a..4e263a7 100644 --- a/server/utils/schedule.ts +++ b/server/utils/schedule.ts @@ -20,7 +20,7 @@ export async function updateScheduleInterestedCounts(users: ServerUser[]) { eventSlotCounts.set(id, (eventSlotCounts.get(id) ?? 0) + 1); } - const schedule = await readSchedule(); + const schedule = readSchedule(); if (schedule.deleted) { throw new Error("Deleted schedule not implemented"); } @@ -58,7 +58,7 @@ export async function updateScheduleInterestedCounts(users: ServerUser[]) { schedule.updatedAt = updatedFrom; await writeSchedule(schedule); await broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "schedule-update", updatedFrom, data: update, diff --git a/server/utils/session.ts b/server/utils/session.ts index c605406..d56bc10 100644 --- a/server/utils/session.ts +++ b/server/utils/session.ts @@ -19,11 +19,11 @@ import type { ApiAuthenticationProvider, ApiSession } from "~/shared/types/api"; import { serverUserToApiAccount } from "./user"; async function removeSessionSubscription(sessionId: number) { - const subscriptions = await readSubscriptions(); + const subscriptions = readSubscriptions(); const index = subscriptions.findIndex(subscription => subscription.sessionId === sessionId); if (index !== -1) { subscriptions.splice(index, 1); - await writeSubscriptions(subscriptions); + writeSubscriptions(subscriptions); } } @@ -35,7 +35,7 @@ async function clearServerSessionInternal(event: H3Event, sessions: ServerSessio if (session) { session.expiresAtMs = Date.now(); broadcastEvent({ - id: await nextEventId(), + id: nextEventId(), type: "session-expired", sessionId, }); @@ -47,9 +47,9 @@ async function clearServerSessionInternal(event: H3Event, sessions: ServerSessio } export async function clearServerSession(event: H3Event) { - const sessions = await readSessions(); + const sessions = readSessions(); if (await clearServerSessionInternal(event, sessions)) { - await writeSessions(sessions); + writeSessions(sessions); } deleteCookie(event, "session"); } @@ -61,7 +61,7 @@ export async function setServerSession( authenticationSlug?: string, authenticationName?: string, ) { - const sessions = await readSessions(); + const sessions = readSessions(); const runtimeConfig = useRuntimeConfig(event); await clearServerSessionInternal(event, sessions); @@ -78,14 +78,14 @@ export async function setServerSession( }; sessions.push(newSession); - await writeSessions(sessions); + writeSessions(sessions); await setSignedCookie(event, "session", String(newSession.id), runtimeConfig.sessionDiscardTimeout) return newSession; } async function rotateSession(event: H3Event, sessions: ServerSession[], session: ServerSession) { const runtimeConfig = useRuntimeConfig(event); - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => !user.deleted && user.id === session.accountId); const now = Date.now(); const newSession: ServerSession = { @@ -94,12 +94,12 @@ async function rotateSession(event: H3Event, sessions: ServerSession[], session: // Authentication provider is removed to avoid possibility of an infinite delay before using it. rotatesAtMs: now + runtimeConfig.sessionRotatesTimeout * 1000, discardAtMs: now + runtimeConfig.sessionDiscardTimeout * 1000, - id: await nextSessionId(), + id: nextSessionId(), }; session.successor = newSession.id; session.expiresAtMs = Date.now() + 10 * 1000; sessions.push(newSession); - await writeSessions(sessions); + writeSessions(sessions); await setSignedCookie(event, "session", String(newSession.id), runtimeConfig.sessionDiscardTimeout) return newSession; } @@ -108,7 +108,7 @@ export async function getServerSession(event: H3Event, ignoreTaken: boolean) { const sessionCookie = await getSignedCookie(event, "session"); if (sessionCookie) { const sessionId = parseInt(sessionCookie, 10); - const sessions = await readSessions(); + const sessions = readSessions(); const session = sessions.find(session => session.id === sessionId); if (session) { const nowMs = Date.now(); @@ -148,7 +148,7 @@ export async function requireServerSession(event: H3Event, message: string) { export async function requireServerSessionWithUser(event: H3Event) { const message = "User session required"; const session = await requireServerSession(event, message); - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => user.id === session.accountId); if (session.accountId === undefined || !account || account.deleted) throw createError({ @@ -163,7 +163,7 @@ export async function requireServerSessionWithUser(event: H3Event) { export async function requireServerSessionWithAdmin(event: H3Event) { const message = "Admin session required"; const session = await requireServerSession(event, message); - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => user.id === session.accountId); if (session.access !== "admin" || account?.type !== "admin") { throw createError({ @@ -176,9 +176,9 @@ export async function requireServerSessionWithAdmin(event: H3Event) { } export async function serverSessionToApi(event: H3Event, session: ServerSession): Promise { - const users = await readUsers(); + const users = readUsers(); const account = users.find(user => !user.deleted && user.id === session.accountId); - const subscriptions = await readSubscriptions(); + const subscriptions = readSubscriptions(); const push = Boolean( subscriptions.find(sub => sub.type === "push" && sub.sessionId === session.id) ); diff --git a/server/web-push.ts b/server/web-push.ts index 06f5216..ea09623 100644 --- a/server/web-push.ts +++ b/server/web-push.ts @@ -33,7 +33,7 @@ async function useVapidDetails(event: H3Event) { export async function sendPush(event: H3Event, title: string, body: string) { const vapidDetails = await useVapidDetails(event); const payload = JSON.stringify({ title, body }); - const subscriptions = await readSubscriptions(); + const subscriptions = readSubscriptions(); console.log(`Sending "${payload}" to ${subscriptions.length} subscribers`); const removeIndexes = []; for (let index = 0; index < subscriptions.length; index += 1) { @@ -65,7 +65,7 @@ export async function sendPush(event: H3Event, title: string, body: string) { for (const index of removeIndexes) { subscriptions.splice(index, 1); } - await writeSubscriptions(subscriptions); + writeSubscriptions(subscriptions); } console.log("Push notices sent"); }