1- import { createTransport , type Transporter } from 'nodemailer' ;
1+ import { email , appUrl } from '../config' ;
2+
3+ interface ScalewayEmailRequest {
4+ from : { email : string ; name ?: string } ;
5+ to : { email : string ; name ?: string } [ ] ;
6+ subject : string ;
7+ text : string ;
8+ html : string ;
9+ project_id : string ;
10+ }
211
3- import { smtp , appUrl } from '../config' ;
12+ async function sendWithScaleway ( to : string , subject : string , text : string , html : string ) : Promise < boolean > {
13+ if ( ! email . secretKey || ! email . projectId ) {
14+ console . error ( '[Email] Scaleway not configured (missing SCW_SECRET_KEY or SCW_PROJECT_ID)' ) ;
15+ return false ;
16+ }
417
5- let transporter : Transporter | null = null ;
18+ const url = `https://api.scaleway.com/transactional-email/v1alpha1/regions/ ${ email . region } /emails` ;
619
7- function getTransporter ( ) : Transporter | null {
8- if ( ! smtp . host ) {
9- return null ;
10- }
20+ const body : ScalewayEmailRequest = {
21+ from : { email : email . from } ,
22+ to : [ { email : to } ] ,
23+ subject,
24+ text,
25+ html,
26+ project_id : email . projectId
27+ } ;
1128
12- if ( ! transporter ) {
13- transporter = createTransport ( {
14- host : smtp . host ,
15- port : smtp . port ,
16- secure : smtp . port === 465 ,
17- auth : smtp . user && smtp . pass ? {
18- user : smtp . user ,
19- pass : smtp . pass
20- } : undefined
29+ try {
30+ const res = await fetch ( url , {
31+ method : 'POST' ,
32+ headers : {
33+ 'X-Auth-Token' : email . secretKey ,
34+ 'Content-Type' : 'application/json'
35+ } ,
36+ body : JSON . stringify ( body )
2137 } ) ;
22- }
2338
24- return transporter ;
39+ if ( ! res . ok ) {
40+ const error = await res . text ( ) ;
41+ console . error ( '[Email] Scaleway API error:' , res . status , error ) ;
42+ return false ;
43+ }
44+
45+ return true ;
46+ } catch ( error ) {
47+ console . error ( '[Email] Failed to send email:' , error ) ;
48+ return false ;
49+ }
2550}
2651
2752export function isEmailConfigured ( ) : boolean {
28- return ! ! smtp . host ;
53+ return email . provider === 'scaleway' && ! ! email . secretKey && ! ! email . projectId ;
2954}
3055
31- export async function sendSetPasswordEmail ( email : string , token : string ) : Promise < boolean > {
32- const transport = getTransporter ( ) ;
33-
34- if ( ! transport ) {
35- console . log ( `[Email] Set password link for ${ email } : ${ appUrl } /sign-in/set-password?token=${ token } ` ) ;
56+ export async function sendSetPasswordEmail ( emailTo : string , token : string ) : Promise < boolean > {
57+ if ( ! isEmailConfigured ( ) ) {
58+ console . log ( `[Email] Set password link for ${ emailTo } : ${ appUrl } /sign-in/set-password?token=${ token } ` ) ;
3659 return true ;
3760 }
3861
39- try {
40- await transport . sendMail ( {
41- from : smtp . from ,
42- to : email ,
43- subject : 'Complete your OpenWorkers registration' ,
44- text : `Welcome to OpenWorkers!
62+ const subject = 'Complete your OpenWorkers registration' ;
63+
64+ const text = `Welcome to OpenWorkers!
4565
4666Click the link below to set your password and complete your registration:
4767
4868${ appUrl } /sign-in/set-password?token=${ token }
4969
5070This link expires in 24 hours.
5171
52- If you didn't create an account, you can ignore this email.` ,
53- html : `
72+ If you didn't create an account, you can ignore this email.` ;
73+
74+ const html = `
5475<!DOCTYPE html>
5576<html>
5677<head>
@@ -70,39 +91,30 @@ If you didn't create an account, you can ignore this email.`,
7091 <p class="footer">This link expires in 24 hours. If you didn't create an account, you can ignore this email.</p>
7192 </div>
7293</body>
73- </html>`
74- } ) ;
94+ </html>` ;
7595
76- return true ;
77- } catch ( error ) {
78- console . error ( '[Email] Failed to send set password email:' , error ) ;
79- return false ;
80- }
96+ return sendWithScaleway ( emailTo , subject , text , html ) ;
8197}
8298
83- export async function sendPasswordResetEmail ( email : string , token : string ) : Promise < boolean > {
84- const transport = getTransporter ( ) ;
85-
86- if ( ! transport ) {
87- console . log ( `[Email] Password reset link for ${ email } : ${ appUrl } /sign-in/reset-password?token=${ token } ` ) ;
99+ export async function sendPasswordResetEmail ( emailTo : string , token : string ) : Promise < boolean > {
100+ if ( ! isEmailConfigured ( ) ) {
101+ console . log ( `[Email] Password reset link for ${ emailTo } : ${ appUrl } /sign-in/reset-password?token=${ token } ` ) ;
88102 return true ;
89103 }
90104
91- try {
92- await transport . sendMail ( {
93- from : smtp . from ,
94- to : email ,
95- subject : 'Reset your OpenWorkers password' ,
96- text : `You requested a password reset for your OpenWorkers account.
105+ const subject = 'Reset your OpenWorkers password' ;
106+
107+ const text = `You requested a password reset for your OpenWorkers account.
97108
98109Click the link below to reset your password:
99110
100111${ appUrl } /sign-in/reset-password?token=${ token }
101112
102113This link expires in 1 hour.
103114
104- If you didn't request this, you can ignore this email.` ,
105- html : `
115+ If you didn't request this, you can ignore this email.` ;
116+
117+ const html = `
106118<!DOCTYPE html>
107119<html>
108120<head>
@@ -122,12 +134,7 @@ If you didn't request this, you can ignore this email.`,
122134 <p class="footer">This link expires in 1 hour. If you didn't request this, you can ignore this email.</p>
123135 </div>
124136</body>
125- </html>`
126- } ) ;
137+ </html>` ;
127138
128- return true ;
129- } catch ( error ) {
130- console . error ( '[Email] Failed to send password reset email:' , error ) ;
131- return false ;
132- }
139+ return sendWithScaleway ( emailTo , subject , text , html ) ;
133140}
0 commit comments