-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
187 lines (159 loc) · 5.79 KB
/
app.js
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
const express = require('express');
// create a new app
const app = express();
// the former is the connection string configured in production env (e.g. on Heroku or .env file), the later is local db
const dbUrl = process.env.DB_URL || 'mongodb://localhost:27017/kidsfun'
// import and configure dotenv for development mode
if (process.env.NODE_ENV !== "production") {
require('dotenv').config();
// development db
} else {
// trust first proxy
app.set('trust proxy', 1);
}
const path = require('path');
const ExpressError = require('./utils/ExpressError');
const activityRoutes = require('./routes/activities');
const reviewRoutes = require('./routes/reviews');
const userRoutes = require('./routes/users');
const User = require('./models/user');
const mongoose = require('mongoose');
mongoose.connect(dbUrl);
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => console.log("Dababase connected"));
// set Express views location
app.set('views', path.join(__dirname, 'views'));
// set view engine to use ejs
app.set('view engine', 'ejs');
// Registers the given template engine 'ejsMate' for 'ejs' files.
const ejsMate = require('ejs-mate');
app.engine('ejs', ejsMate);
// middleware to parse form data
app.use(express.urlencoded({ extended: true }));
// middleware to override POST method
const methodOverride = require("method-override");
app.use(methodOverride('_method'));
// set static file folder to root/public
app.use(express.static(path.join(__dirname, 'public')));
// To remove data using these defaults:
const mongoSanitize = require('express-mongo-sanitize');
app.use(mongoSanitize());
// use helmet security package but customize the contentSecurityPolicy and crossOriginEmbedderPolicy middleware
const scriptSrcUrls = [
"https://api.mapbox.com/",
"https://cdn.jsdelivr.net/",
"https://res.cloudinary.com/vivalkm/"
];
const styleSrcUrls = [
"https://api.mapbox.com/",
"https://cdn.jsdelivr.net/",
"https://res.cloudinary.com/vivalkm/"
];
const connectSrcUrls = [
"https://api.mapbox.com",
"https://events.mapbox.com",
"https://res.cloudinary.com/vivalkm/"
];
const fontSrcUrls = [];
const helmet = require('helmet');
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [],
connectSrc: ["'self'", ...connectSrcUrls],
scriptSrc: ["'unsafe-inline'", "'self'", ...scriptSrcUrls],
styleSrc: ["'unsafe-inline'", "'self'", ...styleSrcUrls],
workerSrc: ["'self'", "blob:"],
objectSrc: [],
imgSrc: [
"'self'",
"blob:",
"data:",
"https://res.cloudinary.com/vivalkm/",
"https://images.unsplash.com/"
],
fontSrc: ["'self'", ...fontSrcUrls],
mediaSrc: ["https://res.cloudinary.com/vivalkm/"],
childSrc: ["blob:"]
}
},
crossOriginEmbedderPolicy: false
})
);
// use MongoDB session store
const MongoStore = require('connect-mongo');
const store = MongoStore.create({
mongoUrl: dbUrl,
secret: process.env.SECRET || 'thisisasecret',
// update session only one time in a period of 24 hours if there is no change in session
touchAfter: 24 * 60 * 60
});
store.on('error', (e) => console.log("Session store error:", e));
// session config
const sessionConfig = {
// change the default name to something else
name: 'status',
secret: process.env.SECRET || 'thisisasecret',
resave: false,
saveUninitialized: true,
cookie: {
// JavaScript will not be able to read this authentication cookie in case of XSS exploitation
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7
},
store: store
}
// use secure cookies in production, but allowing for testing in development
if (process.env.NODE_ENV !== "production") {
// serve secure cookies, the cookie will only be sent over HTTPS, which is HTTP over SSL/TLS
sessionConfig.cookie.secure = true;
}
// make sure to use session() before passport.session() to ensure that the login session is restored in the correct order
const session = require('express-session');
app.use(session(sessionConfig));
// configure passport
const passport = require('passport');
const LocalStrategy = require('passport-local');
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// middleware to handle flash and login status
const flash = require('connect-flash');
app.use(flash());
app.use((req, res, next) => {
res.locals.currentUser = req.user;
console.log(res.locals.currentUser);
res.locals.success = req.flash("success");
res.locals.error = req.flash("error");
next();
});
app.use('/activities', activityRoutes);
app.use('/activities/:id/reviews', reviewRoutes);
app.use('/', userRoutes);
app.get('/', (req, res) => {
res.render('home');
});
// add 404 route handler after all other routes
app.all('*', (req, res, next) => {
// pass the customized error object to error handler
next(new ExpressError("Page not found", 404));
});
app.use((err, req, res, next) => {
// get statusCode with `500` as default
const { statusCode = 500 } = err;
// set default error message if err contains no message
if (!err.message) err.message = "Oh no, something went wrong!";
if (process.env.NODE_ENV === 'production') {
err.stack = '';
}
// render the error view
res.status(statusCode).render('error', { err });
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Serving on port ${port}`);
})