Skip to content

Commit

Permalink
version 1.1.3 (#38)
Browse files Browse the repository at this point in the history
Fixes:
 - event deletion removed from the scheduler
 - validations for lead revoking itself, dates, restrict update of some fields, max registrations.
 - reduce events code hash length.
 - delete event image key instead of replacing it from amazon s3 when an event is deleted.
 - some checks for certificate metadata/

New:
 - deploy script.
 - worker clusters
 - modular email service
  • Loading branch information
rhnmht30 authored Jul 7, 2020
2 parents 4a28077 + cbd391b commit f80b20a
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 244 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ JWT_PRIVATE_KEY=
REDIS_URL=
EMAIL_USER=
EMAIL_PASS=
EMAIL_HOST=
SENDER_EMAIL=
AWS_KEY=
AWS_SECRET=
AWS_BUCKET_NAME=
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="left">
<img width="240" src="https://raw.githubusercontent.com/dsckiet/resources/master/dsckiet-logo.png" />
<h2 align="left"> DSC KIET PORTAL </h2>
<h4 align="left"> A full-fledged web portal for the community to manage members, events, attendance, feedback, and other community tools. <h4>
<h4 align="left"> Scalable backend with async job scheduling to manage everything @dsckiet <h4>
</p>

---
Expand All @@ -11,8 +11,15 @@

## Functionalities

- [ ] < ADD FUNCTIONALITY >
- [ ] < ADD FUNCTIONALITY >
- [X] Users auth and role based dashboard
- [X] Participants auth and dashboard
- [X] CRUD Events
- [X] Event Participation
- [X] Event Attendance
- [X] Event Certificates
- [X] Event Feedback
- [ ] Event Emails


<br>

Expand Down
89 changes: 89 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const express = require("express");
const bodyParser = require("body-parser");
const compression = require("compression");
const morgan = require("morgan");
const path = require("path");
const helmet = require("helmet");
const { NODE_ENV, PORT } = require("./config/index");
const { notFound, sendErrors } = require("./config/errorHandler");
const kue = require("kue");
const app = express();

const cors = require("cors");
require("dotenv").config();
require("./config/dbconnection");

module.exports = () => {
app.use(compression());
app.use(helmet());
app.use(morgan("dev"));
app.use(cors({ exposedHeaders: "x-auth-token" }));
app.use(express.static(path.join(__dirname, "public")));
app.use(
bodyParser.urlencoded({
limit: "50mb",
extended: true,
parameterLimit: 1000000
})
);
app.use(
bodyParser.json({
limit: "50mb",
extended: true,
parameterLimit: 1000000
})
);
app.use("/kue-cli", kue.app);

if (NODE_ENV === "production") {
console.log = console.warn = console.error = () => {};
}

//load Schemas
const User = require("./models/User");
const Participant = require("./models/Participant");
const Event = require("./models/Event");
const Attendance = require("./models/Attendance");
const Feedback = require("./models/Feedback");
const ResetToken = require("./models/ResetToken");
const Subscriber = require("./models/Subscriber");
const Subscription = require("./models/Subscription");

//Routes
app.use("/api/v1", require("./routes/api/v1/index"));
app.use("/api/v1/users", require("./routes/api/v1/users"));
app.use("/api/v1/events", require("./routes/api/v1/events"));
app.use("/api/v1/subscription", require("./routes/api/v1/subscription"));

app.use("*", notFound);

//Error Handlers
app.use(sendErrors);

// Allowing headers
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
res.header("Access-Control-Allow-Credentials", true);
res.header(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, PATCH, OPTIONS"
);
next();
});

