-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
149 lines (132 loc) · 5.35 KB
/
app.js
File metadata and controls
149 lines (132 loc) · 5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
if(process.env.NODE_ENV !== "production") {
require("dotenv").config(); // load environment variables from .env file in development mode
}
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const path = require("path");
const methodOverride = require("method-override"); // for PUT and DELETE requests
const ejsMate = require("ejs-mate");
const ExpressError = require("./utils/ExpressError.js"); // custom error class for Express
const session = require("express-session");
const MongoStore = require("connect-mongo");
const flash = require("connect-flash"); // for flash messages
const passport = require("passport"); // for authentication
const LocalStrategy = require("passport-local");
const cron = require("node-cron");
const meterRouter = require("./routes/meter.js"); // importing the meter routes
const alertRouter = require("./routes/alert.js"); // importing the review routes
const userRouter = require("./routes/user.js"); // importing the user routes
const User = require("./models/user.js"); // importing the User model
const Meter = require("./models/meter.js"); // importing the User model
const simulateNewReading = require("./utils/simulateReadingJob");
const { sendPaymentRequestEmail } = require("./utils/email.js");
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
app.use(express.urlencoded({ extended: true }));
app.use(methodOverride("_method"));
app.engine("ejs", ejsMate); // using ejsMate for layout support in EJS
//session handler
app.use(
session({
store: MongoStore.create({
mongoUrl: process.env.ATLAS_DB,
crypto: {
secret: process.env.SESSION_SECRET
},
touchAfter: 24 * 60 * 60,
ttl: 7 * 24 * 60 * 60, //Set TTL explicitly to 7 days
}),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
httpOnly: true, // prevents client-side JavaScript from accessing the cookie
},
})
);
app.use(flash());
// Passport.js configuration and also it uses session so we have to use passport just after session
app.use(passport.initialize());//Initializes Passport middleware.
app.use(passport.session());//Enables persistent login sessions using cookies and express-session.
passport.use(new LocalStrategy(User.authenticate())); // Tells Passport to use the local strategy (username + password).User.authenticate() is provided by passport-local-mongoose.
passport.serializeUser(User.serializeUser()); // These handle how user data is stored in and retrieved from the session.
passport.deserializeUser(User.deserializeUser()); // These handle how user data is stored in and retrieved from the session.
/* -------------------------- connecting to MongoDB ------------------------- */
const MONGO_URL = process.env.ATLAS_DB;
main()
.then(() => {
console.log("Connected to MongoDB");
cron.schedule("*/20 * * * *", async () => {
console.log("⏱️ Running meter simulation...");
try {
await simulateNewReading();
} catch (err) {
console.error("❌ Error in simulateNewReading:", err.message);
}
});
// Payment request every 20 minutes
cron.schedule("*/30 * * * *", async () => {
console.log("💸 Sending payment requests for all meters...");
try {
const meters = await Meter.find({}).populate("owner");
for (const meter of meters) {
await sendPaymentRequestEmail(meter);
}
console.log("✅ Payment requests sent for all meters.");
} catch (err) {
console.error("❌ Error in payment request cron:", err.message);
}
});
})
.catch((err) => console.log(err));
async function main() {
try {
await mongoose.connect(MONGO_URL, {
serverSelectionTimeoutMS: 10000,
socketTimeoutMS: 45000,
});
} catch (err) {
console.error("Initial MongoDB connection error:", err);
process.exit(1);
}
}
// Middleware to expose flash messages to views
app.use((req, res, next) => {
res.locals.success = req.flash("success");
res.locals.error = req.flash("error");
res.locals.currentUser = req.user; // to access current user in views
next();
});
// routes
app.get("/", (req, res) => {
res.redirect("/meters");
});
app.get("/health", (req, res) => {
res.send("OK");
});
app.use("/", userRouter);
app.use("/meters", meterRouter);
app.use("/alerts", alertRouter);
// if no above route matches, this middleware will be called
app.all("*", (req, res, next) => {
next(new ExpressError(404, "Page Not Found"));
});
// Custom error handling middleware jo saare errors ko handle karega
app.use((err, req, res, next) => {
let { statusCode = 500, message = "Something went wrong" } = err;
res.render("error.ejs", { statusCode, message });
});
const port = process.env.PORT || 3000;
const server = app.listen(port, "0.0.0.0", () => {
console.log(`Server is listening on port ${port}`);
});
// Set timeout values to prevent 502 errors on Render
server.keepAliveTimeout = 120 * 1000; // 120 seconds
server.headersTimeout = 130 * 1000; // must be > keepAliveTimeout
// Catch unhandled promise rejections (like DB or async errors not caught properly)
process.on("unhandledRejection", (err) => {
console.error("Unhandled rejection:", err);
});