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:
parent
6336ccdb96
commit
3be7f8be05
24 changed files with 331 additions and 182 deletions
|
@ -1,22 +0,0 @@
|
|||
import { readAccounts } from "~/server/database"
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await requireServerSession(event);
|
||||
const accounts = await readAccounts();
|
||||
const account = accounts.find(a => a.id === session.accountId);
|
||||
if (!account) {
|
||||
throw new Error("Account does not exist");
|
||||
}
|
||||
|
||||
if (account.type === "admin") {
|
||||
return accounts;
|
||||
}
|
||||
if (account.type === "crew") {
|
||||
return accounts.filter(a => a.type === "crew" || a.type === "admin");
|
||||
}
|
||||
throw createError({
|
||||
status: 403,
|
||||
statusText: "Forbidden",
|
||||
message: "You do not have permission to list accounts",
|
||||
});
|
||||
})
|
|
@ -1,15 +1,8 @@
|
|||
import { deleteDatbase, readAccounts } from "~/server/database";
|
||||
import { deleteDatbase } from "~/server/database";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await requireServerSession(event);
|
||||
let accounts = await readAccounts();
|
||||
const sessionAccount = accounts.find(
|
||||
account => account.id === session.accountId
|
||||
);
|
||||
if (!sessionAccount) {
|
||||
throw Error("Account does not exist");
|
||||
}
|
||||
if (sessionAccount.type !== "admin") {
|
||||
if (session.account.type !== "admin") {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: "Forbidden",
|
||||
|
|
|
@ -1,45 +1,49 @@
|
|||
import {
|
||||
readAccounts, readSessions, readSubscriptions,
|
||||
writeAccounts, writeSessions, writeSubscriptions,
|
||||
readUsers, readSessions, readSubscriptions,
|
||||
writeUsers, writeSessions, writeSubscriptions,
|
||||
} from "~/server/database";
|
||||
import { cancelAccountStreams } from "~/server/streams";
|
||||
import { broadcastEvent, cancelAccountStreams } from "~/server/streams";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const serverSession = await requireServerSession(event);
|
||||
let users = await readUsers();
|
||||
|
||||
let accounts = await readAccounts();
|
||||
const sessionAccount = accounts.find(
|
||||
account => account.id === serverSession.accountId
|
||||
);
|
||||
if (!sessionAccount) {
|
||||
throw Error("Account does not exist");
|
||||
}
|
||||
|
||||
// Remove sessions for this account
|
||||
// Remove sessions for this user
|
||||
const removedSessionIds = new Set<number>();
|
||||
let sessions = await readSessions();
|
||||
sessions = sessions.filter(session => {
|
||||
if (session.accountId === serverSession.accountId) {
|
||||
if (session.account.id === serverSession.account.id) {
|
||||
removedSessionIds.add(session.id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
cancelAccountStreams(serverSession.accountId);
|
||||
cancelAccountStreams(serverSession.account.id);
|
||||
await writeSessions(sessions);
|
||||
await deleteCookie(event, "session");
|
||||
|
||||
// Remove subscriptions for this account
|
||||
// Remove subscriptions for this user
|
||||
let subscriptions = await readSubscriptions();
|
||||
subscriptions = subscriptions.filter(
|
||||
subscription => !removedSessionIds.has(subscription.sessionId)
|
||||
);
|
||||
await writeSubscriptions(subscriptions);
|
||||
|
||||
// Remove the account
|
||||
accounts = accounts.filter(account => account.id !== serverSession.accountId);
|
||||
await writeAccounts(accounts);
|
||||
// Remove the user
|
||||
const account = users.find(user => user.id === serverSession.account.id)!;
|
||||
const now = new Date().toISOString();
|
||||
account.deleted = true;
|
||||
account.updatedAt = now;
|
||||
await writeUsers(users);
|
||||
await broadcastEvent({
|
||||
type: "user-update",
|
||||
data: {
|
||||
id: account.id,
|
||||
updatedAt: now,
|
||||
deleted: true,
|
||||
}
|
||||
});
|
||||
|
||||
// Update Schedule counts.
|
||||
await updateScheduleInterestedCounts(accounts);
|
||||
await updateScheduleInterestedCounts(users);
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { readAccounts, writeAccounts } from "~/server/database";
|
||||
import { readUsers, writeUsers } from "~/server/database";
|
||||
import { DateTime, Info } from "~/shared/utils/luxon";
|
||||
import { apiAccountPatchSchema } from "~/shared/types/api";
|
||||
import { z } from "zod/v4-mini";
|
||||
|
@ -34,40 +34,40 @@ export default defineEventHandler(async (event) => {
|
|||
}
|
||||
}
|
||||
|
||||
const accounts = await readAccounts();
|
||||
const sessionAccount = accounts.find(account => account.id === session.accountId);
|
||||
if (!sessionAccount) {
|
||||
const users = await readUsers();
|
||||
const account = users.find(user => user.id === session.account.id);
|
||||
if (!account) {
|
||||
throw Error("Account does not exist");
|
||||
}
|
||||
|
||||
if (patch.interestedEventIds !== undefined) {
|
||||
if (patch.interestedEventIds.length) {
|
||||
sessionAccount.interestedEventIds = patch.interestedEventIds;
|
||||
account.interestedEventIds = patch.interestedEventIds;
|
||||
} else {
|
||||
delete sessionAccount.interestedEventIds;
|
||||
delete account.interestedEventIds;
|
||||
}
|
||||
}
|
||||
if (patch.interestedEventSlotIds !== undefined) {
|
||||
if (patch.interestedEventSlotIds.length) {
|
||||
sessionAccount.interestedEventSlotIds = patch.interestedEventSlotIds;
|
||||
account.interestedEventSlotIds = patch.interestedEventSlotIds;
|
||||
} else {
|
||||
delete sessionAccount.interestedEventSlotIds;
|
||||
delete account.interestedEventSlotIds;
|
||||
}
|
||||
}
|
||||
if (patch.timezone !== undefined) {
|
||||
if (patch.timezone)
|
||||
sessionAccount.timezone = patch.timezone;
|
||||
account.timezone = patch.timezone;
|
||||
else
|
||||
delete sessionAccount.timezone;
|
||||
delete account.timezone;
|
||||
}
|
||||
if (patch.locale !== undefined) {
|
||||
if (patch.locale)
|
||||
sessionAccount.locale = patch.locale;
|
||||
account.locale = patch.locale;
|
||||
else
|
||||
delete sessionAccount.locale;
|
||||
delete account.locale;
|
||||
}
|
||||
await writeAccounts(accounts);
|
||||
await writeUsers(users);
|
||||
|
||||
// Update Schedule counts.
|
||||
await updateScheduleInterestedCounts(accounts);
|
||||
await updateScheduleInterestedCounts(users);
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { readAccounts, writeAccounts, nextAccountId } from "~/server/database";
|
||||
import type { ApiAccount } from "~/shared/types/api";
|
||||
import { readUsers, writeUsers, nextUserId, type ServerUser } from "~/server/database";
|
||||
import { broadcastEvent } from "~/server/streams";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
let session = await getServerSession(event);
|
||||
|
@ -13,8 +13,8 @@ export default defineEventHandler(async (event) => {
|
|||
const formData = await readFormData(event);
|
||||
const name = formData.get("name");
|
||||
|
||||
const accounts = await readAccounts();
|
||||
let account: ApiAccount;
|
||||
const users = await readUsers();
|
||||
let user: ServerUser;
|
||||
if (typeof name === "string") {
|
||||
if (name === "") {
|
||||
throw createError({
|
||||
|
@ -22,22 +22,24 @@ export default defineEventHandler(async (event) => {
|
|||
message: "Name cannot be blank",
|
||||
});
|
||||
}
|
||||
if (accounts.some(account => account.name && account.name.toLowerCase() === name.toLowerCase())) {
|
||||
if (users.some(user => user.name && user.name.toLowerCase() === name.toLowerCase())) {
|
||||
throw createError({
|
||||
status: 409,
|
||||
message: "User already exists",
|
||||
});
|
||||
}
|
||||
|
||||
account = {
|
||||
id: await nextAccountId(),
|
||||
user = {
|
||||
id: await nextUserId(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
type: "regular",
|
||||
name,
|
||||
};
|
||||
|
||||
} else if (name === null) {
|
||||
account = {
|
||||
id: await nextAccountId(),
|
||||
user = {
|
||||
id: await nextUserId(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
type: "anonymous",
|
||||
};
|
||||
} else {
|
||||
|
@ -47,7 +49,11 @@ export default defineEventHandler(async (event) => {
|
|||
});
|
||||
}
|
||||
|
||||
accounts.push(account);
|
||||
await writeAccounts(accounts);
|
||||
await setServerSession(event, account.id);
|
||||
users.push(user);
|
||||
await writeUsers(users);
|
||||
await broadcastEvent({
|
||||
type: "user-update",
|
||||
data: user,
|
||||
});
|
||||
await setServerSession(event, user);
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { readAccounts } from "~/server/database";
|
||||
import { readUsers } from "~/server/database";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { name } = await readBody(event);
|
||||
|
@ -7,12 +7,12 @@ export default defineEventHandler(async (event) => {
|
|||
return new Response(undefined, { status: 400 })
|
||||
}
|
||||
|
||||
const accounts = await readAccounts();
|
||||
const accounts = await readUsers();
|
||||
const account = accounts.find(a => a.name === name);
|
||||
|
||||
if (!account) {
|
||||
return new Response(undefined, { status: 403 })
|
||||
}
|
||||
|
||||
await setServerSession(event, account.id);
|
||||
await setServerSession(event, account);
|
||||
})
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { readAccounts } from "~/server/database";
|
||||
import { cancelSessionStreams } from "~/server/streams";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await getServerSession(event);
|
||||
if (session) {
|
||||
const accounts = await readAccounts();
|
||||
const account = accounts.find(
|
||||
account => account.id === session.accountId
|
||||
);
|
||||
if (account && account.type === "anonymous") {
|
||||
if (session.account.type === "anonymous") {
|
||||
throw createError({
|
||||
status: 409,
|
||||
message: "Cannot log out of an anonymous account",
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { readAccounts, readSubscriptions } from "~/server/database";
|
||||
import { readSubscriptions } from "~/server/database";
|
||||
import type { ApiSession } from "~/shared/types/api";
|
||||
|
||||
export default defineEventHandler(async (event): Promise<ApiSession | undefined> => {
|
||||
const session = await getServerSession(event);
|
||||
if (!session)
|
||||
return;
|
||||
const accounts = await readAccounts();
|
||||
const subscriptions = await readSubscriptions();
|
||||
const push = Boolean(
|
||||
subscriptions.find(sub => sub.type === "push" && sub.sessionId === session.id)
|
||||
|
@ -15,7 +14,7 @@ export default defineEventHandler(async (event): Promise<ApiSession | undefined>
|
|||
|
||||
return {
|
||||
id: session.id,
|
||||
account: accounts.find(account => account.id === session.accountId)!,
|
||||
account: session.account,
|
||||
push,
|
||||
};
|
||||
})
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
import { pipeline } from "node:stream";
|
||||
import { addStream, deleteStream } from "~/server/streams";
|
||||
import { readAccounts } from "~/server/database";
|
||||
import { readUsers } from "~/server/database";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await getServerSession(event);
|
||||
let accountId: number | undefined;
|
||||
if (session) {
|
||||
const accounts = await readAccounts()
|
||||
accountId = accounts.find(account => account.id === session.accountId)?.id;
|
||||
}
|
||||
const accountId = session?.account.id;
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const source = event.headers.get("x-forwarded-for");
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
import { z } from "zod/v4-mini";
|
||||
import { readAccounts, readSchedule, writeSchedule } from "~/server/database";
|
||||
import { readUsers, readSchedule, writeSchedule } from "~/server/database";
|
||||
import { broadcastEvent } from "~/server/streams";
|
||||
import { apiScheduleSchema } from "~/shared/types/api";
|
||||
import { applyUpdatesToArray } from "~/shared/utils/update";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await requireServerSession(event);
|
||||
const accounts = await readAccounts();
|
||||
const account = accounts.find(a => a.id === session.accountId);
|
||||
if (!account) {
|
||||
throw new Error("Account does not exist");
|
||||
}
|
||||
|
||||
if (account.type !== "admin" && account.type !== "crew") {
|
||||
if (session.account.type !== "admin" && session.account.type !== "crew") {
|
||||
throw createError({
|
||||
status: 403,
|
||||
statusMessage: "Forbidden",
|
||||
|
@ -46,7 +41,7 @@ export default defineEventHandler(async (event) => {
|
|||
}
|
||||
|
||||
// Validate edit restrictions for crew
|
||||
if (account.type === "crew") {
|
||||
if (session.account.type === "crew") {
|
||||
if (update.locations?.length) {
|
||||
throw createError({
|
||||
status: 403,
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { readAccounts, readSchedule } from "~/server/database";
|
||||
import { readUsers, readSchedule } from "~/server/database";
|
||||
import type { ApiAccount } from "~/shared/types/api";
|
||||
import { canSeeCrew } from "../utils/schedule";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await getServerSession(event);
|
||||
let account: ApiAccount | undefined;
|
||||
if (session) {
|
||||
const accounts = await readAccounts()
|
||||
account = accounts.find(account => account.id === session.accountId);
|
||||
}
|
||||
const schedule = await readSchedule();
|
||||
return canSeeCrew(account?.type) ? schedule : filterSchedule(schedule);
|
||||
return canSeeCrew(session?.account.type) ? schedule : filterSchedule(schedule);
|
||||
});
|
||||
|
|
35
server/api/users/index.get.ts
Normal file
35
server/api/users/index.get.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { readUsers, type ServerUser } from "~/server/database"
|
||||
import type { ApiUser } from "~/shared/types/api";
|
||||
|
||||
function serverUserToApi(user: ServerUser): ApiUser {
|
||||
if (user.deleted) {
|
||||
return {
|
||||
id: user.id,
|
||||
updatedAt: user.updatedAt,
|
||||
deleted: true,
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: user.id,
|
||||
updatedAt: user.updatedAt,
|
||||
type: user.type,
|
||||
name: user.name,
|
||||
}
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = await requireServerSession(event);
|
||||
const users = await readUsers();
|
||||
|
||||
if (session.account.type === "admin") {
|
||||
return users.map(serverUserToApi);
|
||||
}
|
||||
if (session.account.type === "crew") {
|
||||
return users.filter(u => u.type === "crew" || u.type === "admin").map(serverUserToApi);
|
||||
}
|
||||
throw createError({
|
||||
status: 403,
|
||||
statusText: "Forbidden",
|
||||
message: "You do not have permission to list users",
|
||||
});
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue