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.
This commit is contained in:
parent
f9d188b2ba
commit
80cec71308
23 changed files with 138 additions and 138 deletions
|
@ -2,7 +2,7 @@
|
|||
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
|
||||
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<T>(filePath: string, fallback: T) {
|
||||
function readJson<T>(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<T>(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<ApiSubscription[]>(subscriptionsPath, []);
|
||||
export function readSubscriptions() {
|
||||
let subscriptions = readJson<ApiSubscription[]>(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<ServerSession[]>(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<ServerAuthenticationMethod[]>(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<ApiEvent[]>(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");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue