obsidian-visualiser/server/api/character/[id].get.ts

80 lines
3.2 KiB
TypeScript

import useDatabase from '~/composables/useDatabase';
import { campaignCharactersTable, campaignMembersTable, campaignTable, characterAbilitiesTable, characterChoicesTable, characterLevelingTable, characterTable, characterTrainingTable, usersTable } from '~/db/schema';
import { group } from '#shared/general.util';
import type { Character, MainStat, TrainingLevel } from '~/types/character';
import { and, eq, exists, getTableColumns, isNotNull, or, sql } from 'drizzle-orm';
export default defineEventHandler(async (e) => {
const id = getRouterParam(e, "id");
if(!id)
{
setResponseStatus(e, 400);
return;
}
const session = await getUserSession(e);
if(!session.user)
{
setResponseStatus(e, 401);
return;
}
const db = useDatabase();
const character = db.query.characterTable.findFirst({
with: {
abilities: true,
levels: true,
training: true,
choices: true,
user: {
columns: { username: true }
},
campaign: {
columns: { character: false, id: true, },
with: {
campaign: {
columns: { owner: true, },
with: {
members: { columns: { user: true } }
}
}
}
}
},
where: and(eq(characterTable.id, parseInt(id, 10)), or(eq(characterTable.visibility, 'public'), eq(characterTable.owner, session.user!.id), exists(db.select({ id: sql`NULL` }).from(campaignCharactersTable)
.leftJoin(campaignTable, eq(campaignCharactersTable.id, campaignTable.id))
.leftJoin(campaignMembersTable, and(eq(campaignMembersTable.id, campaignTable.id), eq(campaignMembersTable.user, session.user.id)))
.where(and(eq(campaignCharactersTable.character, parseInt(id, 10)), or(eq(campaignTable.owner, session.user.id), isNotNull(campaignMembersTable.user))))))),
}).sync();
if(character !== undefined)
{
return {
id: character.id,
name: character.name,
people: character.people,
level: character.level,
aspect: character.aspect,
notes: { public: character.public_notes, private: session.user?.id === character.owner ? character.private_notes : undefined },
variables: character.variables,
training: character.training.reduce((p, v) => { p[v.stat] ??= {}; p[v.stat][v.level as TrainingLevel] = v.choice; return p; }, {} as Record<MainStat, Partial<Record<TrainingLevel, number>>>),
leveling: group(character.levels, "level", "choice"),
abilities: group(character.abilities, "ability", "value"),
choices: character.choices.reduce((p, v) => { p[v.id] ??= []; p[v.id]?.push(v.choice); return p; }, {} as Record<string, number[]>),
owner: character.owner,
username: character.user.username,
visibility: character.visibility,
campaign: character.campaign?.id,
} as Character;
}
setResponseStatus(e, 404);
return;
});