You've already forked obsidian-visualiser
Merge branch 'character' into dev
This commit is contained in:
@@ -5,6 +5,7 @@ import { usersDataTable, usersTable } from '~/db/schema';
|
||||
import { schema } from '~/schemas/registration';
|
||||
import { checkSession, logSession } from '~/server/utils/user';
|
||||
import type { UserSession, UserSessionRequired } from '~/types/auth';
|
||||
import sendMail from '~/server/tasks/mail';
|
||||
|
||||
interface SuccessHandler
|
||||
{
|
||||
@@ -82,7 +83,7 @@ export default defineEventHandler(async (e): Promise<Return> => {
|
||||
id: emailId, timestamp,
|
||||
}
|
||||
});
|
||||
await runTask('mail', {
|
||||
await sendMail({
|
||||
payload: {
|
||||
type: 'mail',
|
||||
to: [body.data.email],
|
||||
|
||||
@@ -3,6 +3,7 @@ import { eq, or } from 'drizzle-orm';
|
||||
import { z } from 'zod';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { usersTable } from '~/db/schema';
|
||||
import sendMail from '~/server/tasks/mail';
|
||||
|
||||
const schema = z.object({
|
||||
profile: z.string(),
|
||||
@@ -32,7 +33,7 @@ export default defineEventHandler(async (e) => {
|
||||
id, timestamp,
|
||||
}
|
||||
});
|
||||
await runTask('mail', {
|
||||
await sendMail({
|
||||
payload: {
|
||||
type: 'mail',
|
||||
data: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { usersDataTable, usersTable } from '~/db/schema';
|
||||
import { schema } from '~/schemas/registration';
|
||||
import { checkSession, logSession } from '~/server/utils/user';
|
||||
import type { UserSession, UserSessionRequired } from '~/types/auth';
|
||||
import sendMail from '~/server/tasks/mail';
|
||||
|
||||
interface SuccessHandler
|
||||
{
|
||||
@@ -73,7 +74,7 @@ export default defineEventHandler(async (e): Promise<Return> => {
|
||||
|
||||
logSession(e, await setUserSession(e, { user: { id: id.id, username: body.data.username, email: body.data.email, state: 0, signin: new Date(), permissions: [], lastTimestamp: new Date() } }) as UserSessionRequired);
|
||||
|
||||
await runTask('mail', {
|
||||
await sendMail({
|
||||
payload: {
|
||||
type: 'mail',
|
||||
to: [body.data.email],
|
||||
|
||||
95
server/api/character.get.ts
Normal file
95
server/api/character.get.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { and, eq, SQL, sql, type Operators } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable, userPermissionsTable } from '~/db/schema';
|
||||
import { hasPermissions } from '~/shared/auth.util';
|
||||
import { group } from '~/shared/general.util';
|
||||
import type { Character, DoubleIndex, Level, MainStat, TrainingLevel } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
let { visibility } = getQuery(e) as { visibility?: "public" | "own" | "admin" };
|
||||
|
||||
if(!visibility)
|
||||
{
|
||||
visibility = "own";
|
||||
}
|
||||
|
||||
let where: ((character: typeof characterTable._.config.columns, sql: Operators) => SQL | undefined) | undefined = undefined;
|
||||
const db = useDatabase();
|
||||
|
||||
if(visibility === "own")
|
||||
{
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
where = (character, { eq, and }) => and(eq(character.owner, session.user!.id), eq(character.visibility, "private"));
|
||||
}
|
||||
else if(visibility === 'public')
|
||||
{
|
||||
where = (character, { eq, and }) => eq(character.visibility, "public");
|
||||
}
|
||||
else if(visibility === 'admin')
|
||||
{
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
const db = useDatabase();
|
||||
|
||||
const rights = db.select({ right: userPermissionsTable.permission }).from(userPermissionsTable).where(eq(userPermissionsTable.id, session.user.id)).all();
|
||||
if(rights.length === 0 || !hasPermissions(rights.map(e => e.right), ['admin']))
|
||||
{
|
||||
setResponseStatus(e, 403);
|
||||
return;
|
||||
}
|
||||
|
||||
where = undefined;
|
||||
}
|
||||
|
||||
const characters = db.query.characterTable.findMany({
|
||||
with: {
|
||||
abilities: true,
|
||||
levels: true,
|
||||
modifiers: true,
|
||||
spells: true,
|
||||
training: true,
|
||||
user: {
|
||||
columns: { username: true }
|
||||
}
|
||||
},
|
||||
where: where,
|
||||
}).sync();
|
||||
|
||||
if(characters !== undefined)
|
||||
{
|
||||
return characters.map(character => ({
|
||||
id: character.id,
|
||||
|
||||
name: character.name,
|
||||
people: character.people,
|
||||
level: character.level,
|
||||
aspect: character.aspect,
|
||||
notes: character.notes,
|
||||
health: character.health,
|
||||
mana: character.mana,
|
||||
|
||||
training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
||||
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
||||
abilities: group(character.abilities.map(e => ({ ...e, value: [e.value, e.max] as [number, number] })), "ability", "value"),
|
||||
spells: character.spells.map(e => e.value),
|
||||
modifiers: group(character.modifiers, "modifier", "value"),
|
||||
|
||||
owner: character.owner,
|
||||
username: character.user.username,
|
||||
visibility: character.visibility,
|
||||
} as Character));
|
||||
}
|
||||
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
});
|
||||
67
server/api/character.post.ts
Normal file
67
server/api/character.post.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { z } from 'zod';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterAbilitiesTable, characterLevelingTable, characterModifiersTable, characterSpellsTable, characterTable, characterTrainingTable } from '~/db/schema';
|
||||
import { CharacterValidation } from '#shared/character.util';
|
||||
import { type Ability, type MainStat } from '~/types/character';
|
||||
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readValidatedBody(e, CharacterValidation.extend({ id: z.unknown(), }).safeParse);
|
||||
if(!body.success)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return body.error.message;
|
||||
}
|
||||
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user || session.user.state !== 1)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
|
||||
try
|
||||
{
|
||||
const id = db.transaction((tx) => {
|
||||
const id = tx.insert(characterTable).values({
|
||||
name: body.data.name,
|
||||
owner: session.user!.id,
|
||||
people: body.data.people!,
|
||||
level: body.data.level,
|
||||
aspect: body.data.aspect,
|
||||
notes: body.data.notes,
|
||||
health: body.data.health,
|
||||
mana: body.data.mana,
|
||||
visibility: body.data.visibility,
|
||||
thumbnail: body.data.thumbnail,
|
||||
}).returning({ id: characterTable.id }).get().id;
|
||||
|
||||
if(body.data.leveling.length > 0) tx.insert(characterLevelingTable).values(body.data.leveling.map(e => ({ character: id, level: e[0], choice: e[1] }))).run();
|
||||
|
||||
const training = Object.entries(body.data.training).flatMap(e => e[1].map(_e => ({ character: id, stat: e[0] as MainStat, level: _e[0], choice: _e[1] })));
|
||||
if(training.length > 0) tx.insert(characterTrainingTable).values(training).run();
|
||||
|
||||
const modifiers = Object.entries(body.data.modifiers).map((e) => ({ character: id, modifier: e[0] as MainStat, value: e[1] }));
|
||||
if(modifiers.length > 0) tx.insert(characterModifiersTable).values(modifiers).run();
|
||||
|
||||
if(body.data.spells.length > 0) tx.insert(characterSpellsTable).values(body.data.spells.map(e => ({ character: id, value: e }))).run();
|
||||
|
||||
const abilities = Object.entries(body.data.abilities).map(e => ({ character: id, ability: e[0] as Ability, value: e[1][0], max: e[1][1] }));
|
||||
if(abilities.length > 0) tx.insert(characterAbilitiesTable).values(abilities).run();
|
||||
|
||||
return id;
|
||||
});
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
return id;
|
||||
}
|
||||
catch(_e)
|
||||
{
|
||||
console.error(_e);
|
||||
|
||||
setResponseStatus(e, 500);
|
||||
return;
|
||||
}
|
||||
});
|
||||
33
server/api/character/[id].delete.ts
Normal file
33
server/api/character/[id].delete.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
if(!id)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
const old = db.select({ id: characterTable.id, owner: characterTable.owner }).from(characterTable).where(eq(characterTable.id, id)).get();
|
||||
|
||||
if(!old)
|
||||
{
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user || old.owner !== session.user.id)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
db.delete(characterTable).where(eq(characterTable.id, id)).run();
|
||||
|
||||
setResponseStatus(e, 200);
|
||||
return;
|
||||
});
|
||||
66
server/api/character/[id].get.ts
Normal file
66
server/api/character/[id].get.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { and, eq, sql } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import { group } from '~/shared/general.util';
|
||||
import type { Character, DoubleIndex, Level, MainStat, TrainingLevel } from '~/types/character';
|
||||
|
||||
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,
|
||||
modifiers: true,
|
||||
spells: true,
|
||||
training: true,
|
||||
user: {
|
||||
columns: { username: true }
|
||||
}
|
||||
},
|
||||
where: (character, { eq, and }) => and(eq(character.id, parseInt(id, 10)), eq(characterTable.owner, session.user!.id)),
|
||||
}).sync();
|
||||
|
||||
if(character !== undefined)
|
||||
{
|
||||
return {
|
||||
id: character.id,
|
||||
|
||||
name: character.name,
|
||||
people: character.people,
|
||||
level: character.level,
|
||||
aspect: character.aspect,
|
||||
notes: character.notes,
|
||||
health: character.health,
|
||||
mana: character.mana,
|
||||
|
||||
training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
||||
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
||||
abilities: group(character.abilities.map(e => ({ ...e, value: [e.value, e.max] as [number, number] })), "ability", "value"),
|
||||
spells: character.spells.map(e => e.value),
|
||||
modifiers: group(character.modifiers, "modifier", "value"),
|
||||
|
||||
owner: character.owner,
|
||||
username: character.user.username,
|
||||
visibility: character.visibility,
|
||||
} as Character;
|
||||
}
|
||||
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
});
|
||||
76
server/api/character/[id].post.ts
Normal file
76
server/api/character/[id].post.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterAbilitiesTable, characterLevelingTable, characterModifiersTable, characterSpellsTable, characterTable, characterTrainingTable } from '~/db/schema';
|
||||
import { CharacterValidation } from '#shared/character.util';
|
||||
import { type Ability, type MainStat } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const params = getRouterParam(e, "id");
|
||||
if(!params)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
const id = parseInt(params, 10);
|
||||
|
||||
const body = await readValidatedBody(e, CharacterValidation.safeParse);
|
||||
if(!body.success)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return body.error.message;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
const old = db.select({ id: characterTable.id, owner: characterTable.owner }).from(characterTable).where(eq(characterTable.id, id)).get();
|
||||
|
||||
if(!old)
|
||||
{
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user || old.owner !== session.user.id || session.user.state !== 1)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
db.transaction((tx) => {
|
||||
tx.update(characterTable).set({
|
||||
name: body.data.name,
|
||||
people: body.data.people!,
|
||||
level: body.data.level,
|
||||
aspect: body.data.aspect,
|
||||
notes: body.data.notes,
|
||||
health: body.data.health,
|
||||
mana: body.data.mana,
|
||||
visibility: body.data.visibility,
|
||||
thumbnail: body.data.thumbnail,
|
||||
}).where(eq(characterTable.id, id)).run();
|
||||
|
||||
tx.delete(characterLevelingTable).where(eq(characterLevelingTable.character, id)).run();
|
||||
tx.delete(characterTrainingTable).where(eq(characterTrainingTable.character, id)).run();
|
||||
tx.delete(characterModifiersTable).where(eq(characterModifiersTable.character, id)).run();
|
||||
tx.delete(characterSpellsTable).where(eq(characterSpellsTable.character, id)).run();
|
||||
tx.delete(characterAbilitiesTable).where(eq(characterAbilitiesTable.character, id)).run();
|
||||
|
||||
if(body.data.leveling.length > 0) tx.insert(characterLevelingTable).values(body.data.leveling.map(e => ({ character: id, level: e[0], choice: e[1] }))).run();
|
||||
|
||||
const training = Object.entries(body.data.training).flatMap(e => e[1].map(_e => ({ character: id, stat: e[0] as MainStat, level: _e[0], choice: _e[1] })));
|
||||
if(training.length > 0) tx.insert(characterTrainingTable).values(training).run();
|
||||
|
||||
const modifiers = Object.entries(body.data.modifiers).map((e) => ({ character: id, modifier: e[0] as MainStat, value: e[1] }));
|
||||
if(modifiers.length > 0) tx.insert(characterModifiersTable).values(modifiers).run();
|
||||
|
||||
if(body.data.spells.length > 0) tx.insert(characterSpellsTable).values(body.data.spells.map(e => ({ character: id, value: e }))).run();
|
||||
|
||||
const abilities = Object.entries(body.data.abilities).map(e => ({ character: id, ability: e[0] as Ability, value: e[1][0], max: e[1][1] }));
|
||||
if(abilities.length > 0) tx.insert(characterAbilitiesTable).values(abilities).run();
|
||||
});
|
||||
|
||||
await useStorage('cache').removeItem(`nitro:functions:character:${id}.json`);
|
||||
|
||||
setResponseStatus(e, 200);
|
||||
return;
|
||||
});
|
||||
149
server/api/character/[id]/compiled.get.ts
Normal file
149
server/api/character/[id]/compiled.get.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { type Character, type CharacterConfig, type CompiledCharacter, type DoubleIndex, type Level, type MainStat, type TrainingLevel, type TrainingOption } from '~/types/character';
|
||||
import characterData from '#shared/character-config.json';
|
||||
import { group } from '#shared/general.util';
|
||||
import { defaultCharacter, MAIN_STATS } from '#shared/character.util';
|
||||
|
||||
export default defineCachedEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
if(!id)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
const character = db.query.characterTable.findFirst({
|
||||
with: {
|
||||
abilities: true,
|
||||
levels: true,
|
||||
modifiers: true,
|
||||
spells: true,
|
||||
training: true,
|
||||
user: {
|
||||
columns: { username: true }
|
||||
}
|
||||
},
|
||||
where: (character, { eq }) => eq(character.id, parseInt(id, 10)),
|
||||
}).sync();
|
||||
|
||||
if(character !== undefined)
|
||||
{
|
||||
return compileCharacter(Object.assign(defaultCharacter, {
|
||||
id: character.id,
|
||||
|
||||
name: character.name,
|
||||
people: character.people,
|
||||
level: character.level,
|
||||
aspect: character.aspect,
|
||||
notes: character.notes,
|
||||
health: character.health,
|
||||
mana: character.mana,
|
||||
|
||||
training: character.training.reduce((p, v) => { if(!(v.stat in p)) p[v.stat] = []; p[v.stat].push([v.level as TrainingLevel, v.choice]); return p; }, {} as Record<MainStat, DoubleIndex<TrainingLevel>[]>),
|
||||
leveling: character.levels.map(e => [e.level as Level, e.choice] as DoubleIndex<Level>),
|
||||
abilities: group(character.abilities.map(e => ({ ...e, value: [e.value, e.max] as [number, number] })), "ability", "value"),
|
||||
spells: character.spells.map(e => e.value),
|
||||
modifiers: group(character.modifiers, "modifier", "value"),
|
||||
|
||||
owner: character.owner,
|
||||
username: character.user.username,
|
||||
visibility: character.visibility,
|
||||
} as Character) as Character);
|
||||
}
|
||||
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
}, { name: "character", getKey: (e) => getRouterParam(e, "id") || 'error' });
|
||||
|
||||
function compileCharacter(character: Character & { username?: string }): CompiledCharacter
|
||||
{
|
||||
const config = characterData as CharacterConfig;
|
||||
const race = character.people !== undefined ? config.peoples[character.people] : undefined;
|
||||
const raceOptions = race ? character.leveling!.map(e => race.options[e[0]][e[1]]) : [];
|
||||
const features = Object.entries(config.training).map(e => [e[0], getFeaturesOf(e[0] as MainStat, character.training[e[0] as MainStat])]) as [MainStat, TrainingOption[]][];
|
||||
|
||||
const compiled: CompiledCharacter = {
|
||||
id: character.id,
|
||||
owner: character.owner,
|
||||
username: character.username,
|
||||
name: character.name,
|
||||
health: raceOptions.reduce((p, v) => p + (v.health ?? 0), 0),
|
||||
mana: raceOptions.reduce((p, v) => p + (v.mana ?? 0), 0),
|
||||
race: character.people!,
|
||||
modifier: features.map(e => [e[0], Math.floor((e[1].length - 1) / 3) + (character.modifiers[e[0]] ?? 0)] as [MainStat, number]).reduce((p, v) => { p[v[0]] = v[1]; return p }, {} as Record<MainStat, number>),
|
||||
level: character.level,
|
||||
values: {
|
||||
health: character.health,
|
||||
mana: character.mana
|
||||
},
|
||||
features: {
|
||||
action: [],
|
||||
reaction: [],
|
||||
freeaction: [],
|
||||
passive: [],
|
||||
},
|
||||
abilities: {
|
||||
athletics: 0,
|
||||
acrobatics: 0,
|
||||
intimidation: 0,
|
||||
sleightofhand: 0,
|
||||
stealth: 0,
|
||||
survival: 0,
|
||||
investigation: 0,
|
||||
history: 0,
|
||||
religion: 0,
|
||||
arcana: 0,
|
||||
understanding: 0,
|
||||
perception: 0,
|
||||
performance: 0,
|
||||
medecine: 0,
|
||||
persuasion: 0,
|
||||
animalhandling: 0,
|
||||
deception: 0
|
||||
},
|
||||
spellslots: 0,
|
||||
artslots: 0,
|
||||
spellranks: {
|
||||
instinct: 0,
|
||||
knowledge: 0,
|
||||
precision: 0,
|
||||
arts: 0,
|
||||
},
|
||||
spells: character.spells ?? [],
|
||||
speed: false,
|
||||
defense: {
|
||||
hardcap: Infinity,
|
||||
static: 6,
|
||||
activeparry: 0,
|
||||
activedodge: 0,
|
||||
passiveparry: 0,
|
||||
passivedodge: 0,
|
||||
},
|
||||
mastery: {
|
||||
strength: 0,
|
||||
dexterity: 0,
|
||||
shield: 0,
|
||||
armor: 0,
|
||||
multiattack: 1,
|
||||
magicpower: 0,
|
||||
magicspeed: 0,
|
||||
magicelement: 0,
|
||||
magicinstinct: 0,
|
||||
},
|
||||
resistance: {},//Object.fromEntries(MAIN_STATS.map(e => [e as MainStat, [0, 0]])) as Record<MainStat, [number, number]>,
|
||||
initiative: 0,
|
||||
aspect: "",
|
||||
notes: character.notes ?? "",
|
||||
};
|
||||
|
||||
//features.forEach(e => e[1].forEach(_e => _e.features?.forEach(f => applyFeature(compiled, f))));
|
||||
|
||||
return compiled;
|
||||
}
|
||||
|
||||
export function getFeaturesOf(stat: MainStat, progression: DoubleIndex<TrainingLevel>[]): TrainingOption[]
|
||||
{
|
||||
const config = characterData as CharacterConfig;
|
||||
return progression.map(e => config.training[stat][e[0]][e[1]]);
|
||||
}
|
||||
71
server/api/character/[id]/duplicate.post.ts
Normal file
71
server/api/character/[id]/duplicate.post.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterAbilitiesTable, characterLevelingTable, characterModifiersTable, characterSpellsTable, characterTable, characterTrainingTable } from '~/db/schema';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
if(!id)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
const old = db.select().from(characterTable).where(eq(characterTable.id, parseInt(id, 10))).get();
|
||||
|
||||
if(!old)
|
||||
{
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user || old.owner !== session.user.id || session.user.state !== 1)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const _id = db.transaction((tx) => {
|
||||
const _id = tx.insert(characterTable).values({
|
||||
name: old.name,
|
||||
owner: session.user!.id,
|
||||
people: old.people!,
|
||||
level: old.level,
|
||||
aspect: old.aspect,
|
||||
notes: old.notes,
|
||||
health: old.health,
|
||||
mana: old.mana,
|
||||
visibility: old.visibility,
|
||||
thumbnail: old.thumbnail,
|
||||
}).returning({ id: characterTable.id }).get().id;
|
||||
|
||||
const leveling = tx.select().from(characterLevelingTable).where(eq(characterLevelingTable.character, parseInt(id, 10))).all();
|
||||
if(leveling.length > 0) tx.insert(characterLevelingTable).values(leveling.map(e => ({ character: _id, level: e.level, choice: e.choice }))).run();
|
||||
|
||||
const training = tx.select().from(characterTrainingTable).where(eq(characterTrainingTable.character, parseInt(id, 10))).all();
|
||||
if(training.length > 0) tx.insert(characterTrainingTable).values(training.map(e => ({ character: _id, stat: e.stat, level: e.level, choice: e.choice }))).run();
|
||||
|
||||
const modifiers = tx.select().from(characterModifiersTable).where(eq(characterModifiersTable.character, parseInt(id, 10))).all();
|
||||
if(modifiers.length > 0) tx.insert(characterModifiersTable).values(modifiers.map(e => ({ character: _id, modifier: e.modifier, value: e.value }))).run();
|
||||
|
||||
const spells = tx.select().from(characterSpellsTable).where(eq(characterSpellsTable.character, parseInt(id, 10))).all();
|
||||
if(spells.length > 0) tx.insert(characterSpellsTable).values(spells.map(e => ({ character: _id, value: e.value }))).run();
|
||||
|
||||
const abilities = tx.select().from(characterAbilitiesTable).where(eq(characterAbilitiesTable.character, parseInt(id, 10))).all();
|
||||
if(abilities.length > 0) tx.insert(characterAbilitiesTable).values(abilities.map(e => ({ character: _id, ability: e.ability, value: e.value, max: e.max }))).run();
|
||||
|
||||
return _id;
|
||||
});
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
return _id;
|
||||
}
|
||||
catch(_e)
|
||||
{
|
||||
setResponseStatus(e, 201);
|
||||
throw _e;
|
||||
}
|
||||
});
|
||||
35
server/api/character/[id]/values.get.ts
Normal file
35
server/api/character/[id]/values.get.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { and, eq, sql } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import type { Character, CharacterValues } from '~/types/character';
|
||||
|
||||
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.select({
|
||||
health: characterTable.health,
|
||||
mana: characterTable.mana,
|
||||
}).from(characterTable).where(and(eq(characterTable.id, parseInt(id, 10)), eq(characterTable.owner, session.user.id))).get();
|
||||
|
||||
if(character !== undefined)
|
||||
{
|
||||
return character as CharacterValues;
|
||||
}
|
||||
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
});
|
||||
44
server/api/character/[id]/values.post.ts
Normal file
44
server/api/character/[id]/values.post.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import type { CharacterValues } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
if(!id)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const body = await readBody(e) as CharacterValues;
|
||||
if(!body)
|
||||
{
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const db = useDatabase();
|
||||
const old = db.select({ id: characterTable.id, owner: characterTable.owner }).from(characterTable).where(eq(characterTable.id, parseInt(id, 10))).get();
|
||||
|
||||
if(!old)
|
||||
{
|
||||
setResponseStatus(e, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getUserSession(e);
|
||||
if(!session.user || old.owner !== session.user.id || session.user.state !== 1)
|
||||
{
|
||||
setResponseStatus(e, 401);
|
||||
return;
|
||||
}
|
||||
|
||||
db.update(characterTable).set({
|
||||
health: body.health,
|
||||
mana: body.mana,
|
||||
}).where(eq(characterTable.id, parseInt(id, 10))).run();
|
||||
|
||||
setResponseStatus(e, 200);
|
||||
return;
|
||||
});
|
||||
22
server/api/homebrew/[id].get.ts
Normal file
22
server/api/homebrew/[id].get.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
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;
|
||||
}
|
||||
|
||||
console.log(id);
|
||||
|
||||
setResponseStatus(e, 200);
|
||||
return {};
|
||||
});
|
||||
23
server/api/homebrew/[id].post.ts
Normal file
23
server/api/homebrew/[id].post.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
}
|
||||
|
||||
console.log(id);
|
||||
console.log(await readBody(e));
|
||||
|
||||
setResponseStatus(e, 200);
|
||||
return;
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { hash } from "bun";
|
||||
import { eq } from "drizzle-orm";
|
||||
import useDatabase from "~/composables/useDatabase";
|
||||
import { usersTable } from "~/db/schema";
|
||||
import sendMail from '~/server/tasks/mail';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const session = await getUserSession(e);
|
||||
@@ -56,7 +57,7 @@ export default defineEventHandler(async (e) => {
|
||||
id: emailId, timestamp,
|
||||
}
|
||||
});
|
||||
await runTask('mail', {
|
||||
await sendMail({
|
||||
payload: {
|
||||
type: 'mail',
|
||||
to: [data.email],
|
||||
|
||||
@@ -28,69 +28,78 @@ const transport = nodemailer.createTransport({
|
||||
pool: true,
|
||||
host: config.mail.host,
|
||||
port: config.mail.port,
|
||||
secure: true,
|
||||
secure: config.mail.port === "465",
|
||||
auth: {
|
||||
user: config.mail.user,
|
||||
pass: config.mail.passwd,
|
||||
},
|
||||
requireTLS: true,
|
||||
tls: { rejectUnauthorized: false },
|
||||
dkim: {
|
||||
domainName: domain,
|
||||
keySelector: selector,
|
||||
privateKey: dkim,
|
||||
},
|
||||
proxy: config.mail.proxy,
|
||||
});
|
||||
|
||||
export default defineTask({
|
||||
meta: {
|
||||
name: 'mail',
|
||||
description: 'Send email',
|
||||
},
|
||||
async run(e) {
|
||||
try {
|
||||
if(e.payload.type !== 'mail')
|
||||
{
|
||||
throw new Error(`Données inconnues`);
|
||||
}
|
||||
|
||||
const payload = e.payload as MailPayload;
|
||||
const template = templates[payload.template];
|
||||
|
||||
if(!template)
|
||||
{
|
||||
throw new Error(`Modèle de mail ${payload.template} inconnu`);
|
||||
}
|
||||
|
||||
console.time('Generating HTML');
|
||||
const mail: Mail.Options = {
|
||||
from: 'd[any] - Ne pas répondre <no-reply@peaceultime.com>',
|
||||
to: payload.to,
|
||||
html: await render(template.component, payload.data),
|
||||
subject: template.subject,
|
||||
textEncoding: 'quoted-printable',
|
||||
};
|
||||
console.timeEnd('Generating HTML');
|
||||
|
||||
if(mail.html === '')
|
||||
return { result: false, error: new Error("Invalid content") };
|
||||
|
||||
console.time('Sending Mail');
|
||||
const status = await transport.sendMail(mail);
|
||||
console.timeEnd('Sending Mail');
|
||||
|
||||
if(status.rejected.length > 0)
|
||||
{
|
||||
return { result: false, error: status.response, details: status.rejectedErrors };
|
||||
}
|
||||
|
||||
return { result: true };
|
||||
}
|
||||
catch(e)
|
||||
if(process.env.NODE_ENV === 'production')
|
||||
{
|
||||
transport.verify((error) => {
|
||||
if(error)
|
||||
{
|
||||
return { result: false, error: e };
|
||||
console.log('Mail server cannot be reached');
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
})
|
||||
else
|
||||
console.log("Mail server is reachable and ready to communicate");
|
||||
});
|
||||
}
|
||||
|
||||
export default async function(e: TaskEvent) {
|
||||
try {
|
||||
if(e.payload.type !== 'mail')
|
||||
{
|
||||
throw new Error(`Données inconnues`);
|
||||
}
|
||||
|
||||
const payload = e.payload as MailPayload;
|
||||
const template = templates[payload.template];
|
||||
|
||||
if(!template)
|
||||
{
|
||||
throw new Error(`Modèle de mail ${payload.template} inconnu`);
|
||||
}
|
||||
|
||||
console.time('Generating HTML');
|
||||
const mail: Mail.Options = {
|
||||
from: 'd[any] - Ne pas répondre <no-reply@peaceultime.com>',
|
||||
to: payload.to,
|
||||
html: await render(template.component, payload.data),
|
||||
subject: template.subject,
|
||||
textEncoding: 'quoted-printable',
|
||||
};
|
||||
console.timeEnd('Generating HTML');
|
||||
|
||||
if(mail.html === '')
|
||||
return { result: false, error: new Error("Invalid content") };
|
||||
|
||||
console.time('Sending Mail');
|
||||
const status = await transport.sendMail(mail);
|
||||
console.timeEnd('Sending Mail');
|
||||
|
||||
if(status.rejected.length > 0)
|
||||
{
|
||||
return { result: false, error: status.response, details: status.rejectedErrors };
|
||||
}
|
||||
|
||||
return { result: true };
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
console.error(e);
|
||||
return { result: false, error: e };
|
||||
}
|
||||
}
|
||||
|
||||
async function render(component: any, data: Record<string, any>): Promise<string>
|
||||
{
|
||||
|
||||
@@ -34,12 +34,11 @@ export default defineTask({
|
||||
{
|
||||
const title = basename(e.path);
|
||||
const order = /(\d+)\. ?(.+)/gsmi.exec(title);
|
||||
const path = (e.path as string).split('/').map(f => { const check = /(\d+)\. ?(.+)/gsmi.exec(f); return check && check[2] ? check[2] : f }).join('/');
|
||||
return {
|
||||
id: getID(ID_SIZE),
|
||||
path: parsePath(path),
|
||||
path: parsePath(e.path),
|
||||
order: i,
|
||||
title: order && order[2] ? order[2] : title,
|
||||
title: title,
|
||||
type: 'folder',
|
||||
content: null,
|
||||
owner: 1,
|
||||
@@ -52,14 +51,13 @@ export default defineTask({
|
||||
const extension = extname(e.path);
|
||||
const title = basename(e.path, extension);
|
||||
const order = /(\d+)\. ?(.+)/gsmi.exec(title);
|
||||
const path = (e.path as string).split('/').map(f => { const check = /(\d+)\. ?(.+)/gsmi.exec(f); return check && check[2] ? check[2] : f }).join('/');
|
||||
const content = (await $fetch(`https://git.peaceultime.com/api/v1/repos/peaceultime/system-aspect/raw/${encodeURIComponent(e.path)}`));
|
||||
|
||||
return {
|
||||
id: getID(ID_SIZE),
|
||||
path: parsePath(extension === '.md' ? path.replace(extension, '') : path),
|
||||
path: parsePath(extension === '.md' ? e.path.replace(extension, '') : e.path),
|
||||
order: i,
|
||||
title: order && order[2] ? order[2] : title,
|
||||
title: title,
|
||||
type: (typeMapping[extension] ?? 'file'),
|
||||
content: reshapeContent(content as string, typeMapping[extension] ?? 'File'),
|
||||
owner: 1,
|
||||
@@ -107,8 +105,8 @@ function reshapeContent(content: string, type: FileType): string | null
|
||||
return content;
|
||||
case "canvas":
|
||||
const data = JSON.parse(content) as CanvasContent;
|
||||
data.edges?.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined);
|
||||
data.nodes?.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined);
|
||||
data.edges?.forEach(e => { console.log(e.color); e.color = typeof e.color === 'string' ? getColor(e.color) : undefined; console.log(e.color); });
|
||||
data.nodes?.forEach(e => { console.log(e.color); e.color = typeof e.color === 'string' ? getColor(e.color) : undefined; console.log(e.color); });
|
||||
return JSON.stringify(data);
|
||||
default:
|
||||
case 'folder':
|
||||
|
||||
@@ -14,7 +14,7 @@ export default defineTask({
|
||||
name: 'validation',
|
||||
description: 'Add email ID to DB',
|
||||
},
|
||||
async run(e) {
|
||||
run(e) {
|
||||
try {
|
||||
if(e.payload.type !== 'validation')
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user