임태민

Update Project directory

- 포스팅 구현
- 회원가입, 로그인, 로그아웃 구현
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
......
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;
3 -}
4 2
5 -h1 { 3 + }
4 + .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
......
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
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
......
1 -const express = require('express'); 1 +var express = require('express');
2 -const router = express.Router(); 2 +var router = express.Router();
3 -const Post = require('../models/Post'); 3 +var Post = require('../models/Post');
4 4
5 5
6 // Post home 6 // Post home
......
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
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
......
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
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
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
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