//Setting up server
(async () => {
try {
await app.listen(PORT);
console.info(
`NODE_ENV: ${NODE_ENV}\nServer is up and running on Port ${PORT}`
);
} catch (err) {
console.info("Error in running server.", err);
}
})();
};
7 changes: 1 addition & 6 deletions config/Scheduler/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@ Queue.process("sendGeneralEmailJob", async (job, done) => {
logger("info", "scheduler", log);
console.log(log);
try {
await sendGeneralEmail(
data.email,
data.subject,
data.content,
data.name
);
await sendGeneralEmail(data.email, data.subject, data.content);
done();
} catch (err) {
console.log(err);
Expand Down
24 changes: 12 additions & 12 deletions config/emailService.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
const nodemailer = require("nodemailer");
var htmlToText = require("nodemailer-html-to-text").htmlToText;

let { EMAIL_USER, EMAIL_PASS } = require("./index");
let { EMAIL_USER, EMAIL_PASS, EMAIL_HOST, SENDER_EMAIL } = require("./index");
let { getMailTemplate } = require("../utility/emailTemplates");
const { logger } = require("../utility/helpers");

const transporter = nodemailer.createTransport({
service: "gmail",
type: "SMTP",
host: "smtp.gmail.com",
host: EMAIL_HOST,
secure: true,
debug: true,
port: 465,
auth: {
user: EMAIL_USER,
pass: EMAIL_PASS
Expand All @@ -17,17 +20,16 @@ const transporter = nodemailer.createTransport({
module.exports.sendSystemEmail = async (email, data, type) => {
let { subject, html } = getMailTemplate(data, type);
let mailOptions = {
from: `DSCKIET <${EMAIL_USER}>`,
from: `DSCKIET <${SENDER_EMAIL}>`,
to: email,
subject,
text: "",
html,
headers: {
"x-priority": "1",
"x-msmail-priority": "High",
importance: "high"
}
};
transporter.use("compile", htmlToText());
try {
await transporter.sendMail(mailOptions);
} catch (err) {
Expand All @@ -36,20 +38,18 @@ module.exports.sendSystemEmail = async (email, data, type) => {
}
};

module.exports.sendGeneralEmail = async (email, subject, content, name) => {
let html = getMailTemplate({ name }, "other", content);
module.exports.sendGeneralEmail = async (email, subject, content) => {
let mailOptions = {
from: `DSCKIET <${EMAIL_USER}>`,
from: `DSCKIET <${SENDER_EMAIL}>`,
to: email,
subject,
text: "",
html,
html: content,
headers: {
"x-priority": "1",
"x-msmail-priority": "High",
importance: "high"
}
};
transporter.use("compile", htmlToText());
try {
await transporter.sendMail(mailOptions);
} catch (err) {
Expand Down
2 changes: 2 additions & 0 deletions config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ module.exports = {
REDIS_URL: process.env.REDIS_URL,
EMAIL_USER: process.env.EMAIL_USER,
EMAIL_PASS: process.env.EMAIL_PASS,
EMAIL_HOST: process.env.EMAIL_HOST,
SENDER_EMAIL: process.env.SENDER_EMAIL,
EVENT_HASH_LENGTH: 10,
USER_HASH_LENGTH: 8,
AWS_KEY: process.env.AWS_KEY,
Expand Down
84 changes: 24 additions & 60 deletions controllers/events_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,6 @@ getPushObject = (part, attendInd) => {
};
};

const scheduleMailsInBatches = (users, jobName, props) => {
let batchSize = 20;
let initial = 0,
i = 0;
while (initial < users.length) {
let currentBatch = users.slice(initial, initial + batchSize);
let start_time = Date.now() + i * 2 * 1000;
//do stuff with currentbatch
currentBatch.map((user, index) => {
let args = {
jobName,
time: start_time + index,
params: {
...props,
name: user.name,
email: user.email
}
};
kue.scheduleJob(args);
});

i = i + 1;
initial = initial + batchSize;
}
return;
};

module.exports.getParticipants = async (req, res) => {
let { eid, query, branch, year, sortBy } = req.query;
let filters = {};
Expand Down Expand Up @@ -1260,42 +1233,33 @@ module.exports.generateCerti = async (req, res) => {
};

module.exports.sendEventMails = async (req, res) => {
let { type, users, eid, subject, content } = req.body;
// type: event-reminder, event-followup, event-thanks
// users: [{ name, email }]
// eid

// for:
// - event-reminder, event-followup, event-thanks:
// pass type among these, event: event id

// - other (example updates, etc):
// pass type can be anything, (subject, content) are mandatory

let event;
let systemMailTypes = ["event-reminder", "event-followup", "event-thanks"];
if (!subject && !content && systemMailTypes.indexOf(type) !== -1) {
event = await Event.findById(eid);
if (!event) {
return sendError(res, "Invalid Event!!", BAD_REQUEST);
}
}
let { users, subject, content, type } = req.body;

let params = {
mailType: type
},
jobname;
let batchSize = 50;
let initial = 0,
i = 0;
while (initial < users.length) {
let currentBatch = users.slice(initial, initial + batchSize);
let start_time = Date.now() + i * 1 * 1000;
//do stuff with currentbatch
currentBatch.map((user, index) => {
content = content.replace(/{{NAME}}/g, user.name);
let args = {
jobName: "sendGeneralEmailJob",
time: start_time + index,
params: {
mailType: type,
email: user.email,
subject,
content
}
};
kue.scheduleJob(args);
});

if (event) {
jobname = "sendSystemEmailJob";
params.event = event;
} else {
jobname = "sendGeneralEmailJob";
params.subject = subject;
params.content = content;
i = i + 1;
initial = initial + batchSize;
}

scheduleMailsInBatches(users, jobname, params);

return sendSuccess(res, null);
};
58 changes: 25 additions & 33 deletions controllers/subscription_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,6 @@ const worker = require("../config/Scheduler/worker");
const { sendError, sendSuccess } = require("../utility/helpers");
const { BAD_REQUEST } = require("../utility/statusCodes");

const scheduleMailsInBatches = (users, jobName, props) => {
let batchSize = 20;
let initial = 0,
i = 0;
while (initial < users.length) {
let currentBatch = users.slice(initial, initial + batchSize);
let start_time = Date.now() + i * 2 * 1000;
//do stuff with currentbatch
currentBatch.map((user, index) => {
let args = {
jobName,
time: start_time + index,
params: {
email: user.email,
...props
}
};
kue.scheduleJob(args);
});

i = i + 1;
initial = initial + batchSize;
}
return;
};

module.exports.subscribers = async (req, res) => {
let subscribers = await Subscriber.find().sort({ createdAt: "desc" });
return sendSuccess(res, subscribers);
Expand Down Expand Up @@ -85,13 +59,31 @@ module.exports.sendSubscription = async (req, res) => {
subscription.save()
]);

let params = {
mailType: "subscription",
subject,
content
},
jobname = "sendGeneralEmailJob";
let batchSize = 50;
let initial = 0,
i = 0;
while (initial < subscribers.length) {
let currentBatch = subscribers.slice(initial, initial + batchSize);
let start_time = Date.now() + i * 1 * 1000;
//do stuff with currentbatch
currentBatch.map((user, index) => {
content = content.replace(/{{NAME}}/g, user.name);
content = content.replace(/{{SUBSCRIBER_ID}}/g, user._id);
let args = {
jobName: "sendGeneralEmailJob",
time: start_time + index,
params: {
mailType: type,
email: user.email,
subject,
content
}
};
kue.scheduleJob(args);
});

scheduleMailsInBatches(subscribers, jobname, params);
i = i + 1;
initial = initial + batchSize;
}
return sendSuccess(res, null);
};
Loading

0 comments on commit f80b20a

Please sign in to comment.