Added account update form and e-mail verification.

This commit is contained in:
2026-07-22 05:49:39 +02:00
parent 17d34f6a80
commit 9b058fd44b
19 changed files with 1108 additions and 233 deletions

View File

@@ -3,11 +3,10 @@ import { CronJob } from 'cron';
import { add } from 'date-fns';
import escapeRegexp from 'escape-string-regexp';
import markdownIt from 'markdown-it';
import { Resend } from 'resend';
import promiseProps from '../utils/promise-props.js';
import { getIdsBySlug } from './cache.js';
import { emailTemplates } from './email-templates.js';
import { sendEmails } from './email.js';
import { HttpError } from './errors.js';
import { knexOwner as knex } from './knex.js';
import initLogger from './logger.js';
@@ -15,18 +14,6 @@ import { indexApi } from './manticore.js';
import { fetchScenesById } from './scenes.js';
const logger = initLogger();
const resend = (() => {
if (config.email.enabled) {
try {
return new Resend(config.email.apiKey);
}
catch (error) {
logger.error(`Failed to initialize Resend: ${error.message}`);
}
}
return null;
})();
function getImagePath(media) {
if (!media) {
@@ -452,11 +439,6 @@ async function sendEmailAlerts() {
return;
}
if (!resend) {
logger.warn('E-mails are enabled, but Resend is not initialized');
return;
}
const qualifyingUsers = await knex('notifications')
.select('users.id as user_id', 'users.settings', knex.raw('max(emailed_at) as last_email'))
.where('notifications.email', true)
@@ -546,7 +528,7 @@ async function sendEmailAlerts() {
&& !!notification.scene
&& notification.user.settings?.alertEmailsEnabled !== false); // keep the e-mails marked as 'sent', if emails get re-enabled there shouldn't be a barrage of old notifications
const emails = await Promise.all(Object.values(usersByUserId).map(async (user) => {
const emailItems = Object.values(usersByUserId).map((user) => {
const userTriggers = Object.values(Object.fromEntries(triggers
.filter((trigger) => trigger.user.id === user.id)
.map((trigger) => [trigger.scene.id, trigger])))
@@ -556,42 +538,24 @@ async function sendEmailAlerts() {
return null;
}
try {
const { html, text } = await emailTemplates.render('alert.vue', {
props: {
user,
triggers: userTriggers,
address: config.web.address,
},
});
return {
template: 'alert',
to: user.email,
subject: `Notification for ${userTriggers.length} scenes on traxxx`,
props: {
user,
triggers: userTriggers,
address: config.web.address,
},
};
}).filter(Boolean);
return {
from: config.email.from,
to: user.email,
subject: `Notification for ${userTriggers.length} scenes on traxxx`,
html,
text,
};
}
catch (error) {
logger.warn(`Failed to render e-mail for '${user.username}' with scenes ${userTriggers.map((trigger) => `${trigger.notificationId}: ${trigger.scene.id}`)}: ${error.message}`);
return null;
}
})).then((allEmails) => allEmails.filter(Boolean));
if (emails.length === 0) {
if (emailItems.length === 0) {
logger.info(`No alert e-mails sent`);
return;
}
const { data, error } = await resend.batch.send(emails);
if (error) {
logger.error(`Failed to send ${emails.length} alert e-mails (${error.statusCode} ${error.name}): ${error.message}`);
}
else {
logger.info(`Sent ${data.data.length}/${emails.length} alert e-mails`);
}
await sendEmails(emailItems);
}
export async function notify(sceneIds, options = {}) {