-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
190 lines (171 loc) · 7.77 KB
/
app.js
File metadata and controls
190 lines (171 loc) · 7.77 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
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
188
189
190
// use dotenv under this condition
if(process.env.NODE_ENV !== "production") { //if we are in develop environment mode
require('dotenv').config(); //require the .env file
}
// require('dotenv').config();
// console.log(process.env.SECRETE); //print SECRETE value in .env
// console.log(process.env.API_KEY);
const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
const ejsMate = require('ejs-mate'); //include ejs-mate engine
const session = require('express-session'); // used to generate session
const flash = require('connect-flash'); // for flash message
const methodOverride = require('method-override');
const passport = require('passport');
const LocalStrategy = require('passport-local');
const ExpressError = require('./utils/ExpressError');
const User = require('./models/user'); //require the User model
const mongoSanitize = require('express-mongo-sanitize');
const helmet = require('helmet'); // helps you secure your Express apps by setting various HTTP headers
const MongoDBStore = require("connect-mongo");
// import from routes folder, then use later
const campgroundRoutes = require('./routes/campgrounds');
const reviewRoutes = require('./routes/reviews');
const userRoutes = require('./routes/users');
// developer mode || public host mode
const dbUrl = process.env.DB_URL || 'mongodb://localhost:27017/yelp-camp';
mongoose.connect(dbUrl);
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => {
console.log("Database connected.")
})
const app = express();
app.engine('ejs', ejsMate); //use ejs-mate engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
//============== use middleware =====================
const secret = process.env.SESSION_SECRET || 'thisshouldbrabettersecret!';
const store = MongoDBStore.create({
mongoUrl: dbUrl, //use the same db location
secret,
touchAfter: 24 * 60 * 60, // time period in seconds
});
store.on("error", function(e){
console.log("SESSION STORE ERROR");
})
// session/cookies generator, each request generate different cookies
const sessionConfig = {
store, // pass store here
name: 'session', // renames cookie, instead of using the default name (easily to be stolen)
secret,
resave: false,
saveUninitialized: true,
cookies: {
httpOnly: true, // HttpOnly: http only assessible, not for js. It is an additional flag included in a Set-Cookie HTTP response header. Using the HttpOnly flag when generating a cookie helps mitigate the risk of client side script accessing the protected cookie (if the browser supports it).
// secure: true, // set http secure: even when user logged in, the user has not been logged in
// user shouldn't login forever, must set a expire date for session
expires: Date.now() + 1000 * 60 * 60 * 24 * 7, //Date.now() return milliseconds. 1000: convert to second, 60: to min, 60: to hours, 24: to days, 7: to weeks
maxAge: 1000 * 60 * 60 * 24 * 7
}
}
app.use(session(sessionConfig)); // this must be used before passport.session
app.use(flash());
/**
* config passport
*/
app.use(passport.initialize());
app.use(passport.session());
//Static passport methods are exposed on the model constructor. ↓
passport.use(new LocalStrategy(User.authenticate())); //STATIC: authenticate() Generates a function that is used in Passport's LocalStrategy
// use static serialize and deserialize of model for passport session support
passport.serializeUser(User.serializeUser()); // let user get into the session
passport.deserializeUser(User.deserializeUser()); // let user get out of the session
app.use(express.urlencoded({ extended: true }));
app.use(methodOverride('_method')); // set query string inside
app.use(express.static(path.join(__dirname, 'public'))); // use that dir as static folder: stores static files, imgs,...
app.use(mongoSanitize()); //Way1: remove the whole thing if there is operater injected
// app.use(mongoSanitize({ //Way2: or we can replace operator to other symbol to avoid injection
// replaceWith: '_',
// }));
// ============== config with helmet to secure the web =====================
// This disables the `contentSecurityPolicy` middleware but keeps the rest.
// app.use(helmet({contentSecurityPolicy:false,}));
const scriptSrcUrls = [
"https://stackpath.bootstrapcdn.com/",
"https://api.tiles.mapbox.com/",
"https://api.mapbox.com/",
"https://kit.fontawesome.com/",
"https://cdnjs.cloudflare.com/",
"https://cdn.jsdelivr.net",
];
const styleSrcUrls = [
"https://kit-free.fontawesome.com/",
"https://stackpath.bootstrapcdn.com/",
"https://api.mapbox.com/",
"https://api.tiles.mapbox.com/",
"https://fonts.googleapis.com/",
"https://use.fontawesome.com/",
"https://cdn.jsdelivr.net/",
];
const connectSrcUrls = [
"https://api.mapbox.com/",
"https://a.tiles.mapbox.com/",
"https://b.tiles.mapbox.com/",
"https://events.mapbox.com/",
];
const fontSrcUrls = [];
app.use(
helmet.contentSecurityPolicy({ // include above urls as local self resource
directives: {
defaultSrc: [],
connectSrc: ["'self'", ...connectSrcUrls],
scriptSrc: ["'unsafe-inline'", "'self'", ...scriptSrcUrls],
styleSrc: ["'self'", "'unsafe-inline'", ...styleSrcUrls],
workerSrc: ["'self'", "blob:"],
objectSrc: [],
imgSrc: [
"'self'",
"blob:",
"data:",
"https://res.cloudinary.com/dbu69dh96/", //SHOULD MATCH YOUR CLOUDINARY ACCOUNT (id name)!
"https://images.unsplash.com/",
],
fontSrc: ["'self'", ...fontSrcUrls],
},
})
);
// ============ area to create new property for res ===================
// make a flash message: save successfully--show, refresh the page--gone
// set it before routers, then use next() pass to routers
app.use((req, res, next) => {
// console.log(req.session);
// console.log(req.query); // senitizing testing: print the injected query from url
res.locals.currentUser = req.user;
res.locals.success = req.flash('success'); // make a middleware, call success as response
res.locals.error = req.flash('error'); // show flash error message
next();
})
// ================ routes =====================
// app.get('/fakeUser', async (req, res) => {
// const user = new User({email: 'wenjun@gmail.com', username: 'wen'});
// //Static passport methods
// const newUser = await User.register(user, 'passapassword'); // register(user, password, cb) Convenience method to register a new user instance with a given password. Checks if username is unique.
// res.send(newUser);
// })
// use the routes
app.use('/campgrounds', campgroundRoutes); // all campground routes/paths start from /campgrounds
app.use('/campgrounds/:id/reviews', reviewRoutes);
app.use('/', userRoutes);
app.get('/', (req, res) => {
//res.send('Hello from Yelp Camp!')
res.render('home');
})
// apply for every request with invalid url path: 404 error
app.all('*', (req, res, next) => {
//res.send('404!!!');
next(new ExpressError('Page Not Found', 404));
})
// apply for every request with data validation error: 500 server error
app.use((err, req, res, next) =>{
// we can set default value in { var=default }, if no value provided on these params
//const { statusCode = 500, message = 'Something Went Wrong' } = err;
//res.status(statusCode).send(message);
const { statusCode = 500 } = err;
if( !err.message ) err.message = 'Oh No! Something Went Wrong!'; // if err.message doen't set, set it to default: ...
res.status(statusCode).render('error', { err }); //render error.ejs, pass err to template
})
app.listen(3000, () => {
console.log("Serving on port 3000")
})