Showing
22 changed files
with
1090 additions
and
17 deletions
Project/config/passport.js
0 → 100644
1 | +var passport = require('passport'); | ||
2 | +var LocalStrategy = require('passport-local').Strategy; // 1 | ||
3 | +var User = require('../models/User'); | ||
4 | + | ||
5 | +// serialize & deserialize User // 2 | ||
6 | +passport.serializeUser(function(user, done) { | ||
7 | + done(null, user.id); | ||
8 | +}); | ||
9 | +passport.deserializeUser(function(id, done) { | ||
10 | + User.findOne({_id:id}, function(err, user) { | ||
11 | + done(err, user); | ||
12 | + }); | ||
13 | +}); | ||
14 | + | ||
15 | +// local strategy // 3 | ||
16 | +passport.use('local-login', | ||
17 | + new LocalStrategy({ | ||
18 | + usernameField : 'username', // 3-1 | ||
19 | + passwordField : 'password', // 3-1 | ||
20 | + passReqToCallback : true | ||
21 | + }, | ||
22 | + function(req, username, password, done) { // 3-2 | ||
23 | + User.findOne({username:username}) | ||
24 | + .select({password:1}) | ||
25 | + .exec(function(err, user) { | ||
26 | + if (err) return done(err); | ||
27 | + | ||
28 | + if (user && user.authenticate(password)){ // 3-3 | ||
29 | + return done(null, user); | ||
30 | + } | ||
31 | + else { | ||
32 | + req.flash('username', username); | ||
33 | + req.flash('errors', {login:'The username or password is incorrect.'}); | ||
34 | + return done(null, false); | ||
35 | + } | ||
36 | + }); | ||
37 | + } | ||
38 | + ) | ||
39 | +); | ||
40 | + | ||
41 | +module.exports = passport; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -const express = require('express'); | 1 | +var express = require('express'); |
2 | -const mongoose = require('mongoose'); | 2 | +var mongoose = require('mongoose'); |
3 | -const bodyParser = require('body-parser'); | 3 | +var bodyParser = require('body-parser'); |
4 | -const methodOverride = require('body-parser'); | 4 | +var methodOverride = require('method-override'); |
5 | -const app = express(); | 5 | +var flash = require('connect-flash'); |
6 | +var session = require('express-session'); | ||
7 | +var passport = require('./config/passport'); | ||
8 | +//require('./config/passport'); | ||
6 | 9 | ||
10 | +var app = express(); | ||
7 | 11 | ||
8 | // DB Setting | 12 | // DB Setting |
9 | mongoose.set('useNewUrlParser', true); | 13 | mongoose.set('useNewUrlParser', true); |
... | @@ -36,11 +40,32 @@ app.engine('html', require('ejs').renderFile); | ... | @@ -36,11 +40,32 @@ app.engine('html', require('ejs').renderFile); |
36 | app.use(bodyParser.json()); | 40 | app.use(bodyParser.json()); |
37 | app.use(bodyParser.urlencoded({extended:true})); | 41 | app.use(bodyParser.urlencoded({extended:true})); |
38 | app.use(methodOverride('_method')); | 42 | app.use(methodOverride('_method')); |
43 | +app.use(flash()); | ||
44 | +app.use(session({ | ||
45 | + secret:'MySecret', | ||
46 | + resave:true, | ||
47 | + saveUninitialized:true | ||
48 | +})); | ||
49 | + | ||
50 | + | ||
51 | + | ||
52 | +// Passport | ||
53 | +app.use(passport.initialize()); | ||
54 | +app.use(passport.session()); | ||
55 | + | ||
56 | + | ||
57 | +// Custom Middlewares | ||
58 | +app.use(function(req,res,next){ | ||
59 | + res.locals.isAuthenticated = req.isAuthenticated(); | ||
60 | + res.locals.currentUser = req.user; | ||
61 | + next(); | ||
62 | +}); | ||
39 | 63 | ||
40 | 64 | ||
41 | // Routes | 65 | // Routes |
42 | app.use('/', require('./routes/home')); | 66 | app.use('/', require('./routes/home')); |
43 | app.use('/posts', require('./routes/posts')); | 67 | app.use('/posts', require('./routes/posts')); |
68 | +app.use('/users', require('./routes/users')) | ||
44 | 69 | ||
45 | 70 | ||
46 | // Server | 71 | // Server | ... | ... |
Project/models/User.js
0 → 100644
1 | +var mongoose = require('mongoose'); | ||
2 | +var bcrypt = require('bcryptjs'); // 1 | ||
3 | + | ||
4 | +// schema //1 | ||
5 | +var userSchema = mongoose.Schema({ | ||
6 | + username:{ | ||
7 | + type:String, | ||
8 | + required:[true,'Username is required!'], | ||
9 | + match:[/^.{4,12}$/,'Should be 4-12 characters!'], | ||
10 | + trim:true, | ||
11 | + unique:true | ||
12 | + }, | ||
13 | + password:{ | ||
14 | + type:String, | ||
15 | + required:[true,'Password is required!'], | ||
16 | + select:false | ||
17 | + }, | ||
18 | + name:{ | ||
19 | + type:String, | ||
20 | + required:[true,'Name is required!'], | ||
21 | + match:[/^.{4,12}$/,'Should be 4-12 characters!'], | ||
22 | + trim:true | ||
23 | + }, | ||
24 | + email:{ | ||
25 | + type:String, | ||
26 | + match:[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,'Should be a vaild email address!'], | ||
27 | + trim:true | ||
28 | + } | ||
29 | +},{ | ||
30 | + toObject:{virtuals:true} | ||
31 | +}); | ||
32 | + | ||
33 | + | ||
34 | +// virtuals // 2 | ||
35 | +userSchema.virtual('passwordConfirmation') | ||
36 | + .get(function(){ return this._passwordConfirmation; }) | ||
37 | + .set(function(value){ this._passwordConfirmation=value; }); | ||
38 | + | ||
39 | +userSchema.virtual('originalPassword') | ||
40 | + .get(function(){ return this._originalPassword; }) | ||
41 | + .set(function(value){ this._originalPassword=value; }); | ||
42 | + | ||
43 | +userSchema.virtual('currentPassword') | ||
44 | + .get(function(){ return this._currentPassword; }) | ||
45 | + .set(function(value){ this._currentPassword=value; }); | ||
46 | + | ||
47 | +userSchema.virtual('newPassword') | ||
48 | + .get(function(){ return this._newPassword; }) | ||
49 | + .set(function(value){ this._newPassword=value; }); | ||
50 | + | ||
51 | + | ||
52 | +// password validation // 2 | ||
53 | +var passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$/; | ||
54 | +var passwordRegexErrorMessage = 'Should be minimum 8 characters of alphabet and number combination!'; | ||
55 | +userSchema.path('password').validate(function(v) { | ||
56 | + var user = this; | ||
57 | + | ||
58 | + | ||
59 | + // create user | ||
60 | + if(user.isNew){ | ||
61 | + if(!user.passwordConfirmation){ | ||
62 | + user.invalidate('passwordConfirmation', 'Password Confirmation is required.'); | ||
63 | + } | ||
64 | + | ||
65 | + if(!passwordRegex.test(user.password)){ | ||
66 | + user.invalidate('password', passwordRegexErrorMessage); | ||
67 | + } | ||
68 | + else if(user.password !== user.passwordConfirmation) { | ||
69 | + user.invalidate('passwordConfirmation', 'Password Confirmation does not matched!'); | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + // update user | ||
74 | + if(!user.isNew){ | ||
75 | + if(!user.currentPassword){ | ||
76 | + user.invalidate('currentPassword', 'Current Password is required!'); | ||
77 | + } | ||
78 | + else if(!bcrypt.compareSync(user.currentPassword, user.originalPassword)){ | ||
79 | + user.invalidate('currentPassword', 'Current Password is invalid!'); | ||
80 | + } | ||
81 | + | ||
82 | + if(user.newPassword && !passwordRegex.test(user.newPassword)){ | ||
83 | + user.invalidate("newPassword", passwordRegexErrorMessage); | ||
84 | + } | ||
85 | + else if(user.newPassword !== user.passwordConfirmation) { | ||
86 | + user.invalidate('passwordConfirmation', 'Password Confirmation does not matched!'); | ||
87 | + } | ||
88 | + } | ||
89 | +}); | ||
90 | + | ||
91 | +userSchema.pre('save', function (next){ | ||
92 | + var user = this; | ||
93 | + if(!user.isModified('password')){ // 3-1 | ||
94 | + return next(); | ||
95 | + } | ||
96 | + else { | ||
97 | + user.password = bcrypt.hashSync(user.password); //3-2 | ||
98 | + return next(); | ||
99 | + } | ||
100 | +}); | ||
101 | + | ||
102 | +// model methods // 4 | ||
103 | +userSchema.methods.authenticate = function (password) { | ||
104 | + var user = this; | ||
105 | + return bcrypt.compareSync(password,user.password); | ||
106 | +}; | ||
107 | + | ||
108 | +// model & export | ||
109 | +var User = mongoose.model('user',userSchema); | ||
110 | +module.exports = User; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -9,11 +9,16 @@ | ... | @@ -9,11 +9,16 @@ |
9 | "version": "1.0.0", | 9 | "version": "1.0.0", |
10 | "license": "ISC", | 10 | "license": "ISC", |
11 | "dependencies": { | 11 | "dependencies": { |
12 | + "bcryptjs": "^2.4.3", | ||
12 | "body-parser": "^1.19.0", | 13 | "body-parser": "^1.19.0", |
14 | + "connect-flash": "^0.1.1", | ||
13 | "ejs": "^3.1.6", | 15 | "ejs": "^3.1.6", |
14 | "express": "^4.17.1", | 16 | "express": "^4.17.1", |
17 | + "express-session": "^1.17.1", | ||
15 | "method-override": "^3.0.0", | 18 | "method-override": "^3.0.0", |
16 | - "mongoose": "^5.12.8" | 19 | + "mongoose": "^5.12.8", |
20 | + "passport": "^0.4.1", | ||
21 | + "passport-local": "^1.0.0" | ||
17 | } | 22 | } |
18 | }, | 23 | }, |
19 | "node_modules/@types/bson": { | 24 | "node_modules/@types/bson": { |
... | @@ -76,6 +81,11 @@ | ... | @@ -76,6 +81,11 @@ |
76 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", | 81 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", |
77 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" | 82 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" |
78 | }, | 83 | }, |
84 | + "node_modules/bcryptjs": { | ||
85 | + "version": "2.4.3", | ||
86 | + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", | ||
87 | + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" | ||
88 | + }, | ||
79 | "node_modules/bl": { | 89 | "node_modules/bl": { |
80 | "version": "2.2.1", | 90 | "version": "2.2.1", |
81 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", | 91 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", |
... | @@ -166,6 +176,14 @@ | ... | @@ -166,6 +176,14 @@ |
166 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", | 176 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", |
167 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" | 177 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" |
168 | }, | 178 | }, |
179 | + "node_modules/connect-flash": { | ||
180 | + "version": "0.1.1", | ||
181 | + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", | ||
182 | + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=", | ||
183 | + "engines": { | ||
184 | + "node": ">= 0.4.0" | ||
185 | + } | ||
186 | + }, | ||
169 | "node_modules/content-disposition": { | 187 | "node_modules/content-disposition": { |
170 | "version": "0.5.3", | 188 | "version": "0.5.3", |
171 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", | 189 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", |
... | @@ -320,6 +338,37 @@ | ... | @@ -320,6 +338,37 @@ |
320 | "node": ">= 0.10.0" | 338 | "node": ">= 0.10.0" |
321 | } | 339 | } |
322 | }, | 340 | }, |
341 | + "node_modules/express-session": { | ||
342 | + "version": "1.17.1", | ||
343 | + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", | ||
344 | + "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", | ||
345 | + "dependencies": { | ||
346 | + "cookie": "0.4.0", | ||
347 | + "cookie-signature": "1.0.6", | ||
348 | + "debug": "2.6.9", | ||
349 | + "depd": "~2.0.0", | ||
350 | + "on-headers": "~1.0.2", | ||
351 | + "parseurl": "~1.3.3", | ||
352 | + "safe-buffer": "5.2.0", | ||
353 | + "uid-safe": "~2.1.5" | ||
354 | + }, | ||
355 | + "engines": { | ||
356 | + "node": ">= 0.8.0" | ||
357 | + } | ||
358 | + }, | ||
359 | + "node_modules/express-session/node_modules/depd": { | ||
360 | + "version": "2.0.0", | ||
361 | + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", | ||
362 | + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", | ||
363 | + "engines": { | ||
364 | + "node": ">= 0.8" | ||
365 | + } | ||
366 | + }, | ||
367 | + "node_modules/express-session/node_modules/safe-buffer": { | ||
368 | + "version": "5.2.0", | ||
369 | + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", | ||
370 | + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" | ||
371 | + }, | ||
323 | "node_modules/filelist": { | 372 | "node_modules/filelist": { |
324 | "version": "1.0.2", | 373 | "version": "1.0.2", |
325 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", | 374 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", |
... | @@ -676,6 +725,14 @@ | ... | @@ -676,6 +725,14 @@ |
676 | "node": ">= 0.8" | 725 | "node": ">= 0.8" |
677 | } | 726 | } |
678 | }, | 727 | }, |
728 | + "node_modules/on-headers": { | ||
729 | + "version": "1.0.2", | ||
730 | + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", | ||
731 | + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", | ||
732 | + "engines": { | ||
733 | + "node": ">= 0.8" | ||
734 | + } | ||
735 | + }, | ||
679 | "node_modules/optional-require": { | 736 | "node_modules/optional-require": { |
680 | "version": "1.0.3", | 737 | "version": "1.0.3", |
681 | "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", | 738 | "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", |
... | @@ -692,11 +749,47 @@ | ... | @@ -692,11 +749,47 @@ |
692 | "node": ">= 0.8" | 749 | "node": ">= 0.8" |
693 | } | 750 | } |
694 | }, | 751 | }, |
752 | + "node_modules/passport": { | ||
753 | + "version": "0.4.1", | ||
754 | + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", | ||
755 | + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", | ||
756 | + "dependencies": { | ||
757 | + "passport-strategy": "1.x.x", | ||
758 | + "pause": "0.0.1" | ||
759 | + }, | ||
760 | + "engines": { | ||
761 | + "node": ">= 0.4.0" | ||
762 | + } | ||
763 | + }, | ||
764 | + "node_modules/passport-local": { | ||
765 | + "version": "1.0.0", | ||
766 | + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", | ||
767 | + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", | ||
768 | + "dependencies": { | ||
769 | + "passport-strategy": "1.x.x" | ||
770 | + }, | ||
771 | + "engines": { | ||
772 | + "node": ">= 0.4.0" | ||
773 | + } | ||
774 | + }, | ||
775 | + "node_modules/passport-strategy": { | ||
776 | + "version": "1.0.0", | ||
777 | + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", | ||
778 | + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", | ||
779 | + "engines": { | ||
780 | + "node": ">= 0.4.0" | ||
781 | + } | ||
782 | + }, | ||
695 | "node_modules/path-to-regexp": { | 783 | "node_modules/path-to-regexp": { |
696 | "version": "0.1.7", | 784 | "version": "0.1.7", |
697 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", | 785 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", |
698 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" | 786 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" |
699 | }, | 787 | }, |
788 | + "node_modules/pause": { | ||
789 | + "version": "0.0.1", | ||
790 | + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", | ||
791 | + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" | ||
792 | + }, | ||
700 | "node_modules/process-nextick-args": { | 793 | "node_modules/process-nextick-args": { |
701 | "version": "2.0.1", | 794 | "version": "2.0.1", |
702 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | 795 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", |
... | @@ -722,6 +815,14 @@ | ... | @@ -722,6 +815,14 @@ |
722 | "node": ">=0.6" | 815 | "node": ">=0.6" |
723 | } | 816 | } |
724 | }, | 817 | }, |
818 | + "node_modules/random-bytes": { | ||
819 | + "version": "1.0.0", | ||
820 | + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", | ||
821 | + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", | ||
822 | + "engines": { | ||
823 | + "node": ">= 0.8" | ||
824 | + } | ||
825 | + }, | ||
725 | "node_modules/range-parser": { | 826 | "node_modules/range-parser": { |
726 | "version": "1.2.1", | 827 | "version": "1.2.1", |
727 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", | 828 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", |
... | @@ -898,6 +999,17 @@ | ... | @@ -898,6 +999,17 @@ |
898 | "node": ">= 0.6" | 999 | "node": ">= 0.6" |
899 | } | 1000 | } |
900 | }, | 1001 | }, |
1002 | + "node_modules/uid-safe": { | ||
1003 | + "version": "2.1.5", | ||
1004 | + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", | ||
1005 | + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", | ||
1006 | + "dependencies": { | ||
1007 | + "random-bytes": "~1.0.0" | ||
1008 | + }, | ||
1009 | + "engines": { | ||
1010 | + "node": ">= 0.8" | ||
1011 | + } | ||
1012 | + }, | ||
901 | "node_modules/unpipe": { | 1013 | "node_modules/unpipe": { |
902 | "version": "1.0.0", | 1014 | "version": "1.0.0", |
903 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | 1015 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", |
... | @@ -983,6 +1095,11 @@ | ... | @@ -983,6 +1095,11 @@ |
983 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", | 1095 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", |
984 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" | 1096 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" |
985 | }, | 1097 | }, |
1098 | + "bcryptjs": { | ||
1099 | + "version": "2.4.3", | ||
1100 | + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", | ||
1101 | + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" | ||
1102 | + }, | ||
986 | "bl": { | 1103 | "bl": { |
987 | "version": "2.2.1", | 1104 | "version": "2.2.1", |
988 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", | 1105 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", |
... | @@ -1061,6 +1178,11 @@ | ... | @@ -1061,6 +1178,11 @@ |
1061 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", | 1178 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", |
1062 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" | 1179 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" |
1063 | }, | 1180 | }, |
1181 | + "connect-flash": { | ||
1182 | + "version": "0.1.1", | ||
1183 | + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", | ||
1184 | + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" | ||
1185 | + }, | ||
1064 | "content-disposition": { | 1186 | "content-disposition": { |
1065 | "version": "0.5.3", | 1187 | "version": "0.5.3", |
1066 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", | 1188 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", |
... | @@ -1182,6 +1304,33 @@ | ... | @@ -1182,6 +1304,33 @@ |
1182 | "vary": "~1.1.2" | 1304 | "vary": "~1.1.2" |
1183 | } | 1305 | } |
1184 | }, | 1306 | }, |
1307 | + "express-session": { | ||
1308 | + "version": "1.17.1", | ||
1309 | + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", | ||
1310 | + "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", | ||
1311 | + "requires": { | ||
1312 | + "cookie": "0.4.0", | ||
1313 | + "cookie-signature": "1.0.6", | ||
1314 | + "debug": "2.6.9", | ||
1315 | + "depd": "~2.0.0", | ||
1316 | + "on-headers": "~1.0.2", | ||
1317 | + "parseurl": "~1.3.3", | ||
1318 | + "safe-buffer": "5.2.0", | ||
1319 | + "uid-safe": "~2.1.5" | ||
1320 | + }, | ||
1321 | + "dependencies": { | ||
1322 | + "depd": { | ||
1323 | + "version": "2.0.0", | ||
1324 | + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", | ||
1325 | + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" | ||
1326 | + }, | ||
1327 | + "safe-buffer": { | ||
1328 | + "version": "5.2.0", | ||
1329 | + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", | ||
1330 | + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" | ||
1331 | + } | ||
1332 | + } | ||
1333 | + }, | ||
1185 | "filelist": { | 1334 | "filelist": { |
1186 | "version": "1.0.2", | 1335 | "version": "1.0.2", |
1187 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", | 1336 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", |
... | @@ -1433,6 +1582,11 @@ | ... | @@ -1433,6 +1582,11 @@ |
1433 | "ee-first": "1.1.1" | 1582 | "ee-first": "1.1.1" |
1434 | } | 1583 | } |
1435 | }, | 1584 | }, |
1585 | + "on-headers": { | ||
1586 | + "version": "1.0.2", | ||
1587 | + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", | ||
1588 | + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" | ||
1589 | + }, | ||
1436 | "optional-require": { | 1590 | "optional-require": { |
1437 | "version": "1.0.3", | 1591 | "version": "1.0.3", |
1438 | "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", | 1592 | "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", |
... | @@ -1443,11 +1597,38 @@ | ... | @@ -1443,11 +1597,38 @@ |
1443 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | 1597 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", |
1444 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" | 1598 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" |
1445 | }, | 1599 | }, |
1600 | + "passport": { | ||
1601 | + "version": "0.4.1", | ||
1602 | + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", | ||
1603 | + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", | ||
1604 | + "requires": { | ||
1605 | + "passport-strategy": "1.x.x", | ||
1606 | + "pause": "0.0.1" | ||
1607 | + } | ||
1608 | + }, | ||
1609 | + "passport-local": { | ||
1610 | + "version": "1.0.0", | ||
1611 | + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", | ||
1612 | + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", | ||
1613 | + "requires": { | ||
1614 | + "passport-strategy": "1.x.x" | ||
1615 | + } | ||
1616 | + }, | ||
1617 | + "passport-strategy": { | ||
1618 | + "version": "1.0.0", | ||
1619 | + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", | ||
1620 | + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" | ||
1621 | + }, | ||
1446 | "path-to-regexp": { | 1622 | "path-to-regexp": { |
1447 | "version": "0.1.7", | 1623 | "version": "0.1.7", |
1448 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", | 1624 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", |
1449 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" | 1625 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" |
1450 | }, | 1626 | }, |
1627 | + "pause": { | ||
1628 | + "version": "0.0.1", | ||
1629 | + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", | ||
1630 | + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" | ||
1631 | + }, | ||
1451 | "process-nextick-args": { | 1632 | "process-nextick-args": { |
1452 | "version": "2.0.1", | 1633 | "version": "2.0.1", |
1453 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | 1634 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", |
... | @@ -1467,6 +1648,11 @@ | ... | @@ -1467,6 +1648,11 @@ |
1467 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", | 1648 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", |
1468 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" | 1649 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" |
1469 | }, | 1650 | }, |
1651 | + "random-bytes": { | ||
1652 | + "version": "1.0.0", | ||
1653 | + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", | ||
1654 | + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" | ||
1655 | + }, | ||
1470 | "range-parser": { | 1656 | "range-parser": { |
1471 | "version": "1.2.1", | 1657 | "version": "1.2.1", |
1472 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", | 1658 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", |
... | @@ -1618,6 +1804,14 @@ | ... | @@ -1618,6 +1804,14 @@ |
1618 | "mime-types": "~2.1.24" | 1804 | "mime-types": "~2.1.24" |
1619 | } | 1805 | } |
1620 | }, | 1806 | }, |
1807 | + "uid-safe": { | ||
1808 | + "version": "2.1.5", | ||
1809 | + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", | ||
1810 | + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", | ||
1811 | + "requires": { | ||
1812 | + "random-bytes": "~1.0.0" | ||
1813 | + } | ||
1814 | + }, | ||
1621 | "unpipe": { | 1815 | "unpipe": { |
1622 | "version": "1.0.0", | 1816 | "version": "1.0.0", |
1623 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | 1817 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | ... | ... |
... | @@ -10,10 +10,15 @@ | ... | @@ -10,10 +10,15 @@ |
10 | "author": "", | 10 | "author": "", |
11 | "license": "ISC", | 11 | "license": "ISC", |
12 | "dependencies": { | 12 | "dependencies": { |
13 | + "bcryptjs": "^2.4.3", | ||
13 | "body-parser": "^1.19.0", | 14 | "body-parser": "^1.19.0", |
15 | + "connect-flash": "^0.1.1", | ||
14 | "ejs": "^3.1.6", | 16 | "ejs": "^3.1.6", |
15 | "express": "^4.17.1", | 17 | "express": "^4.17.1", |
18 | + "express-session": "^1.17.1", | ||
16 | "method-override": "^3.0.0", | 19 | "method-override": "^3.0.0", |
17 | - "mongoose": "^5.12.8" | 20 | + "mongoose": "^5.12.8", |
21 | + "passport": "^0.4.1", | ||
22 | + "passport-local": "^1.0.0" | ||
18 | } | 23 | } |
19 | } | 24 | } | ... | ... |
1 | body { | 1 | body { |
2 | - font-family: 'Open Sans', sans-serif; | 2 | + |
3 | -} | 3 | + } |
4 | - | 4 | + .h1 { |
5 | -h1 { | ||
6 | font-family: 'PT Serif', serif; | 5 | font-family: 'PT Serif', serif; |
7 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
6 | + } | ||
7 | + .breadcrumb-item { | ||
8 | + font-size: 0.8em !important; | ||
9 | + } | ||
10 | + .ellipsis{ | ||
11 | + display: block; | ||
12 | + width: 100%; | ||
13 | + white-space: nowrap; | ||
14 | + overflow: hidden; | ||
15 | + text-overflow: ellipsis; /* 1 */ | ||
16 | + } | ||
17 | + | ||
18 | + .board-table { | ||
19 | + table-layout: fixed; | ||
20 | + } | ||
21 | + .board-table .date { | ||
22 | + width: 100px; | ||
23 | + } | ||
24 | + | ||
25 | + .post-body{ | ||
26 | + white-space: pre-line; /* 2 */ | ||
27 | + } | ||
28 | + .post-info{ | ||
29 | + font-size: 0.8em; | ||
30 | + } | ||
31 | + | ||
32 | + .user-form { | ||
33 | + width: 400px; | ||
34 | + } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
Project/public/js/script.js
0 → 100644
1 | +$(function(){ | ||
2 | + function get2digits (num){ | ||
3 | + return ('0' + num).slice(-2); | ||
4 | + } | ||
5 | + | ||
6 | + function getDate(dateObj){ | ||
7 | + if(dateObj instanceof Date) | ||
8 | + return dateObj.getFullYear() + '-' + get2digits(dateObj.getMonth()+1)+ '-' + get2digits(dateObj.getDate()); | ||
9 | + } | ||
10 | + | ||
11 | + function getTime(dateObj){ | ||
12 | + if(dateObj instanceof Date) | ||
13 | + return get2digits(dateObj.getHours()) + ':' + get2digits(dateObj.getMinutes())+ ':' + get2digits(dateObj.getSeconds()); | ||
14 | + } | ||
15 | + | ||
16 | + function convertDate(){ | ||
17 | + $('[data-date]').each(function(index,element){ | ||
18 | + var dateString = $(element).data('date'); | ||
19 | + if(dateString){ | ||
20 | + var date = new Date(dateString); | ||
21 | + $(element).html(getDate(date)); | ||
22 | + } | ||
23 | + }); | ||
24 | + } | ||
25 | + | ||
26 | + function convertDateTime(){ | ||
27 | + $('[data-date-time]').each(function(index,element){ | ||
28 | + var dateString = $(element).data('date-time'); | ||
29 | + if(dateString){ | ||
30 | + var date = new Date(dateString); | ||
31 | + $(element).html(getDate(date)+' '+getTime(date)); | ||
32 | + } | ||
33 | + }); | ||
34 | + } | ||
35 | + | ||
36 | + convertDate(); | ||
37 | + convertDateTime(); | ||
38 | + }); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/public/js/scripts.js
deleted
100644 → 0
File mode changed
1 | -const express = require('express'); | 1 | +var express = require('express'); |
2 | -const router = express.Router(); | 2 | +var router = express.Router(); |
3 | +var passport = require('passport'); | ||
4 | +require('../config/passport'); | ||
3 | 5 | ||
4 | router.get('/', function(req,res){ | 6 | router.get('/', function(req,res){ |
5 | res.render('home/welcome'); | 7 | res.render('home/welcome'); |
... | @@ -9,4 +11,53 @@ router.get('/about', function(req,res){ | ... | @@ -9,4 +11,53 @@ router.get('/about', function(req,res){ |
9 | res.render('home/about'); | 11 | res.render('home/about'); |
10 | }); | 12 | }); |
11 | 13 | ||
14 | +//Login | ||
15 | +router.get('/login', function(req,res){ | ||
16 | + var username = req.flash('username')[0]; | ||
17 | + var errors = req.flash('errors')[0] || {}; | ||
18 | + res.render('home/login',{ | ||
19 | + username:username, | ||
20 | + errors:errors | ||
21 | + }); | ||
22 | +}); | ||
23 | + | ||
24 | + | ||
25 | +//Post login | ||
26 | +router.post('/login', | ||
27 | + function(req,res,next){ | ||
28 | + var errors = {}; | ||
29 | + var isValid = true; | ||
30 | + | ||
31 | + if(!req.body.username){ | ||
32 | + isValid = false; | ||
33 | + errors.username = 'Username is required!'; | ||
34 | + } | ||
35 | + if(!req.body.password){ | ||
36 | + isValid = false; | ||
37 | + errors.password = 'Password is required!'; | ||
38 | + } | ||
39 | + | ||
40 | + if(isValid){ | ||
41 | + next(); | ||
42 | + } | ||
43 | + else{ | ||
44 | + req.flash('errors',errors); | ||
45 | + res.redirect('/login'); | ||
46 | + } | ||
47 | + }, | ||
48 | + passport.authenticate('local-login', { | ||
49 | + successRedirect : '/posts', | ||
50 | + failureRedirect : '/login' | ||
51 | + } | ||
52 | +)); | ||
53 | + | ||
54 | + | ||
55 | +// Logout | ||
56 | +router.get('/logout', function(req,res){ | ||
57 | + req.logout(); | ||
58 | + res.redirect('/'); | ||
59 | +}); | ||
60 | + | ||
61 | + | ||
62 | + | ||
12 | module.exports = router; | 63 | module.exports = router; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
Project/routes/users.js
0 → 100644
1 | +var express = require('express'); | ||
2 | +var router = express.Router(); | ||
3 | +var User = require('../models/User'); | ||
4 | + | ||
5 | +// Index // 1 | ||
6 | +router.get('/', function(req, res){ | ||
7 | + User.find({}) | ||
8 | + .sort({username:1}) | ||
9 | + .exec(function(err, users){ | ||
10 | + if(err) return res.json(err); | ||
11 | + res.render('users/index', {users:users}); | ||
12 | + }); | ||
13 | +}); | ||
14 | + | ||
15 | +// New | ||
16 | +router.get('/new', function(req, res){ | ||
17 | + var user = req.flash('user')[0] || {}; | ||
18 | + var errors = req.flash('errors')[0] || {}; | ||
19 | + res.render('users/new', {user:user, errors:errors}); | ||
20 | +}); | ||
21 | + | ||
22 | +// create | ||
23 | +router.post('/', function(req, res){ | ||
24 | + User.create(req.body, function(err, user){ | ||
25 | + if(err){ | ||
26 | + req.flash('user', req.body); | ||
27 | + req.flash('errors', parseError(err)); | ||
28 | + return res.redirect('/users/new'); | ||
29 | + } | ||
30 | + res.redirect('/users'); | ||
31 | + }); | ||
32 | +}); | ||
33 | + | ||
34 | +// show | ||
35 | +router.get('/:username', function(req, res){ | ||
36 | + User.findOne({username:req.params.username}, function(err, user){ | ||
37 | + if(err) return res.json(err); | ||
38 | + res.render('users/show', {user:user}); | ||
39 | + }); | ||
40 | +}); | ||
41 | + | ||
42 | +// edit | ||
43 | +router.get('/:username/edit', function(req, res){ | ||
44 | + var user = req.flash('user')[0]; | ||
45 | + var errors = req.flash('errors')[0] || {}; | ||
46 | + if(!user){ | ||
47 | + User.findOne({username:req.params.username}, function(err, user){ | ||
48 | + if(err) return res.json(err); | ||
49 | + res.render('users/edit', {username:req.params.username, user:user, errors:errors}); | ||
50 | + }); | ||
51 | + } | ||
52 | + else{ | ||
53 | + res.render('users/edit', {username:req.params.username, user:user, errors:errors }); | ||
54 | + } | ||
55 | +}); | ||
56 | + | ||
57 | +// update | ||
58 | +router.put('/:username', function(req, res, next){ | ||
59 | + User.findOne({username:req.params.username}) // 2-1 | ||
60 | + .select('password') // 2-2 | ||
61 | + .exec(function(err, user){ | ||
62 | + if(err) return res.json(err); | ||
63 | + | ||
64 | + // update user object | ||
65 | + user.originalPassword = user.password; | ||
66 | + user.password = req.body.newPassword? req.body.newPassword : user.password; // 2-3 | ||
67 | + for(var p in req.body){ // 2-4 | ||
68 | + user[p] = req.body[p]; | ||
69 | + } | ||
70 | + | ||
71 | + // save updated user | ||
72 | + user.save(function(err, user){ | ||
73 | + if(err){ | ||
74 | + req.flash('user', req.body); | ||
75 | + req.flash('errors', parseError(err)); | ||
76 | + return res.redirect('/users/'+req.params.username+'/edit'); | ||
77 | + } | ||
78 | + res.redirect('/users/'+user.username); | ||
79 | + }); | ||
80 | + }); | ||
81 | +}); | ||
82 | + | ||
83 | +// destroy | ||
84 | +router.delete('/:username', function(req, res){ | ||
85 | + User.deleteOne({username:req.params.username}, function(err){ | ||
86 | + if(err) return res.json(err); | ||
87 | + res.redirect('/users'); | ||
88 | + }); | ||
89 | +}); | ||
90 | + | ||
91 | +module.exports = router; | ||
92 | + | ||
93 | +function parseError(errors){ | ||
94 | + var parsed = {}; | ||
95 | + if(errors.name == 'ValidationError'){ | ||
96 | + for(var name in errors.errors){ | ||
97 | + var validationError = errors.errors[name]; | ||
98 | + parsed[name] = {message:validationError.message}; | ||
99 | + } | ||
100 | + } | ||
101 | + else if(errors.code == '11000' && errors.errmsg.indexOf('username') > 0){ | ||
102 | + parsed.username = {message:'Already exists!'}; | ||
103 | + } | ||
104 | + else { | ||
105 | + parsed.unhandled = JSON.stringify(errors); | ||
106 | + } | ||
107 | + return parsed; | ||
108 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/home/login.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container"> | ||
10 | + | ||
11 | + <h3 class="mb-3">Login</h3> | ||
12 | + | ||
13 | + <form class="user-form" action="/login" method="post"> | ||
14 | + | ||
15 | + <div class="form-group row"> | ||
16 | + <label for="username" class="col-sm-3 col-form-label">Username</label> | ||
17 | + <div class="col-sm-9"> | ||
18 | + <input type="text" id="username" name="username" value="<%= username %>" class="form-control <%= (errors.username)?'is-invalid':'' %>"> | ||
19 | + <% if(errors.username){ %> | ||
20 | + <span class="invalid-feedback"><%= errors.username %></span> | ||
21 | + <% } %> | ||
22 | + </div> | ||
23 | + </div> | ||
24 | + | ||
25 | + <div class="form-group row"> | ||
26 | + <label for="password" class="col-sm-3 col-form-label">Password</label> | ||
27 | + <div class="col-sm-9"> | ||
28 | + <input type="password" id="password" name="password" value="" class="form-control <%= (errors.password)?'is-invalid':'' %>"> | ||
29 | + <% if(errors.password){ %> | ||
30 | + <span class="invalid-feedback"><%= errors.password %></span> | ||
31 | + <% } %> | ||
32 | + </div> | ||
33 | + </div> | ||
34 | + | ||
35 | + <% if(errors.login){ %> | ||
36 | + <div class="invalid-feedback d-block"><%= errors.login %></div> | ||
37 | + <% } %> | ||
38 | + | ||
39 | + <div class="mt-3"> | ||
40 | + <input class="btn btn-primary" type="submit" value="Submit"> | ||
41 | + </div> | ||
42 | + | ||
43 | + </form> | ||
44 | + | ||
45 | + </div> | ||
46 | + </body> | ||
47 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -13,4 +13,8 @@ | ... | @@ -13,4 +13,8 @@ |
13 | <!-- my css --> | 13 | <!-- my css --> |
14 | <link rel="stylesheet" href="/css/master.css"> | 14 | <link rel="stylesheet" href="/css/master.css"> |
15 | 15 | ||
16 | +<!-- my js --> | ||
17 | +<script src="/js/script.js"></script> | ||
18 | + | ||
19 | + | ||
16 | <title>Mapmory</title> | 20 | <title>Mapmory</title> |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -8,6 +8,17 @@ | ... | @@ -8,6 +8,17 @@ |
8 | <ul class="navbar-nav"> | 8 | <ul class="navbar-nav"> |
9 | <li class="nav-item"><a href="/" class="nav-link">Home</a></li> | 9 | <li class="nav-item"><a href="/" class="nav-link">Home</a></li> |
10 | <li class="nav-item"><a href="/about" class="nav-link">About</a></li> | 10 | <li class="nav-item"><a href="/about" class="nav-link">About</a></li> |
11 | + <li class="nav-item"><a href="/posts" class="nav-link">Posts</a></li> | ||
12 | + </ul> | ||
13 | + | ||
14 | + <ul class="navbar-nav ml-auto"> <!-- 우측 정렬 --> | ||
15 | + <% if(isAuthenticated){ %> | ||
16 | + <li class="nav-item"><a href="/users/<%= currentUser.username %>" class="nav-link">My Account</a></li> | ||
17 | + <li class="nav-item"><a href="/logout" class="nav-link">Logout</a></li> | ||
18 | + <% } else { %> | ||
19 | + <li class="nav-item"><a href="/users/new" class="nav-link">Sign Up</a></li> | ||
20 | + <li class="nav-item"><a href="/login" class="nav-link">Login</a></li> | ||
21 | + <% } %> | ||
11 | </ul> | 22 | </ul> |
12 | </div> | 23 | </div> |
13 | </div> | 24 | </div> | ... | ... |
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <nav aria-label="breadcrumb"> | ||
12 | + <ol class="breadcrumb p-1 pl-2 pr-2"> | ||
13 | + <li class="breadcrumb-item"><a href="/">Home</a></li> | ||
14 | + <li class="breadcrumb-item"><a href="/posts">Board</a></li> | ||
15 | + <li class="breadcrumb-item"><a href="/posts/<%= post._id %>"><%= post.title %></a></li> | ||
16 | + <li class="breadcrumb-item active" aria-current="page">Edit Post</li> | ||
17 | + </ol> | ||
18 | + </nav> | ||
19 | + | ||
20 | + <form action="/posts/<%= post._id %>?_method=put" method="post"> | ||
21 | + | ||
22 | + <div class="form-group"> | ||
23 | + <label for="title">Title</label> | ||
24 | + <input type="text" id="title" name="title" value="<%= post.title %>" class="form-control"> | ||
25 | + </div> | ||
26 | + | ||
27 | + <div class="form-group"> | ||
28 | + <label for="body">Body</label> | ||
29 | + <textarea id="body" name="body" rows="5" class="form-control"><%= post.body %></textarea> | ||
30 | + </div> | ||
31 | + | ||
32 | + <div> | ||
33 | + <a class="btn btn-primary" href="/posts/<%= post._id %>">Back</a> | ||
34 | + <button type="submit" class="btn btn-primary">Submit</button> | ||
35 | + </div> | ||
36 | + | ||
37 | + </form> | ||
38 | + | ||
39 | + </div> | ||
40 | + </body> | ||
41 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <h2 class="mb-3">Board</h2> | ||
12 | + | ||
13 | + <table class="board-table table table-sm border-bottom"> | ||
14 | + | ||
15 | + <thead class="thead-light"> | ||
16 | + <tr> | ||
17 | + <th scope="col">Title</th> | ||
18 | + <th scope="col" class="date">Date</th> | ||
19 | + </tr> | ||
20 | + </thead> | ||
21 | + | ||
22 | + <tbody> | ||
23 | + <% if(posts == null || posts.length == 0){ %> | ||
24 | + <tr> | ||
25 | + <td colspan=2> EMPTY </td> | ||
26 | + </tr> | ||
27 | + <% } %> | ||
28 | + <% posts.forEach(function(post) { %> | ||
29 | + <tr> | ||
30 | + <td> | ||
31 | + <a href="/posts/<%= post._id %>"><div class="ellipsis"><%= post.title %></div></a> | ||
32 | + </td> | ||
33 | + <td class="date"> | ||
34 | + <span data-date="<%= post.createdAt %>"></span> | ||
35 | + </td> | ||
36 | + </tr> | ||
37 | + <% }) %> | ||
38 | + </tbody> | ||
39 | + | ||
40 | + </table> | ||
41 | + | ||
42 | + <div> | ||
43 | + <a class="btn btn-primary" href="/posts/new">New</a> | ||
44 | + </div> | ||
45 | + | ||
46 | + </div> | ||
47 | + </body> | ||
48 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <nav aria-label="breadcrumb"> <!-- 1 --> | ||
12 | + <ol class="breadcrumb p-1 pl-2 pr-2"> | ||
13 | + <li class="breadcrumb-item"><a href="/">Home</a></li> | ||
14 | + <li class="breadcrumb-item"><a href="/posts">Board</a></li> | ||
15 | + <li class="breadcrumb-item active" aria-current="page">New Post</li> | ||
16 | + </ol> | ||
17 | + </nav> | ||
18 | + | ||
19 | + <form action="/posts" method="post"> | ||
20 | + | ||
21 | + <div class="form-group"> | ||
22 | + <label for="title">Title</label> | ||
23 | + <input type="text" id="title" name="title" value="" class="form-control"> | ||
24 | + </div> | ||
25 | + | ||
26 | + <div class="form-group"> | ||
27 | + <label for="body">Body</label> | ||
28 | + <textarea id="body" name="body" rows="5" class="form-control"></textarea> | ||
29 | + </div> | ||
30 | + | ||
31 | + <div> | ||
32 | + <a class="btn btn-primary" href="/posts">Back</a> | ||
33 | + <button type="submit" class="btn btn-primary">Submit</button> | ||
34 | + </div> | ||
35 | + | ||
36 | + </form> | ||
37 | + | ||
38 | + </div> | ||
39 | + </body> | ||
40 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <nav aria-label="breadcrumb"> | ||
12 | + <ol class="breadcrumb p-1 pl-2 pr-2"> | ||
13 | + <li class="breadcrumb-item"><a href="/">Home</a></li> | ||
14 | + <li class="breadcrumb-item"><a href="/posts">Board</a></li> | ||
15 | + <li class="breadcrumb-item active" aria-current="page"><%= post.title %></li> | ||
16 | + </ol> | ||
17 | + </nav> | ||
18 | + | ||
19 | + <div class="card"> | ||
20 | + <h5 class="card-header p-2"><%= post.title %></h5> | ||
21 | + <div class="row"> <!-- 1 --> | ||
22 | + | ||
23 | + <div class="col-md-7 col-lg-8 col-xl-9 order-sm-2 order-md-1"> <!-- 1 --> | ||
24 | + <div class="post-body p-2"><%= post.body %></div> | ||
25 | + </div> | ||
26 | + | ||
27 | + <div class="col-md-5 col-lg-4 col-xl-3 order-sm-1 order-md-2"> <!-- 1 --> | ||
28 | + <div class="post-info card m-2 p-2"> | ||
29 | + <div><span>Created</span> : <span data-date-time="<%= post.createdAt %>"></span></div> <!-- 2 --> | ||
30 | + <% if(post.updatedAt) { %> | ||
31 | + <div><span>Updated</span> : <span data-date-time="<%= post.updatedAt %>"></span></div> <!-- 2 --> | ||
32 | + <% } %> | ||
33 | + </div> | ||
34 | + </div> | ||
35 | + | ||
36 | + </div> | ||
37 | + </div> | ||
38 | + | ||
39 | + <div class="mt-3"> | ||
40 | + <a class="btn btn-primary" href="/posts">Back</a> | ||
41 | + <a class="btn btn-primary" href="/posts/<%= post._id %>/edit">Edit</a> | ||
42 | + <form action="/posts/<%= post._id %>?_method=delete" method="post" class="d-inline"> | ||
43 | + <a class="btn btn-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a> | ||
44 | + </form> | ||
45 | + </div> | ||
46 | + | ||
47 | + </div> | ||
48 | + </body> | ||
49 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
Project/views/users/edit.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <h3 class="mb-3">Edit User</h3> | ||
12 | + | ||
13 | + <form action="/users/<%= username %>?_method=put" method="post"> <!-- 1 --> | ||
14 | + | ||
15 | + <div class="form-group row"> | ||
16 | + <label for="currentPassword" class="col-sm-3 col-form-label">Current Password*</label> | ||
17 | + <div class="col-sm-9 col-sm-offset-3"> | ||
18 | + <input type="password" id="currentPassword" name="currentPassword" value="" class="form-control <%= (errors.currentPassword)?'is-invalid':'' %>"> <!-- 2 --> | ||
19 | + <% if(errors.currentPassword){ %> <!-- 3 --> | ||
20 | + <span class="invalid-feedback"><%= errors.currentPassword.message %></span> | ||
21 | + <% } %> | ||
22 | + </div> | ||
23 | + </div> | ||
24 | + | ||
25 | + <hr></hr> | ||
26 | + | ||
27 | + <div class="form-group row"> | ||
28 | + <label for="username" class="col-sm-3 col-form-label">Username*</label> | ||
29 | + <div class="col-sm-9"> | ||
30 | + <input type="text" id="username" name="username" value="<%= user.username %>" class="form-control <%= (errors.username)?'is-invalid':'' %>"> <!-- 2 --> | ||
31 | + <% if(errors.username){ %> <!-- 3 --> | ||
32 | + <span class="invalid-feedback"><%= errors.username.message %></span> | ||
33 | + <% } %> | ||
34 | + </div> | ||
35 | + </div> | ||
36 | + | ||
37 | + <div class="form-group row"> | ||
38 | + <label for="name" class="col-sm-3 col-form-label">Name*</label> | ||
39 | + <div class="col-sm-9"> | ||
40 | + <input type="text" id="name" name="name" value="<%= user.name %>" class="form-control <%= (errors.name)?'is-invalid':'' %>"> <!-- 2 --> | ||
41 | + <% if(errors.name){ %> <!-- 3 --> | ||
42 | + <span class="invalid-feedback"><%= errors.name.message %></span> | ||
43 | + <% } %> | ||
44 | + </div> | ||
45 | + </div> | ||
46 | + | ||
47 | + <div class="form-group row"> | ||
48 | + <label for="email" class="col-sm-3 col-form-label">Email</label> | ||
49 | + <div class="col-sm-9"> | ||
50 | + <input type="text" id="email" name="email" value="<%= user.email %>" class="form-control <%= (errors.email)?'is-invalid':'' %>"> <!-- 2 --> | ||
51 | + <% if(errors.email){ %> <!-- 3 --> | ||
52 | + <span class="invalid-feedback"><%= errors.email.message %></span> | ||
53 | + <% } %> | ||
54 | + </div> | ||
55 | + </div> | ||
56 | + | ||
57 | + <div class="form-group row"> | ||
58 | + <label for="newPassword" class="col-sm-3 col-form-label">New Password</label> | ||
59 | + <div class="col-sm-9 col-sm-offset-3"> | ||
60 | + <input type="password" id="newPassword" name="newPassword" value="" class="form-control <%= (errors.newPassword)?'is-invalid':'' %>"> <!-- 2 --> | ||
61 | + <% if(errors.newPassword){ %> <!-- 3 --> | ||
62 | + <span class="invalid-feedback"><%= errors.newPassword.message %></span> | ||
63 | + <% } %> | ||
64 | + </div> | ||
65 | + </div> | ||
66 | + | ||
67 | + <div class="form-group row"> | ||
68 | + <label for="passwordConfirmation" class="col-sm-3 col-form-label">Password Confirmation</label> | ||
69 | + <div class="col-sm-9 col-sm-offset-3"> | ||
70 | + <input type="password" id="passwordConfirmation" name="passwordConfirmation" value="" class="form-control <%= (errors.passwordConfirmation)?'is-invalid':'' %>"> <!-- 2 --> | ||
71 | + <% if(errors.passwordConfirmation){ %> <!-- 3 --> | ||
72 | + <span class="invalid-feedback"><%= errors.passwordConfirmation.message %></span> | ||
73 | + <% } %> | ||
74 | + </div> | ||
75 | + </div> | ||
76 | + | ||
77 | + <p> | ||
78 | + <small>*Required</small> | ||
79 | + </p> | ||
80 | + | ||
81 | + <% if(errors.unhandled){ %> <!-- 4 --> | ||
82 | + <div class="alert alert-danger"> | ||
83 | + <%= errors.unhandled %> | ||
84 | + </div> | ||
85 | + <% } %> | ||
86 | + | ||
87 | + </form> | ||
88 | + | ||
89 | + </div> | ||
90 | + </body> | ||
91 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/users/index.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <h3 class="mb-3">Users</h3> | ||
12 | + | ||
13 | + <ul class="list-group"> | ||
14 | + <% if(users == null || users.length == 0){ %> | ||
15 | + <li class="list-group-item"> There is no user yet.</li> | ||
16 | + <% } %> | ||
17 | + <% users.forEach(function(user) { %> | ||
18 | + <li class="list-group-item"> | ||
19 | + <a href="/users/<%= user.username %>"><%= user.username %></a> | ||
20 | + </li> | ||
21 | + <% }) %> | ||
22 | + </ul> | ||
23 | + | ||
24 | + </div> | ||
25 | + </body> | ||
26 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/users/new.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <h3 class="contentBoxTop mb-3">New User</h3> | ||
12 | + | ||
13 | + <form action="/users" method="post"> | ||
14 | + | ||
15 | + <div class="form-group row"> | ||
16 | + <label for="username" class="col-sm-3 col-form-label">Username*</label> | ||
17 | + <div class="col-sm-9"> | ||
18 | + <input type="text" id="username" name="username" value="<%= user.username %>" class="form-control <%= (errors.username)?'is-invalid':'' %>"> <!-- 1, 2 --> | ||
19 | + <% if(errors.username){ %> <!-- 3 --> | ||
20 | + <span class="invalid-feedback"><%= errors.username.message %></span> | ||
21 | + <% } %> | ||
22 | + </div> | ||
23 | + </div> | ||
24 | + <div class="form-group row"> | ||
25 | + <label for="name" class="col-sm-3 col-form-label">Name*</label> | ||
26 | + <div class="col-sm-9"> | ||
27 | + <input type="text" id="name" name="name" value="<%= user.name %>" class="form-control <%= (errors.name)?'is-invalid':'' %>"> <!-- 1, 2 --> | ||
28 | + <% if(errors.name){ %> <!-- 3 --> | ||
29 | + <span class="invalid-feedback"><%= errors.name.message %></span> | ||
30 | + <% } %> | ||
31 | + </div> | ||
32 | + </div> | ||
33 | + <div class="form-group row"> | ||
34 | + <label for="email" class="col-sm-3 col-form-label">Email</label> | ||
35 | + <div class="col-sm-9"> | ||
36 | + <input type="text" id="email" name="email" value="<%= user.email %>" class="form-control <%= (errors.email)?'is-invalid':'' %>"> <!-- 1, 2 --> | ||
37 | + <% if(errors.email){ %> | ||
38 | + <span class="invalid-feedback"><%= errors.email.message %></span> | ||
39 | + <% } %> | ||
40 | + </div> | ||
41 | + </div> | ||
42 | + <div class="form-group row"> | ||
43 | + <label for="password" class="col-sm-3 col-form-label">Password*</label> | ||
44 | + <div class="col-sm-9"> | ||
45 | + <input type="password" id="password" name="password" value="" class="form-control <%= (errors.password)?'is-invalid':'' %>"> <!-- 1, 2 --> | ||
46 | + <% if(errors.password){ %> <!-- 3 --> | ||
47 | + <span class="invalid-feedback"><%= errors.password.message %></span> | ||
48 | + <% } %> | ||
49 | + </div> | ||
50 | + </div> | ||
51 | + <div class="form-group row"> | ||
52 | + <label for="passwordConfirmation" class="col-sm-3 col-form-label">Password Confirmation*</label> | ||
53 | + <div class="col-sm-9 col-sm-offset-3"> | ||
54 | + <input type="password" id="passwordConfirmation" name="passwordConfirmation" value="" class="form-control <%= (errors.passwordConfirmation)?'is-invalid':'' %>"> <!-- 1, 2 --> | ||
55 | + <% if(errors.passwordConfirmation){ %> <!-- 3 --> | ||
56 | + <span class="invalid-feedback"><%= errors.passwordConfirmation.message %></span> | ||
57 | + <% } %> | ||
58 | + </div> | ||
59 | + </div> | ||
60 | + <p> | ||
61 | + <small>*Required</small> | ||
62 | + </p> | ||
63 | + | ||
64 | + <% if(errors.unhandled){ %> <!-- 4 --> | ||
65 | + <div class="alert alert-danger"> | ||
66 | + <%= errors.unhandled %> | ||
67 | + </div> | ||
68 | + <% } %> | ||
69 | + | ||
70 | + <div class="form-group"> | ||
71 | + <button type="submit" class="btn btn-primary">Submit</button> | ||
72 | + </div> | ||
73 | + </form> | ||
74 | + | ||
75 | + </div> | ||
76 | + </body> | ||
77 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/users/show.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <h3 class="contentBoxTop"><%= user.username %></h3> | ||
12 | + | ||
13 | + <form class="user-form" action="/users" method="post"> | ||
14 | + <fieldset disabled> | ||
15 | + <div class="form-group row"> | ||
16 | + <label for="name" class="col-sm-3 col-form-label">Name</label> | ||
17 | + <div class="col-sm-9"> | ||
18 | + <input class="form-control" type="text" id="name" name="name" value="<%= user.name %>"> | ||
19 | + </div> | ||
20 | + </div> | ||
21 | + <div class="form-group row"> | ||
22 | + <label for="email" class="col-sm-3 col-form-label">Email</label> | ||
23 | + <div class="col-sm-9"> | ||
24 | + <input class="form-control" type="text" id="email" name="email" value="<%= user.email %>"> | ||
25 | + </div> | ||
26 | + </div> | ||
27 | + </fieldset> | ||
28 | + </form> | ||
29 | + | ||
30 | + <div> | ||
31 | + <a class="btn btn-primary" href="/users">Back</a> | ||
32 | + <a class="btn btn-primary" href="/users/<%= user.username %>/edit">Edit</a> | ||
33 | + <form action="/users/<%= user.username %>?_method=delete" method="post" class="d-inline"> | ||
34 | + <a class="btn btn-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a> | ||
35 | + </form> | ||
36 | + </div> | ||
37 | + | ||
38 | + </div> | ||
39 | + </body> | ||
40 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment