This commit is contained in:
2025-09-30 21:50:59 +02:00
20 changed files with 1162 additions and 855 deletions

View File

@@ -4,7 +4,11 @@ declare module 'nitropack'
{
interface TaskPayload
{
type: string
type: string;
}
interface TaskResult<RT = unknown>
{
error?: Error | string;
}
}
@@ -17,7 +21,7 @@ export default defineEventHandler(async (e) => {
return;
}
const id = getRouterParam(e, 'id');
const payload: Record<string, any> = await readBody(e);
const body: Record<string, any> = await readBody(e);
if(!id)
{
@@ -25,8 +29,11 @@ export default defineEventHandler(async (e) => {
return;
}
payload.type = id;
payload.data = JSON.parse(payload.data);
body.data = JSON.parse(body.data);
const payload = {
type: id,
data: body,
}
const result = await runTask(id, {
payload: payload
@@ -36,7 +43,7 @@ export default defineEventHandler(async (e) => {
{
setResponseStatus(e, 500);
if(result.error && (result.error as Error).message)
if(result.error && result.error.message)
throw result.error;
else if(result.error)
throw new Error(result.error);

View File

@@ -25,7 +25,7 @@ export default defineEventHandler(async (e) => {
return;
}
where = (character, { eq, and }) => and(eq(character.owner, session.user!.id), eq(character.visibility, "private"));
where = (character, { eq, and }) => and(eq(character.owner, session.user!.id));
}
else if(visibility === 'public')
{

View File

@@ -37,12 +37,12 @@ export default defineEventHandler(async (e) => {
thumbnail: body.data.thumbnail,
}).returning({ id: characterTable.id }).get().id;
if(Object.keys(body.data.leveling).length > 0) tx.insert(characterLevelingTable).values(Object.entries(body.data.leveling).map(e => ({ character: id, level: parseInt(e[0], 10), choice: e[1]! }))).run();
if(Object.keys(body.data.leveling).length > 0) tx.insert(characterLevelingTable).values(Object.entries(body.data.leveling).filter(e => e[1] !== undefined).map(e => ({ character: id, level: parseInt(e[0], 10), choice: e[1]! }))).run();
const training = Object.entries(body.data.training).flatMap(e => Object.entries(e[1]).map(_e => ({ character: id, stat: e[0] as MainStat, level: parseInt(_e[0], 10), choice: _e[1]! })));
const training = Object.entries(body.data.training).flatMap(e => Object.entries(e[1]).filter(e => e[1] !== undefined).map(_e => ({ character: id, stat: e[0] as MainStat, level: parseInt(_e[0], 10), choice: _e[1]! })));
if(training.length > 0) tx.insert(characterTrainingTable).values(training).run();
const abilities = Object.entries(body.data.abilities).map(e => ({ character: id, ability: e[0] as Ability, value: e[1] }));
const abilities = Object.entries(body.data.abilities).filter(e => e[1] !== undefined).map(e => ({ character: id, ability: e[0] as Ability, value: e[1] }));
if(abilities.length > 0) tx.insert(characterAbilitiesTable).values(abilities).run();
return id;

View File

@@ -1,35 +0,0 @@
import { and, eq, sql } from 'drizzle-orm';
import useDatabase from '~/composables/useDatabase';
import { characterTable } from '~/db/schema';
import type { CharacterVariables } 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 CharacterVariables;
}
setResponseStatus(e, 404);
return;
});

View File

@@ -1,7 +1,8 @@
import { eq } from 'drizzle-orm';
import useDatabase from '~/composables/useDatabase';
import { characterTable } from '~/db/schema';
import type { CharacterValues } from '~/types/character';
import { CharacterVariablesValidation } from '~/shared/character.util';
import type { CharacterVariables } from '~/types/character';
export default defineEventHandler(async (e) => {
const id = getRouterParam(e, "id");
@@ -11,11 +12,12 @@ export default defineEventHandler(async (e) => {
return;
}
const body = await readBody(e) as CharacterValues;
if(!body)
const body = await readValidatedBody(e, CharacterVariablesValidation.safeParse);
if(!body.success)
{
console.error(body.error);
setResponseStatus(e, 400);
return;
throw body.error;
}
const db = useDatabase();
@@ -35,8 +37,7 @@ export default defineEventHandler(async (e) => {
}
db.update(characterTable).set({
health: body.health,
mana: body.mana,
variables: body.data
}).where(eq(characterTable.id, parseInt(id, 10))).run();
setResponseStatus(e, 200);

View File

@@ -1,9 +1,9 @@
<template>
<div style='margin-left: auto; margin-right: auto; width: 75%; font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 1rem; line-height: 1.5rem; color: #171717;'>
<div style="margin-left: auto; margin-right: auto; text-align: center;">
<a href="https://d-any.com">
<a style="display: inline-block;" href="https://d-any.com">
<img src="https://d-any.com/logo.light.png" alt="Logo" title="d[any] logo" width="64" height="64" style="display: block; height: 4rem; width: 4rem; margin-left: auto; margin-right: auto;" />
<span style="margin-inline-end: 1rem; font-size: 1.5rem; line-height: 2rem; font-weight: 700; font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;">d[any]</span>
<span style="margin-inline-end: 1rem; font-size: 1.5rem; color: black; text-decoration: none; line-height: 2rem; font-weight: 700; font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;">d[any]</span>
</a>
</div>
<div style="padding: 1rem;">
@@ -11,6 +11,6 @@
</div>
</div>
<div style="background-color: #171717;">
<p style="padding-top: 1rem; padding-bottom: 1rem; text-align: center; font-size: 0.75rem; line-height: 1rem; color: #fff;">Copyright Peaceultime - d[any] - 2024</p>
<p style="padding-top: 1rem; padding-bottom: 1rem; text-align: center; font-size: 0.75rem; line-height: 1rem; color: #fff;">Copyright Peaceultime / d[any] - 2024 / 2025</p>
</div>
</template>

View File

@@ -17,10 +17,9 @@ export const templates: Record<string, { component: any, subject: string }> = {
import type Mail from 'nodemailer/lib/mailer';
interface MailPayload
{
type: 'mail'
to: string[]
template: string
data: Record<string, any>
to: string[];
template: string;
data: Record<string, any>;
}
const transport = nodemailer.createTransport({
@@ -55,51 +54,59 @@ if(process.env.NODE_ENV === 'production')
});
}
export default async function(e: TaskEvent) {
try {
if(e.payload.type !== 'mail')
{
throw new Error(`Données inconnues`);
export default defineTask({
meta: {
name: 'mail',
description: ''
},
run: async ({ payload, context }) => {
try {
if(payload.type !== 'mail')
{
throw new Error(`Données inconnues`);
}
const mailPayload = payload.data as MailPayload;
const template = templates[mailPayload.template];
console.log(mailPayload);
if(!template)
{
throw new Error(`Modèle de mail ${mailPayload.template} inconnu`);
}
console.time('Generating HTML');
const mail: Mail.Options = {
from: 'd[any] - Ne pas répondre <no-reply@peaceultime.com>',
to: mailPayload.to,
html: await render(template.component, mailPayload.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 };
}
const payload = e.payload as MailPayload;
const template = templates[payload.template];
if(!template)
catch(e)
{
throw new Error(`Modèle de mail ${payload.template} inconnu`);
console.error(e);
return { result: false, error: e };
}
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>
{