Skip to content

Commit 4f200a6

Browse files
committed
Set up server
1 parent fc7147e commit 4f200a6

File tree

11 files changed

+152
-94
lines changed

11 files changed

+152
-94
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2024 1ilit
3+
Copyright (c) 2025 1ilit
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

package-lock.json

Lines changed: 27 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
"license": "ISC",
1818
"devDependencies": {
1919
"@eslint/js": "^9.25.0",
20+
"@types/cors": "^2.8.17",
2021
"@types/dotenv": "^6.1.1",
2122
"@types/express": "^5.0.1",
2223
"@types/node": "^22.14.1",
24+
"@types/nodemailer": "^6.4.17",
2325
"eslint": "^9.25.0",
2426
"globals": "^16.0.0",
2527
"nodemon": "^3.1.9",
@@ -31,7 +33,7 @@
3133
"cors": "^2.8.5",
3234
"dotenv": "^16.5.0",
3335
"express": "^4.21.2",
34-
"nodemailer": "^6.9.5",
36+
"nodemailer": "^6.10.1",
3537
"ts-node": "^10.9.2"
3638
}
3739
}

src/app.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
11
import express from 'express';
2+
import cors from 'cors';
3+
import { emailRouter } from './routes/email-route';
4+
import { config } from './config';
25

36
const app = express();
47

58
app.use(express.json());
9+
app.use(
10+
cors({
11+
origin: function (origin, callback) {
12+
if (origin && config.server.allowedOrigins.indexOf(origin) !== -1) {
13+
callback(null, true);
14+
} else {
15+
callback(new Error('Not allowed by CORS'));
16+
}
17+
},
18+
}),
19+
);
620

721
app.get('/', (req, res) => {
822
res.send('Hello');
923
});
1024

11-
export default app;
25+
app.use('/email', emailRouter);
26+
27+
export default app;

src/config.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/config/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { config as dotenvConfig } from 'dotenv';
2+
3+
dotenvConfig();
4+
5+
export const config = {
6+
server: {
7+
port: process.env.PORT || 5000,
8+
allowedOrigins: process.env.CLIENT_URLS ? process.env.CLIENT_URLS.split(',') : [],
9+
},
10+
mail: {
11+
service: process.env.MAIL_SERVICE || 'gmail',
12+
username: process.env.MAIL_USERNAME || '',
13+
password: process.env.MAIL_PASSWORD || '',
14+
},
15+
};

src/controllers/email-controller.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Request, Response } from 'express';
2+
import { sendEmail } from '../utils/send-email';
3+
import { config } from '../config';
4+
import { emailStyles } from '../styles/email-styles';
5+
6+
async function send(req: Request, res: Response) {
7+
const { subject, message, attachments } = req.body;
8+
9+
if (!req.body || !subject || !message) {
10+
res.status(400).json({
11+
success: false,
12+
message: 'Incorrect body',
13+
});
14+
}
15+
16+
try {
17+
await sendEmail(
18+
subject,
19+
`<html><head>${emailStyles}</head><body>${message}</body></html>`,
20+
config.mail.username,
21+
config.mail.username,
22+
attachments,
23+
);
24+
res.status(200).json({
25+
success: true,
26+
message: `Email sent to ${config.mail.username}`,
27+
});
28+
} catch {
29+
res.status(500).json({
30+
success: false,
31+
message: 'Something went wrong.',
32+
});
33+
}
34+
}
35+
36+
export { send };

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import app from './app';
22
import { config } from './config';
33

