-
Votre mot de passe doit respecter les critères de sécurité suivants
- :
-
Entre 8 et 128
- caractères
-
Au moins
- une minuscule et une majuscule
-
Au moins un
- chiffre
-
Au moins un
- caractère spécial parmi la liste suivante:
- ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~
-
+
+ Prérequis de sécurité
+ 8 à 128 caractères
+ Une minuscule
+ Une majuscule
+ Un chiffre
+ Un caractère special
@@ -52,7 +46,8 @@ const { add: addToast, clear: clearToasts } = useToast();
const confirmPassword = ref("");
const checkedLength = computed(() => state.password.length >= 8 && state.password.length <= 128);
-const checkedLowerUpper = computed(() => state.password.toLowerCase() !== state.password && state.password.toUpperCase() !== state.password);
+const checkedLower = computed(() => state.password.toUpperCase() !== state.password);
+const checkedUpper = computed(() => state.password.toLowerCase() !== state.password);
const checkedDigit = computed(() => /[0-9]/.test(state.password));
const checkedSymbol = computed(() => " !\"#$%&'()*+,-./:;<=>?@[]^_`{|}~".split("").some(e => state.password.includes(e)));
diff --git a/server/api/auth/register.post.ts b/server/api/auth/register.post.ts
index e029c21..4e9cf83 100644
--- a/server/api/auth/register.post.ts
+++ b/server/api/auth/register.post.ts
@@ -73,6 +73,19 @@ export default defineEventHandler(async (e): Promise
=> {
logSession(e, await setUserSession(e, { user: { id: id.id, username: body.data.username, email: body.data.email, state: 0, signin: new Date(), permissions: [] } }) as UserSessionRequired);
+ runTask('mail', {
+ payload: {
+ type: 'mail',
+ to: [body.data.email],
+ template: 'registration',
+ data: {
+ username: body.data.username,
+ timestamp: Date.now(),
+ id: id.id,
+ }
+ }
+ });
+
setResponseStatus(e, 201);
return { success: true, session };
}
diff --git a/server/components/mail/base.vue b/server/components/mail/base.vue
index 31212ab..b7f1235 100644
--- a/server/components/mail/base.vue
+++ b/server/components/mail/base.vue
@@ -1,20 +1,16 @@
-
-
-
-
-
+
+
+
Copyright Peaceultime - d[any] - 2024
+
\ No newline at end of file
diff --git a/server/components/mail/registration.vue b/server/components/mail/registration.vue
index ce83b75..699ba81 100644
--- a/server/components/mail/registration.vue
+++ b/server/components/mail/registration.vue
@@ -1,22 +1,26 @@
-
-
Bienvenue sur d[any], {{ username }}.
+
+
Bienvenue sur d[any], {{ username }}.
Nous vous invitons à valider votre compte afin de profiter de toutes les fonctionnalités de d[any].
-
-
Je valide mon compte
+
Vous pouvez egalement copier le lien suivant pour valider votre compte:
-
{{ `https://obsidian.peaceultime.com/user/mail-validation?u=${id}&t=${timestamp}&h=${0}` }}
+
{{ `https://obsidian.peaceultime.com/user/mailvalidation?u=${id}&t=${timestamp}&h=${hash}` }}
-
Ce lien est valable 1 heure.
\ No newline at end of file
diff --git a/server/routes/user/mailvalidation.get.ts b/server/routes/user/mailvalidation.get.ts
new file mode 100644
index 0000000..da6c030
--- /dev/null
+++ b/server/routes/user/mailvalidation.get.ts
@@ -0,0 +1,54 @@
+import { eq } from "drizzle-orm";
+import { z } from "zod";
+import useDatabase from "~/composables/useDatabase";
+import { usersTable } from "~/db/schema";
+
+const schema = z.object({
+ h: z.coerce.string(),
+ u: z.coerce.number(),
+ t: z.coerce.number(),
+});
+
+export default defineEventHandler(async (e) => {
+ const query = await getValidatedQuery(e, schema.safeParse);
+
+ if(!query.success)
+ throw query.error;
+
+ if(Bun.hash(query.data.u.toString(), query.data.t).toString() !== query.data.h)
+ {
+ return createError({
+ statusCode: 400,
+ message: 'Lien incorrect',
+ })
+ }
+ if(Date.now() > query.data.t + (60 * 60 * 1000))
+ {
+ return createError({
+ statusCode: 400,
+ message: 'Le lien a expiré',
+ })
+ }
+
+ const db = useDatabase();
+ const result = db.select({ state: usersTable.state }).from(usersTable).where(eq(usersTable.id, query.data.u)).get();
+
+ if(result === undefined)
+ {
+ return createError({
+ statusCode: 400,
+ message: 'Aucune donnée utilisateur trouvée',
+ })
+ }
+ if(result?.state === 1)
+ {
+ return createError({
+ statusCode: 400,
+ message: 'Votre compte a déjà été validé',
+ })
+ }
+
+ db.update(usersTable).set({ state: 1 }).where(eq(usersTable.id, query.data.u)).run();
+
+ sendRedirect(e, '/user/mailvalidated');
+})
\ No newline at end of file
diff --git a/server/tasks/mail.ts b/server/tasks/mail.ts
index 897a76e..a0926ba 100644
--- a/server/tasks/mail.ts
+++ b/server/tasks/mail.ts
@@ -1,6 +1,9 @@
import nodemailer from 'nodemailer';
import { createSSRApp, h } from 'vue';
import { renderToString } from 'vue/server-renderer';
+import postcss from 'postcss';
+import tailwindcss from 'tailwindcss';
+import { join } from 'node:path';
import base from '../components/mail/base.vue';
import registration from '../components/mail/registration.vue';
@@ -15,7 +18,7 @@ export const templates: Record
= {
};
import 'nitropack/types';
-import type Registration from '../components/mail/registration.vue';
+import type Mail from 'nodemailer/lib/mailer';
declare module 'nitropack/types'
{
interface TaskPayload
@@ -65,22 +68,22 @@ export default defineTask({
throw new Error(`Modèle de mail ${payload.template} inconnu`);
}
- const mail = {
+ console.time('Generating HTML');
+ const mail: Mail.Options = {
from: 'd[any] - Ne pas répondre ',
to: payload.to,
html: await render(template.component, payload.data),
subject: template.subject,
- attachments: [{
- filename: 'logo.svg',
- path: '../../public/logo.dark.svg',
- cid: 'logo.obsidian.peaceultime.com',
- }]
+
};
+ 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)
{
@@ -100,9 +103,11 @@ async function render(component: any, data: Record): Promise h(component, data, []) });
+ return h(base, null, { default: () => h(component, data, { default: () => null }) });
}
});
- return await renderToString(app);
+ const html = await renderToString(app);
+
+ return (`${html}
`);
}
\ No newline at end of file