You've already forked obsidian-visualiser
Add mail template, mail HTML generation and a few UI fixes
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import { hasPermissions } from "#shared/auth.util";
|
||||
|
||||
declare module 'nitropack'
|
||||
{
|
||||
interface TaskPayload
|
||||
{
|
||||
type: string
|
||||
}
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const session = await getUserSession(e);
|
||||
|
||||
@@ -16,6 +24,9 @@ export default defineEventHandler(async (e) => {
|
||||
setResponseStatus(e, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
payload.type = id;
|
||||
payload.data = JSON.parse(payload.data);
|
||||
|
||||
const result = await runTask(id, {
|
||||
payload: payload
|
||||
|
||||
20
server/components/mail/base.vue
Normal file
20
server/components/mail/base.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<html>
|
||||
<body style="min-width: 1000px;">
|
||||
<div style="user-select: none; font-size: medium; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; width: 70%; margin: auto;">
|
||||
<div style="margin: auto;">
|
||||
<a href="https://obsidian.peaceultime.com" style="display: inline-block; ">
|
||||
<span style="font-size: 25px; font-weight: bolder;">d[any]</span>
|
||||
<img src="cid:logo.obsidian.peaceultime.com" style="width: 64px; height: 64px; display: inline;" alt="Logo" width="64" height="64"/>
|
||||
</a>
|
||||
</div>
|
||||
<div style="width: 70%; padding: 1em 0;">
|
||||
<slot />
|
||||
</div>
|
||||
<footer style="color: #707070; font-style: italic; display: flex; justify-content: center; align-items: center; padding: 1em 0;">
|
||||
<p>Copyright Peaceultime - d[any] - 2024</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</template>
|
||||
22
server/components/mail/registration.vue
Normal file
22
server/components/mail/registration.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<p style="font-size: 19px; font-variant: small-caps;">Bienvenue sur d[any], <span style="">{{ username }}</span>.</p>
|
||||
<p>Nous vous invitons à valider votre compte afin de profiter de toutes les fonctionnalités de d[any].</p>
|
||||
<div style="display: flex; justify-content: center; align-items: center; padding: 1em 0;">
|
||||
<a :href="`https://obsidian.peaceultime.com/user/mail-validation?u=${id}&t=${timestamp}&h=${0}`" target="_blank" style="border: 1px solid #999; padding: 1px 4px; background-color: #eee; font-weight: lighter; font-size: large">Je valide mon compte</a>
|
||||
</div>
|
||||
<div>
|
||||
<span>Vous pouvez egalement copier le lien suivant pour valider votre compte: </span>
|
||||
<pre style="border-bottom: 1px solid #999; font-size: small; display: inline-block; user-select: all">{{ `https://obsidian.peaceultime.com/user/mail-validation?u=${id}&t=${timestamp}&h=${0}` }}</pre>
|
||||
</div>
|
||||
<span style="font-size: small; padding-left: 8px;">Ce lien est valable 1 heure.</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { id, username, timestamp } = defineProps<{
|
||||
id: number
|
||||
username: string
|
||||
timestamp: number
|
||||
}>();
|
||||
</script>
|
||||
20
server/components/mail/revalidation.vue
Normal file
20
server/components/mail/revalidation.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<img />
|
||||
<p>Bienvenue sur d[any], <span>{{ username }}</span>.</p>
|
||||
</div>
|
||||
<p class="">Nous vous invitons à valider votre compte en cliquant <a :href="`https://obsidian.peaceultime.com/user/mail-validation?u=${id}&t=${timestamp}&h=${hash(id.toString(), timestamp)}`"><Button>ici</Button></a> afin de profiter de toutes les fonctionnalités de d[any]</p>
|
||||
<p class="">Vous pouvez egalement copier le lien suivant pour valider votre compte: {{ `https://obsidian.peaceultime.com/user/mail-validation?u=${id}&t=${timestamp}&h=${hash(id.toString(), timestamp)}` }}</p>
|
||||
<span>Ce lien est valable 1 heure.</span>
|
||||
<footer></footer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { hash } from 'bun';
|
||||
|
||||
const { id, username, timestamp } = defineProps<{
|
||||
id: number
|
||||
username: string
|
||||
timestamp: number
|
||||
}>();
|
||||
</script>
|
||||
@@ -1,9 +1,34 @@
|
||||
import nodemailer from 'nodemailer';
|
||||
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';
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
const [domain, selector, dkim] = config.mail.dkim.split(":");
|
||||
|
||||
export const templates: Record<string, { component: any, subject: string }> = {
|
||||
"registration": { component: registration, subject: 'Bienvenue sur d[any] 😎' },
|
||||
"revalidate-mail": { component: revalidation, subject: 'd[any]: Valider votre email' },
|
||||
};
|
||||
|
||||
import 'nitropack/types';
|
||||
import type Registration from '../components/mail/registration.vue';
|
||||
declare module 'nitropack/types'
|
||||
{
|
||||
interface TaskPayload
|
||||
{
|
||||
type: 'mail'
|
||||
to: string[]
|
||||
template: string
|
||||
data: Record<string, any>
|
||||
}
|
||||
}
|
||||
|
||||
const transport = nodemailer.createTransport({
|
||||
//@ts-ignore
|
||||
pool: true,
|
||||
host: config.mail.host,
|
||||
port: config.mail.port,
|
||||
@@ -27,13 +52,35 @@ export default defineTask({
|
||||
},
|
||||
async run(e) {
|
||||
try {
|
||||
const payload: { to: string[], message: string, subject: string } = e.payload;
|
||||
const status = await transport.sendMail({
|
||||
from: 'Message automatique d[any] <no-reply@peaceultime.com>',
|
||||
if(e.payload.type !== 'mail')
|
||||
{
|
||||
throw new Error(`Données inconnues`);
|
||||
}
|
||||
|
||||
const payload = e.payload;
|
||||
const template = templates[payload.template];
|
||||
|
||||
if(!template)
|
||||
{
|
||||
throw new Error(`Modèle de mail ${payload.template} inconnu`);
|
||||
}
|
||||
|
||||
const mail = {
|
||||
from: 'd[any] - Ne pas répondre <no-reply@peaceultime.com>',
|
||||
to: payload.to,
|
||||
text: payload.message,
|
||||
subject: payload.subject,
|
||||
});
|
||||
html: await render(template.component, payload.data),
|
||||
subject: template.subject,
|
||||
attachments: [{
|
||||
filename: 'logo.svg',
|
||||
path: '../../public/logo.dark.svg',
|
||||
cid: 'logo.obsidian.peaceultime.com',
|
||||
}]
|
||||
};
|
||||
|
||||
if(mail.html === '')
|
||||
return { result: false, error: new Error("Invalid content") };
|
||||
|
||||
const status = await transport.sendMail(mail);
|
||||
|
||||
if(status.rejected.length > 0)
|
||||
{
|
||||
@@ -47,4 +94,15 @@ export default defineTask({
|
||||
return { result: false, error: e };
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
async function render(component: any, data: Record<string, any>): Promise<string>
|
||||
{
|
||||
const app = createSSRApp({
|
||||
render(){
|
||||
return h(base, null, { default: () => h(component, data, []) });
|
||||
}
|
||||
});
|
||||
|
||||
return await renderToString(app);
|
||||
}
|
||||
Reference in New Issue
Block a user