4-
app.listen(config.PORT, () => {
5-
console.log(`Server is running on http://localhost:${config.PORT}`);
4+
app.listen(config.server.port, () => {
5+
console.log(`Server is running on http://localhost:${config.server.port}`);
66
});

src/routes/email-route.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import express from 'express';
2+
import { send } from '../controllers/email-controller';
3+
4+
const emailRouter = express.Router();
5+
6+
emailRouter.post('/send', send);
7+
8+
export { emailRouter };
Lines changed: 2 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,2 @@
1-
const express = require("express");
2-
const app = express();
3-
const cors = require("cors");
4-
const nodemailer = require("nodemailer");
5-
const bodyparser = require("body-parser");
6-
const dotenv = require("dotenv").config();
7-
8-
const { PORT, CLIENT_URLS, EMAIL_PASS, EMAIL_USER, EMAIL_REPORT } = process.env;
9-
10-
const whitelist = CLIENT_URLS.split(",");
11-
12-
app.use(express.json());
13-
app.use(bodyparser.json());
14-
app.use(
15-
cors({
16-
origin: function (origin, callback) {
17-
if (whitelist.indexOf(origin) !== -1) {
18-
callback(null, true);
19-
} else {
20-
callback(new Error("Not allowed by CORS"));
21-
}
22-
},
23-
})
24-
);
25-
26-
const emailStyles =
27-
"<style>.ltr{text-align:left;}.rtl{text-align:right;}.editor-text-bold{font-weight:bold;}.editor-text-italic{font-style:italic;}.editor-text-underline{text-decoration:underline;}.editor-text-strikethrough{text-decoration:line-through;}.editor-text-underlineStrikethrough{text-decoration:underlineline-through;}.editor-text-code{background-color:#ccc;padding:1px0.25rem;font-family:Menlo,Consolas,Monaco,monospace;font-size:94%;}.editor-link{color:rgb(33,111,219);text-decoration:none;}.editor-code{background-color:#ccc;font-family:Menlo,Consolas,Monaco,monospace;display:block;padding:8px 8px 8px 52px;line-height:1.53;font-size:13px;margin:0;margin-top:8px;margin-bottom:8px;tab-size:2;overflow-x:auto;position:relative;}.editor-code:before{content:attr(data-gutter);position:absolute;background-color:#ddd;left:0;top:0;border-right:1px solid #ccc;padding:8px;color:#777;white-space:pre-wrap;text-align:right;min-width:25px;}.editor-code:after{content:attr(data-highlight-language);top:0;right:3px;padding:3px;font-size:10px;text-transform:uppercase;position:absolute;color: #000;}.editor-tokenComment{color:slategray;}.editor-tokenPunctuation{color:#999;}.editor-tokenProperty{color:#905;}.editor-tokenSelector{color:#690;}.editor-tokenOperator{color:#9a6e3a;}.editor-tokenAttr{color:#07a;}.editor-tokenVariable{color:#e90;}.editor-tokenFunction{color:#dd4a68;}.editor-paragraph{margin:0;margin-bottom:8px;position:relative;}.editor-paragraph:last-child{margin-bottom:0;}.editor-heading-h1{font-size:24px;margin:0;margin-bottom:12px;padding:0;}.editor-heading-h2{font-size:16px;margin:0;margin-top:10px;padding:0;}.editor-quote{margin:0;margin-left:20px;font-size:15px;color:rgb(101,103,107);border-left-color:rgb(206,208,212);border-left-width:4px;border-left-style:solid;padding-left:16px;}.editor-list-ol{padding:0;margin:0;margin-left:16px;list-style-type:decimal;}.editor-list-ul{list-style-type:circle;padding:0;margin:0;margin-left:16px;}.editor-listitem{margin:8px 32px 8px 32px;}.editor-nested-listitem{list-style-type:none;}</style>";
28-
29-
let transporter = nodemailer.createTransport({
30-
host: "smtp-mail.outlook.com",
31-
service: "outlook",
32-
secureConnection: false,
33-
tls: {
34-
ciphers: "SSLv3",
35-
},
36-
port: 587,
37-
auth: {
38-
user: EMAIL_USER,
39-
pass: EMAIL_PASS,
40-
},
41-
});
42-
43-
const sendEmail = async (subject, message, to, from, attachments = []) => {
44-
const options = {
45-
from: from,
46-
to: to,
47-
subject: subject,
48-
html: message,
49-
attachments: attachments,
50-
};
51-
52-
transporter.sendMail(options, (err, info) => {
53-
if (err) {
54-
console.log(err);
55-
}
56-
});
57-
};
58-
59-
app.post("/send_email", async (req, res) => {
60-
const { subject, message, attachments } = req.body;
61-
62-
try {
63-
await sendEmail(
64-
subject,
65-
`<html><head>${emailStyles}</head><body>${message}</body></html>`,
66-
EMAIL_REPORT,
67-
EMAIL_USER,
68-
attachments
69-
);
70-
res.status(200).json({ message: "Report submitted!" });
71-
} catch (e) {
72-
res.status(500).json({ error: e });
73-
}
74-
});
75-
76-
app.listen(PORT, () => {
77-
console.log("Server started");
78-
});
1+
export const emailStyles =
2+
'<style>.ltr{text-align:left;}.rtl{text-align:right;}.editor-text-bold{font-weight:bold;}.editor-text-italic{font-style:italic;}.editor-text-underline{text-decoration:underline;}.editor-text-strikethrough{text-decoration:line-through;}.editor-text-underlineStrikethrough{text-decoration:underlineline-through;}.editor-text-code{background-color:#ccc;padding:1px0.25rem;font-family:Menlo,Consolas,Monaco,monospace;font-size:94%;}.editor-link{color:rgb(33,111,219);text-decoration:none;}.editor-code{background-color:#ccc;font-family:Menlo,Consolas,Monaco,monospace;display:block;padding:8px 8px 8px 52px;line-height:1.53;font-size:13px;margin:0;margin-top:8px;margin-bottom:8px;tab-size:2;overflow-x:auto;position:relative;}.editor-code:before{content:attr(data-gutter);position:absolute;background-color:#ddd;left:0;top:0;border-right:1px solid #ccc;padding:8px;color:#777;white-space:pre-wrap;text-align:right;min-width:25px;}.editor-code:after{content:attr(data-highlight-language);top:0;right:3px;padding:3px;font-size:10px;text-transform:uppercase;position:absolute;color: #000;}.editor-tokenComment{color:slategray;}.editor-tokenPunctuation{color:#999;}.editor-tokenProperty{color:#905;}.editor-tokenSelector{color:#690;}.editor-tokenOperator{color:#9a6e3a;}.editor-tokenAttr{color:#07a;}.editor-tokenVariable{color:#e90;}.editor-tokenFunction{color:#dd4a68;}.editor-paragraph{margin:0;margin-bottom:8px;position:relative;}.editor-paragraph:last-child{margin-bottom:0;}.editor-heading-h1{font-size:24px;margin:0;margin-bottom:12px;padding:0;}.editor-heading-h2{font-size:16px;margin:0;margin-top:10px;padding:0;}.editor-quote{margin:0;margin-left:20px;font-size:15px;color:rgb(101,103,107);border-left-color:rgb(206,208,212);border-left-width:4px;border-left-style:solid;padding-left:16px;}.editor-list-ol{padding:0;margin:0;margin-left:16px;list-style-type:decimal;}.editor-list-ul{list-style-type:circle;padding:0;margin:0;margin-left:16px;}.editor-listitem{margin:8px 32px 8px 32px;}.editor-nested-listitem{list-style-type:none;}</style>';

src/utils/send-email.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createTransport } from 'nodemailer';
2+
import { type Attachment } from 'nodemailer/lib/mailer';
3+
import { config } from '../config';
4+
5+
const transporter = createTransport({
6+
service: config.mail.service,
7+
auth: {
8+
user: config.mail.username,
9+
pass: config.mail.password,
10+
},
11+
});
12+
13+
async function sendEmail(
14+
subject: string,
15+
message: string,
16+
to: string,
17+
from: string,
18+
attachments: Attachment[] = [],
19+
) {
20+
const options = {
21+
from,
22+
to,
23+
subject,
24+
html: message,
25+
attachments,
26+
};
27+
28+
return new Promise((resolve, reject) => {
29+
transporter.sendMail(options, (err, info) => {
30+
if (err) {
31+
console.error('Email sending failed:', err);
32+
reject(new Error(err.message));
33+
} else {
34+
console.log('Email sent:', info.messageId);
35+
resolve(info.messageId);
36+
}
37+
});
38+
});
39+
}
40+
41+
export { sendEmail };

0 commit comments

Comments
 (0)