Refactor user storage and update

Rename accounts to users to be consistent with the new naming scheme
where account only referes to the logged in user of the session and
implement live updates of users via a user store which listens for
updates from the event stream.
This commit is contained in:
Hornwitser 2025-06-23 00:17:22 +02:00
parent 6336ccdb96
commit 3be7f8be05
24 changed files with 331 additions and 182 deletions

View file

@ -1,19 +1,37 @@
import { readFile, unlink, writeFile } from "node:fs/promises";
import type { ApiAccount, ApiSchedule, ApiSubscription } from "~/shared/types/api";
import type { ApiSchedule, ApiSubscription, ApiUserType } from "~/shared/types/api";
import { generateDemoSchedule, generateDemoAccounts } from "./generate-demo-schedule";
import type { Id } from "~/shared/types/common";
export interface ServerSession {
id: number,
accountId: number,
account: ServerUser,
};
interface StoredSession {
id: number,
accountId: number,
}
export interface ServerUser {
id: Id,
updatedAt: string,
deleted?: boolean,
type: ApiUserType,
name?: string,
interestedEventIds?: number[],
interestedEventSlotIds?: number[],
timezone?: string,
locale?: string,
}
// For this demo I'm just storing the runtime data in JSON files. When making
// this into proper application this should be replaced with an actual database.
const schedulePath = "data/schedule.json";
const subscriptionsPath = "data/subscriptions.json";
const accountsPath = "data/accounts.json";
const nextAccountIdPath = "data/next-account-id.json";
const usersPath = "data/users.json";
const nextUserIdPath = "data/next-user-id.json";
const sessionsPath = "data/sessions.json";
const nextSessionIdPath = "data/next-session-id.json";
@ -30,7 +48,7 @@ async function remove(path: string) {
export async function deleteDatbase() {
await remove(schedulePath);
await remove(subscriptionsPath);
await remove(accountsPath);
await remove(usersPath);
await remove(sessionsPath);
}
@ -67,21 +85,21 @@ export async function writeSubscriptions(subscriptions: ApiSubscription[]) {
await writeFile(subscriptionsPath, JSON.stringify(subscriptions, undefined, "\t") + "\n", "utf-8");
}
export async function nextAccountId() {
let nextId = await readJson(nextAccountIdPath, 0);
export async function nextUserId() {
let nextId = await readJson(nextUserIdPath, 0);
if (nextId === 0) {
nextId = Math.max(...(await readAccounts()).map(account => account.id), -1) + 1;
nextId = Math.max(...(await readUsers()).map(user => user.id), -1) + 1;
}
await writeFile(nextAccountIdPath, String(nextId + 1), "utf-8");
await writeFile(nextUserIdPath, String(nextId + 1), "utf-8");
return nextId;
}
export async function readAccounts() {
return await readJson(accountsPath, generateDemoAccounts);
export async function readUsers() {
return await readJson(usersPath, generateDemoAccounts as () => ServerUser[]);
}
export async function writeAccounts(accounts: ApiAccount[]) {
await writeFile(accountsPath, JSON.stringify(accounts, undefined, "\t") + "\n", "utf-8");
export async function writeUsers(users: ServerUser[]) {
await writeFile(usersPath, JSON.stringify(users, undefined, "\t") + "\n", "utf-8");
}
export async function nextSessionId() {
@ -91,9 +109,26 @@ export async function nextSessionId() {
}
export async function readSessions() {
return await readJson<ServerSession[]>(sessionsPath, []);
const users = await readUsers();
const sessions: ServerSession[] = [];
for (const stored of await readJson<StoredSession[]>(sessionsPath, [])) {
const user = users.find(user => user.id === stored.accountId);
if (user) {
sessions.push({
id: stored.id,
account: user,
});
}
}
return sessions;
}
export async function writeSessions(sessions: ServerSession[]) {
await writeFile(sessionsPath, JSON.stringify(sessions, undefined, "\t") + "\n", "utf-8");
const stored: StoredSession[] = sessions.map(
session => ({
id: session.id,
accountId: session.account.id,
}),
);
await writeFile(sessionsPath, JSON.stringify(stored, undefined, "\t") + "\n", "utf-8");
}