diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts new file mode 100644 index 0000000..03300da --- /dev/null +++ b/client/src/lib/api.ts @@ -0,0 +1,34 @@ +export async function sendAuthCode(payload: { + email?: string; + phoneNumber?: string; + firstName?: string; + lastName?: string; +}) { + const res = await fetch("/api/auth/send-code", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ message: "Failed to send code" })); + throw new Error(err.message || "Failed to send code"); + } + return res.json(); +} + +export async function verifyAuthCode(payload: { + email?: string; + phoneNumber?: string; + code: string; +}) { + const res = await fetch("/api/auth/verify-code", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({ message: "Failed to verify code" })); + throw new Error(err.message || "Failed to verify code"); + } + return res.json(); +} \ No newline at end of file diff --git a/server/email-service.ts b/server/email-service.ts index 2540559..8d825b0 100644 --- a/server/email-service.ts +++ b/server/email-service.ts @@ -1,120 +1,127 @@ -// SendGrid email service integration -import sgMail from '@sendgrid/mail'; +import nodemailer from "nodemailer"; -class EmailService { - private initialized = false; +type SendResult = { success: boolean; info?: any }; - constructor() { - this.initialize(); - } +const SMTP_HOST = process.env.SMTP_HOST; +const SMTP_PORT = process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : undefined; +const SMTP_USER = process.env.SMTP_USER; +const SMTP_PASS = process.env.SMTP_PASS; +const SMTP_SECURE = process.env.SMTP_SECURE === "true"; - private initialize() { - try { - const apiKey = process.env.SENDGRID_API_KEY; - - if (!apiKey) { - console.log('SendGrid API key not found - email verification disabled'); - console.log('Set SENDGRID_API_KEY environment variable to enable email verification'); - return; - } +let transporter: nodemailer.Transporter | null = null; - if (!apiKey.startsWith('SG.')) { - console.warn('Warning: SendGrid API key format appears invalid (should start with "SG.")'); - } +if (SMTP_HOST && SMTP_PORT && SMTP_USER && SMTP_PASS) { + transporter = nodemailer.createTransport({ + host: SMTP_HOST, + port: SMTP_PORT, + secure: SMTP_SECURE || SMTP_PORT === 465, + auth: { + user: SMTP_USER, + pass: SMTP_PASS, + }, + }); +} - sgMail.setApiKey(apiKey); - this.initialized = true; - console.log('✓ SendGrid email service initialized successfully'); - } catch (error) { - console.error('Failed to initialize SendGrid:', error); - } - } +function verificationEmailHtml(code: string, firstName?: string) { + const name = firstName ? `Hi ${firstName},` : "Hello,"; + return ` +
+

${name}

+

Your verification code for Instant Plumber Connect is:

+

${code}

+

This code expires in 10 minutes. If you did not request this, please ignore this email.

+
+

If you have trouble signing in, contact support.

+
+ `; +} - isReady(): boolean { - return this.initialized; - } +function welcomeEmailHtml(firstName?: string) { + const name = firstName ? `Hi ${firstName},` : "Welcome,"; + return ` +
+

${name}

+

Welcome to Instant Plumber Connect — glad to have you on board.

+

Start by updating your profile and verifying your contact details.

+
+

Thank you,
The Instant Plumber Connect Team

+
+ `; +} - async sendVerificationEmail(email: string, code: string, firstName?: string): Promise { - if (!this.initialized) { - console.log('Email service not initialized - cannot send verification email'); - return false; - } +export const emailService = { + isReady() { + return transporter !== null || process.env.NODE_ENV === "development"; + }, + async sendVerificationEmail(to: string, code: string, firstName?: string): Promise { try { - const msg = { - to: email, - from: 'noreply@instantplumberconnect.com', // Replace with your verified sender - subject: 'Your Instant Plumber Connect Verification Code', - text: `Your verification code is: ${code}`, - html: ` -
-

Instant Plumber Connect

-

Hello ${firstName || 'there'},

-

Your verification code is:

-
-

${code}

-
-

This code will expire in 10 minutes.

-

If you didn't request this verification, please ignore this email.

-
-

- Instant Plumber Connect - Professional plumbing services made simple -

-
- `, - }; - - await sgMail.send(msg); - console.log(`Verification email sent successfully to ${email}`); - return true; - } catch (error: any) { - console.error('SendGrid email error:', error); - - // Handle specific SendGrid errors - if (error.response) { - console.error('SendGrid response error:', { - status: error.response.status, - body: error.response.body + const subject = "Your Instant Plumber Connect verification code"; + const html = verificationEmailHtml(code, firstName); + if (transporter) { + const info = await transporter.sendMail({ + from: process.env.EMAIL_FROM || `"Instant Plumber Connect" `, + to, + subject, + html, + text: `Your verification code is ${code}`, }); + console.log("Verification email sent:", info.messageId); + return true; + } else { + // Development fallback: log code so you can test without SMTP configured + console.log(`[DEV EMAIL] To: ${to} Subject: ${subject} Code: ${code}`); + return true; } - + } catch (err) { + console.error("Error sending verification email:", err); return false; } - } + }, - async sendWelcomeEmail(email: string, firstName: string): Promise { - if (!this.initialized) { + async sendWelcomeEmail(to: string, firstName?: string): Promise { + try { + const subject = "Welcome to Instant Plumber Connect"; + const html = welcomeEmailHtml(firstName); + if (transporter) { + const info = await transporter.sendMail({ + from: process.env.EMAIL_FROM || `"Instant Plumber Connect" `, + to, + subject, + html, + text: `Welcome to Instant Plumber Connect.`, + }); + console.log("Welcome email sent:", info.messageId); + return true; + } else { + console.log(`[DEV EMAIL] To: ${to} Subject: ${subject}`); + return true; + } + } catch (err) { + console.error("Error sending welcome email:", err); return false; } + }, + async sendGenericEmail(to: string, subject: string, html: string): Promise { try { - const msg = { - to: email, - from: 'noreply@instantplumberconnect.com', - subject: 'Welcome to Instant Plumber Connect!', - html: ` -
-

Welcome to Instant Plumber Connect!

-

Hello ${firstName},

-

Your plumber account has been successfully created. You can now:

-
    -
  • Receive instant video call requests from customers
  • -
  • Manage your availability status
  • -
  • Track your earnings and call history
  • -
-

Start receiving calls by setting yourself as available in your dashboard!

-
- `, - }; - - await sgMail.send(msg); - console.log(`Welcome email sent to ${email}`); - return true; - } catch (error) { - console.error('Error sending welcome email:', error); - return false; + if (transporter) { + const info = await transporter.sendMail({ + from: process.env.EMAIL_FROM || `"Instant Plumber Connect" `, + to, + subject, + html, + }); + return { success: true, info }; + } else { + console.log(`[DEV EMAIL] To: ${to} Subject: ${subject} HTML: ${html}`); + return { success: true, info: "dev-fallback" }; + } + } catch (err) { + console.error("Error sending email:", err); + return { success: false, info: err }; } - } -} + }, +}; -export const emailService = new EmailService(); \ No newline at end of file +export default emailService; \ No newline at end of file