From 4b913eb53f172d4f3da0fc10be6450b66ab9cb68 Mon Sep 17 00:00:00 2001 From: Rokojori Date: Tue, 14 Jul 2026 15:45:27 +0200 Subject: [PATCH] Email Notifier Update --- source/server/email/EmailSender.ts | 4 +++ source/server/email/EmailService.ts | 22 +++++++++++++ source/server/email/SMTPEmailSender.ts | 45 ++++++++++++++++++++++++++ source/server/index.ts | 12 ++++++- source/server/routes/deploy.ts | 10 +++++- 5 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 source/server/email/EmailSender.ts create mode 100644 source/server/email/EmailService.ts create mode 100644 source/server/email/SMTPEmailSender.ts diff --git a/source/server/email/EmailSender.ts b/source/server/email/EmailSender.ts new file mode 100644 index 0000000..01e5f14 --- /dev/null +++ b/source/server/email/EmailSender.ts @@ -0,0 +1,4 @@ +export interface EmailSender +{ + sendEmail( to: string, subject: string, body: string ): Promise; +} diff --git a/source/server/email/EmailService.ts b/source/server/email/EmailService.ts new file mode 100644 index 0000000..6a3574f --- /dev/null +++ b/source/server/email/EmailService.ts @@ -0,0 +1,22 @@ +import { EmailSender } from './EmailSender'; +import { SMTPEmailSender } from './SMTPEmailSender'; + +export class EmailService +{ + static readonly reportEmail = 'josef@rokojori.com'; + + private static sender: EmailSender = new SMTPEmailSender( + { + host: process.env.SMTP_HOST ?? '', + port: Number( process.env.SMTP_PORT ?? 587 ), + secure: process.env.SMTP_SECURE === 'true', + user: process.env.SMTP_USER ?? '', + pass: process.env.SMTP_PASS ?? '', + from: process.env.SMTP_FROM ?? '' + } ); + + static async sendEmail( to: string, subject: string, body: string ): Promise + { + return EmailService.sender.sendEmail( to, subject, body ); + } +} diff --git a/source/server/email/SMTPEmailSender.ts b/source/server/email/SMTPEmailSender.ts new file mode 100644 index 0000000..f907a9e --- /dev/null +++ b/source/server/email/SMTPEmailSender.ts @@ -0,0 +1,45 @@ +import nodemailer from 'nodemailer'; +import { EmailSender } from './EmailSender'; + +export interface SMTPConfig +{ + host: string; + port: number; + secure: boolean; + user: string; + pass: string; + from: string; +} + +export class SMTPEmailSender implements EmailSender +{ + private transporter: nodemailer.Transporter; + private from: string; + + constructor( config: SMTPConfig ) + { + this.from = config.from; + this.transporter = nodemailer.createTransport( + { + host: config.host, + port: config.port, + secure: config.secure, + auth: + { + user: config.user, + pass: config.pass + } + } ); + } + + async sendEmail( to: string, subject: string, body: string ): Promise + { + await this.transporter.sendMail( + { + from: this.from, + to, + subject, + text: body + } ); + } +} diff --git a/source/server/index.ts b/source/server/index.ts index c73172a..3b87fdf 100644 --- a/source/server/index.ts +++ b/source/server/index.ts @@ -11,6 +11,7 @@ import layoutRouter from './routes/layout'; import rojosRouter from './routes/rojos'; import { generateLocales } from './localeGenerator'; import deployRouter from './routes/deploy'; +import { EmailService } from './email/EmailService'; generateLocales(); @@ -36,7 +37,16 @@ app.get( '/api/auth/me', requireAuth, ( req, res ) => res.json( req.user ) ); app.get( '/', ( _req, res ) => res.redirect( '/dashboard.html' ) ); export function startServer( port: number ): void { - app.listen( port, () => console.log( `Roject running on http://localhost:${port}` ) ); + app.listen( port, () => + { + const time = new Date().toISOString(); + console.log( `Roject running on http://localhost:${port}` ); + EmailService.sendEmail( + EmailService.reportEmail, + 'Roject — server started', + `Server started at ${time} on port ${port}.` + ).catch( () => {} ); + } ); } if ( require.main === module ) { diff --git a/source/server/routes/deploy.ts b/source/server/routes/deploy.ts index 88dc788..843f157 100644 --- a/source/server/routes/deploy.ts +++ b/source/server/routes/deploy.ts @@ -2,6 +2,7 @@ import { Router } from 'express'; import express from 'express'; import { spawn } from 'child_process'; import { createHmac, timingSafeEqual } from 'crypto'; +import { EmailService } from '../email/EmailService'; const router = Router(); @@ -46,7 +47,14 @@ router.post( '/', express.raw( { type: 'application/json' } ), ( req, res ) => { return; } - console.log( `[deploy] triggered by push from ${payload.pusher?.login ?? 'unknown'}` ); + const pusher = payload.pusher?.login ?? 'unknown'; + const time = new Date().toISOString(); + console.log( `[deploy] triggered by push from ${pusher}` ); + EmailService.sendEmail( + EmailService.reportEmail, + 'Roject — deploy requested', + `Deploy triggered at ${time} by ${pusher}.` + ).catch( () => {} ); res.json( { message: 'Deploy triggered' } ); triggerDeploy(); } );