diff --git a/bun.lockb b/bun.lockb index 68e2969..017e16a 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/db.sqlite b/db.sqlite index 13b359a..99dc515 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/db.sqlite-shm b/db.sqlite-shm new file mode 100644 index 0000000..6be91d4 Binary files /dev/null and b/db.sqlite-shm differ diff --git a/db.sqlite-wal b/db.sqlite-wal index e69de29..fb0deeb 100644 Binary files a/db.sqlite-wal and b/db.sqlite-wal differ diff --git a/nuxt.config.ts b/nuxt.config.ts index f07bf3a..68c909f 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,5 +1,6 @@ // https://nuxt.com/docs/api/configuration/nuxt-config -import PluginVue from '@vitejs/plugin-vue'; +import vuePlugin from 'rollup-plugin-vue' +import postcssPlugin from 'rollup-plugin-postcss' export default defineNuxtConfig({ compatibilityDate: '2024-04-03', @@ -123,12 +124,21 @@ export default defineNuxtConfig({ }, ], nitro: { + alias: { + 'public': '//public', + }, + publicAssets: [{ + baseURL: 'public', + dir: 'public', + }], preset: 'bun', experimental: { tasks: true, }, rollupConfig: { - plugins: [ PluginVue() ], + plugins: [ + vuePlugin({ include: /\.vue$/, target: 'node' }) + ] }, }, runtimeConfig: { diff --git a/package.json b/package.json index 7e6efbb..09719e9 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.1", + "rollup-plugin-postcss": "^4.0.2", + "rollup-plugin-vue": "^6.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vue": "latest", diff --git a/pages/admin/index.vue b/pages/admin/index.vue index 9e4d728..9848673 100644 --- a/pages/admin/index.vue +++ b/pages/admin/index.vue @@ -81,9 +81,6 @@ async function fetch()
-
- -
@@ -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 @@ \ 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 @@ \ 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