+ $router.go(-1)">
+ Reinitialisation de mon mot de passe
+
+
+
+ Un mail vous a été envoyé si un compte existe pour cet identifiant.
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/(automatic)/resetting-password.vue b/pages/user/(automatic)/resetting-password.vue
new file mode 100644
index 0000000..e8ae7fd
--- /dev/null
+++ b/pages/user/(automatic)/resetting-password.vue
@@ -0,0 +1,87 @@
+
+
+ d[any] - Reinitialisation de mon mot de passe
+
+
+
+ $router.go(-1)">
+ Reinitialisation de mon mot de passe
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/changing-password.vue b/pages/user/changing-password.vue
new file mode 100644
index 0000000..5a7ff68
--- /dev/null
+++ b/pages/user/changing-password.vue
@@ -0,0 +1,88 @@
+
+
+ d[any] - Modification de mon mot de passe
+
+
+
+ $router.go(-1)">
+ Modification de mon mot de passe
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/login.vue b/pages/user/login.vue
index 22ce527..b44c8c2 100644
--- a/pages/user/login.vue
+++ b/pages/user/login.vue
@@ -11,6 +11,7 @@
+ Mot de passe oublié ?Pas de compte ?
diff --git a/pages/user/profile.vue b/pages/user/profile.vue
index 4fe637e..c6bacaa 100644
--- a/pages/user/profile.vue
+++ b/pages/user/profile.vue
@@ -12,7 +12,7 @@ async function revalidateUser()
{
loading.value = true;
await $fetch(`/api/users/${user.value?.id}/revalidate`, {
- method: 'get'
+ method: 'post'
});
loading.value = false;
toaster.add({ closeable: false, duration: 10000, timer: true, content: 'Un mail vous a été envoyé.', type: 'info' });
@@ -54,7 +54,7 @@ async function deleteUser()
-
+
@@ -17,10 +17,11 @@
import { computed } from 'vue';
import Bun from 'bun';
-const { id, username, timestamp } = defineProps<{
+const { id, userId, username, timestamp } = defineProps<{
id: number
+ userId: number
username: string
timestamp: number
}>();
-const hash = computed(() => Bun.hash(id.toString(), timestamp));
+const hash = computed(() => Bun.hash('1' + userId.toString(), timestamp));
\ No newline at end of file
diff --git a/server/components/mail/reset-password.vue b/server/components/mail/reset-password.vue
new file mode 100644
index 0000000..d0fcf0d
--- /dev/null
+++ b/server/components/mail/reset-password.vue
@@ -0,0 +1,29 @@
+
+
+
Bonjour {{ username }}.
+
Vous avez demandé à réinitialiser votre mot de passe aujourd'hui à {{ format(new Date(timestamp), 'HH:mm') }}.
+ Si vous n'êtes pas à l'origine de cette demande, vous n'avez pas à modifier votre mot de passe, ce dernier est conservé tant que vous n'interagissez pas avec le lien ci-dessus.
+
+
+
+
\ No newline at end of file
diff --git a/server/routes/user/mailvalidation.get.ts b/server/routes/user/mailvalidation.get.ts
index da6c030..ed31af8 100644
--- a/server/routes/user/mailvalidation.get.ts
+++ b/server/routes/user/mailvalidation.get.ts
@@ -1,10 +1,11 @@
-import { eq } from "drizzle-orm";
+import { eq, getTableColumns, lte } from "drizzle-orm";
import { z } from "zod";
import useDatabase from "~/composables/useDatabase";
-import { usersTable } from "~/db/schema";
+import { emailValidationTable, usersTable } from "~/db/schema";
const schema = z.object({
h: z.coerce.string(),
+ i: z.coerce.string(),
u: z.coerce.number(),
t: z.coerce.number(),
});
@@ -15,22 +16,33 @@ export default defineEventHandler(async (e) => {
if(!query.success)
throw query.error;
- if(Bun.hash(query.data.u.toString(), query.data.t).toString() !== query.data.h)
+ if(Bun.hash('1' + 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 validate = db.select(getTableColumns(emailValidationTable)).from(emailValidationTable).where(eq(emailValidationTable.id, query.data.i)).get();
+
+ if(!validate || validate.timestamp <= new Date())
+ {
+ return createError({
+ statusCode: 400,
+ message: 'Le lien a expiré',
+ });
+ }
+
+ db.delete(emailValidationTable).where(lte(emailValidationTable.timestamp, new Date())).run();
const result = db.select({ state: usersTable.state }).from(usersTable).where(eq(usersTable.id, query.data.u)).get();
if(result === undefined)
@@ -38,14 +50,14 @@ export default defineEventHandler(async (e) => {
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();
diff --git a/server/tasks/mail.ts b/server/tasks/mail.ts
index bd22a49..c203f59 100644
--- a/server/tasks/mail.ts
+++ b/server/tasks/mail.ts
@@ -3,28 +3,24 @@ import { createSSRApp, h } from 'vue';
import { renderToString } from 'vue/server-renderer';
import base from '../components/mail/base.vue';
-import registration from '../components/mail/registration.vue';
-//import revalidation from '../components/mail/revalidation.vue';
+import Registration from '../components/mail/registration.vue';
+import ResetPassword from '../components/mail/reset-password.vue';
const config = useRuntimeConfig();
const [domain, selector, dkim] = config.mail.dkim.split(":");
export const templates: Record = {
- "registration": { component: registration, subject: 'Bienvenue sur d[any] 😎' },
-// "revalidate-mail": { component: revalidation, subject: 'd[any]: Valider votre email' },
+ "registration": { component: Registration, subject: 'Bienvenue sur d[any] 😎' },
+ "reset-password": { component: ResetPassword, subject: 'Réinitialisation de votre mot de passe' },
};
-import 'nitropack/types';
import type Mail from 'nodemailer/lib/mailer';
-declare module 'nitropack/types'
+interface MailPayload
{
- interface TaskPayload
- {
- type: 'mail'
- to: string[]
- template: string
- data: Record
- }
+ type: 'mail'
+ to: string[]
+ template: string
+ data: Record
}
const transport = nodemailer.createTransport({
@@ -57,7 +53,7 @@ export default defineTask({
throw new Error(`Données inconnues`);
}
- const payload = e.payload;
+ const payload = e.payload as MailPayload;
const template = templates[payload.template];
if(!template)
diff --git a/server/tasks/validation.ts b/server/tasks/validation.ts
new file mode 100644
index 0000000..2877499
--- /dev/null
+++ b/server/tasks/validation.ts
@@ -0,0 +1,39 @@
+import { lt } from "drizzle-orm";
+import { emailValidationTable } from "~/db/schema";
+import useDatabase from '~/composables/useDatabase';
+
+interface ValidationPayload
+{
+ type: 'validation'
+ id: string
+ timestamp: number
+}
+
+export default defineTask({
+ meta: {
+ name: 'validation',
+ description: 'Add email ID to DB',
+ },
+ async run(e) {
+ try {
+ if(e.payload.type !== 'validation')
+ {
+ throw new Error(`Données inconnues`);
+ }
+
+ const payload = e.payload as ValidationPayload;
+ const db = useDatabase();
+
+ db.delete(emailValidationTable).where(lt(emailValidationTable.timestamp, new Date())).run();
+ db.insert(emailValidationTable).values({ id: payload.id, timestamp: new Date(payload.timestamp) }).run();
+
+ return { result: true };
+ }
+ catch(e)
+ {
+ console.error(e);
+
+ return { result: false, error: e };
+ }
+ },
+})
\ No newline at end of file
diff --git a/todo.md b/todo.md
index 00871d8..1a28a2a 100644
--- a/todo.md
+++ b/todo.md
@@ -1,4 +1,5 @@
-- [ ] Rename auto des liens au changement de path
+- [x] Mot de passe oublié
+- [x] Rename auto des liens au changement de path
- [ ] Autocomplete des liens dans l'editeur
- [ ] Editeur de graphe
- [ ] Filtrage de lien avec le header id