send from gmail
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@ -136,4 +136,6 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
data
|
||||
data
|
||||
|
||||
public/uploads
|
||||
6
doc/prompts/14. Gmail sender
Normal file
6
doc/prompts/14. Gmail sender
Normal file
@ -0,0 +1,6 @@
|
||||
Please do following
|
||||
- checkout to new brach features/gmail_sender
|
||||
- create email.ts
|
||||
- create function to send email usimg gmail smtp
|
||||
- creadentials are in .env
|
||||
- the function receives CustomerId and save to the Record
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@ -18,11 +18,13 @@
|
||||
"@editorjs/marker": "^1.4.0",
|
||||
"@editorjs/paragraph": "^2.11.7",
|
||||
"@editorjs/quote": "^2.7.6",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"bcrypt": "^5.1.1",
|
||||
"csv-parse": "^5.6.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql2": "^3.13.0",
|
||||
"next": "15.2.2",
|
||||
"nodemailer": "^6.10.0",
|
||||
"pg": "^8.14.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
@ -1550,12 +1552,19 @@
|
||||
"version": "20.17.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.24.tgz",
|
||||
"integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/nodemailer": {
|
||||
"version": "6.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz",
|
||||
"integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
|
||||
@ -5937,6 +5946,14 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
|
||||
"integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
@ -8133,7 +8150,6 @@
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unique-filename": {
|
||||
|
||||
@ -22,11 +22,13 @@
|
||||
"@editorjs/marker": "^1.4.0",
|
||||
"@editorjs/paragraph": "^2.11.7",
|
||||
"@editorjs/quote": "^2.7.6",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"bcrypt": "^5.1.1",
|
||||
"csv-parse": "^5.6.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql2": "^3.13.0",
|
||||
"next": "15.2.2",
|
||||
"nodemailer": "^6.10.0",
|
||||
"pg": "^8.14.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
@ -48,4 +50,4 @@
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
87
src/lib/email.ts
Normal file
87
src/lib/email.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import nodemailer from 'nodemailer';
|
||||
import { ContactRecord } from './database/entities/ContactRecord';
|
||||
import { Customer } from './database/entities/Customer';
|
||||
import { getDataSource } from './database';
|
||||
|
||||
interface SendEmailResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export async function sendEmail(
|
||||
customerId: string,
|
||||
subject: string,
|
||||
body: string,
|
||||
htmlBody?: string
|
||||
): Promise<SendEmailResult> {
|
||||
try {
|
||||
// Get data source
|
||||
const dataSource = await getDataSource();
|
||||
|
||||
// Get customer details
|
||||
const customer = await dataSource.getRepository(Customer).findOne({
|
||||
where: { id: customerId }
|
||||
});
|
||||
|
||||
if (!customer) {
|
||||
throw new Error(`Customer with ID ${customerId} not found`);
|
||||
}
|
||||
|
||||
// Create Gmail transporter
|
||||
const transporter = nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: process.env.GMAIL_USER,
|
||||
pass: process.env.GMAIL_APP_PASSWORD // Using app-specific password
|
||||
}
|
||||
});
|
||||
|
||||
// Send email
|
||||
const info = await transporter.sendMail({
|
||||
from: process.env.GMAIL_USER,
|
||||
to: customer.email,
|
||||
subject: subject,
|
||||
text: body,
|
||||
html: htmlBody || body
|
||||
});
|
||||
|
||||
// Create contact record
|
||||
const contactRecord = new ContactRecord();
|
||||
contactRecord.customer = customer;
|
||||
contactRecord.customerId = customer.id;
|
||||
contactRecord.contactType = 'EMAIL';
|
||||
contactRecord.notes = `Email sent successfully. Subject: ${subject}. Message ID: ${info.messageId}`;
|
||||
|
||||
// Save contact record
|
||||
await dataSource.getRepository(ContactRecord).save(contactRecord);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
// Create contact record for failed attempt
|
||||
if (error instanceof Error) {
|
||||
try {
|
||||
const dataSource = await getDataSource();
|
||||
const customer = await dataSource.getRepository(Customer).findOne({
|
||||
where: { id: customerId }
|
||||
});
|
||||
|
||||
if (customer) {
|
||||
const contactRecord = new ContactRecord();
|
||||
contactRecord.customer = customer;
|
||||
contactRecord.customerId = customer.id;
|
||||
contactRecord.contactType = 'EMAIL';
|
||||
contactRecord.notes = `Failed to send email: ${error.message}`;
|
||||
|
||||
await dataSource.getRepository(ContactRecord).save(contactRecord);
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error('Failed to save error record:', dbError);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ import { Customer } from '../lib/database/entities/Customer';
|
||||
interface CustomerCSVRow {
|
||||
City: string;
|
||||
Name: string;
|
||||
'Website URL': string;
|
||||
URL: string;
|
||||
Email: string;
|
||||
}
|
||||
|
||||
@ -66,14 +66,20 @@ async function importCustomers(csvFilePath: string): Promise<void> {
|
||||
// Create new customer
|
||||
const customer = new Customer();
|
||||
customer.name = name;
|
||||
customer.url = record['Website URL'] === 'null' ? '' : record['Website URL'];
|
||||
customer.url = record['URL'] === 'null' ? '' : record['URL'];
|
||||
customer.email = email;
|
||||
|
||||
// Save to database
|
||||
await customerRepository.save(customer);
|
||||
importedCount++;
|
||||
try {
|
||||
|
||||
// Save to database
|
||||
await customerRepository.save(customer);
|
||||
importedCount++;
|
||||
|
||||
console.log(`Imported customer: ${name}`);
|
||||
} catch (e) {
|
||||
console.log(`Skipped: ${name}`);
|
||||
}
|
||||
|
||||
console.log(`Imported customer: ${name}`);
|
||||
}
|
||||
|
||||
console.log('Import summary:');
|
||||
|
||||
Reference in New Issue
Block a user