Showing
22 changed files
with
895 additions
and
16 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 |
This diff is collapsed. Click to expand it.
... | @@ -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