Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Config/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ config({
path: '.env'
});

export const {DB_URI,TEST_DB_URI,PORT,JWT_EXPIRES_IN,JWT_SECRET,ARCJET_KEY,NODE_ENV} = process.env;
export const {DB_URI,TEST_DB_URI,PORT,JWT_EXPIRES_IN,JWT_SECRET,ARCJET_KEY,NODE_ENV,QSTASH_TOKEN,QSTASH_URL,QSTASH_CURRENT_SIGNING_KEY,QSTASH_NEXT_SIGNING_KEY,SERVER_URL,EMAIL_USER,EMAIL_PASSWORD} = process.env;
36 changes: 36 additions & 0 deletions Config/nodemailer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import nodemailer from 'nodemailer';
import {EMAIL_PASSWORD, EMAIL_USER} from "./env.js";

const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user:EMAIL_USER,
pass:EMAIL_PASSWORD
}
});

export const sendAccountCreatedEmail = async (toEmail, username) => {
await transporter.sendMail({
from: `"My App" <${EMAIL_USER}>`,
to: toEmail,
subject: "Account Created Successfully 🎉",
html: `
<h2>Welcome, ${username}!</h2>
<p>Your account has been created successfully.</p>
<p>We're glad to have you with us 💙</p>
`,
});
};

export const sendNewsletterResponseEmail = async (toEmail, name) => {
await transporter.sendMail({
from: `"My App" <${EMAIL_USER}>`,
to: toEmail,
subject: "Newsletter Message Received ✅",
html: `
<h2>Hey ${name},</h2>
<p>Thanks for reaching out! Your message has been successfully sent.</p>
<p>We'll get back to you soon.</p>
`,
});
};
8 changes: 8 additions & 0 deletions Config/upstash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {Client as WorkflowClient} from '@upstash/workflow'

import {QSTASH_TOKEN,QSTASH_URL} from "./env.js";

export const workflowClient=new WorkflowClient({
baseURL:QSTASH_URL,
token:QSTASH_TOKEN,
})
5 changes: 4 additions & 1 deletion Controllers/auth.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import User from "../Models/user.model.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import { JWT_EXPIRES_IN, JWT_SECRET } from "../Config/env.js";
import {sendAccountCreatedEmail} from "../Config/nodemailer.js";

export const signUp = async (req, res, next) => {

Expand Down Expand Up @@ -44,12 +45,14 @@ export const signUp = async (req, res, next) => {
{ expiresIn: JWT_EXPIRES_IN }
);

await sendAccountCreatedEmail(newUser.email, newUser.name);

await session.commitTransaction();
session.endSession();

res.status(201).json({
success: true,
message: "User created successfully",
message: "User created successfully.. email sent!!",
data: {
token,
},
Expand Down
19 changes: 16 additions & 3 deletions Controllers/subscription.controller.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import subSchema from "../models/subscription.model.js";

import Subscription from "../models/subscription.model.js";
import {workflowClient} from "../Config/upstash.js";
import {SERVER_URL} from "../Config/env.js";

export const createSubscriptions=async(req,res,next)=>{

try{
const newSubscription=await subSchema.create({
const newSubscription=await Subscription.create({
...req.body,
user:req.user._id
})
});

const {workflowRunId}=await workflowClient.trigger({
url:`${SERVER_URL}/api/v1/subscriptions/reminder`,
body:{
subscriptionId:newSubscription.id
},
headers:{
"Content-Type":"application/json"
}

});

res.status(201).json({
status:"success",
Expand Down
57 changes: 57 additions & 0 deletions Controllers/workflow.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {createRequire} from 'module'
import dayjs from 'dayjs'
import workflowRouter from "../routes/workflow.routes.js";
import Subscription from "../Models/subscription.model.js";
const require=createRequire(import.meta.url);
const {serve} =require('@upstash/workflow/express');
//to use commonjs syntax to require serve module

const REMINDERS=[7,5,2,1];

export const sendReminders = serve(async(context)=>{

const {subscriptionId} = context.requestPayload;
const subscription=await fetchSubscription(context,subscriptionId);

if(!subscription || subscription.status!='active'){
return;
}

const renewalDate=dayjs(subscription.renewalDate);

if(renewalDate.isBefore(dayjs())){
console.log(`Renewal date is passed for subscription ${subscriptionId}.. stopping workflow`);
return;
}

for(const daysBefore of REMINDERS){
const reminderDate=renewalDate.subtract(daysBefore,'day');

if(reminderDate.isAfter(dayjs())){
await sleepUntilReminder(context,`reminder ${daysBefore} days before`,reminderDate);
}

await triggerReminder(context,`Reminder ${daysBefore} days before`);
}
}

)

const fetchSubscription=async(context,subscriptionId)=>{

return await context.run('get subscriptions',()=>{
return Subscription.findById(subscriptionId).populate('user','name email');
});
}

const sleepUntilReminder=async (context,label,date)=>{
console.log(`Sleep until ${label} remainder at ${date}`);
await context.sleepUntil(label,date.toDate());
}

const triggerReminder=async (context,label)=>{
return await context.run(label,()=>{
console.log(`Triggering ${label} reminder`);

})
}
5 changes: 3 additions & 2 deletions Models/subscription.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ const subSchema = new mongoose.Schema({
timeStamps: true
})

const Subscription=new mongoose.model('Subscription',subSchema);
const Subscription =
mongoose.models.Subscription || mongoose.model("Subscription", subSchema);
//create and stores subscriptions in subscriptions collection

export default Subscription;
export default Subscription;
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import authRoutes from "./routes/auth.routes.js";
import subscriptionRoutes from "./routes/subscription.routes.js";
import userRoutes from "./routes/user.routes.js";
import authMiddleware from "./Middlewares/auth.middleware.js";
import workflowRouter from "./routes/workflow.routes.js";

app.use(express.json());

app.use("/api/v1/auth", authRoutes);
app.use("/api/v1/subscriptions", subscriptionRoutes);
app.use("/api/v1/users", userRoutes);
app.use("/api/v1/workflows", workflowRouter);


app.use(errorHandler);
Expand Down
Loading