임태민

Update project directory

......@@ -3,8 +3,9 @@ const mongoose = require('mongoose');
// Declare the schemea of post
var postSchema = mongoose.Schema({
title:{type:String, required:true},
body:{type:String, required:true},
title:{type:String, required:[true, 'Title is required!']},
body:{type:String, required:[true, 'Content is required!']},
author:{type:mongoose.Schema.Types.ObjectId, ref:'user', required:true},
createdAt:{type:Date, default:Date.now},
updatedAt:{type:Date},
});
......
body {
background-color:whitesmoke;
}
.h1 {
font-family: 'PT Serif', serif;
......@@ -21,6 +21,10 @@ body {
.board-table .date {
width: 100px;
}
.board-table .author,
.board-table .date{
width: 100px;
}
.post-body{
white-space: pre-line; /* 2 */
......@@ -32,3 +36,11 @@ body {
.user-form {
width: 400px;
}
.mb-3{
background-color:tan;
}
\ No newline at end of file
......
var express = require('express');
var router = express.Router();
var Post = require('../models/Post');
var util = require('../util');
// Post home
router.get('/', function(req, res){
Post.find({})
.populate('author')
.sort('-createdAt')
.exec(function(err, posts){
if(err){return res.json(err)};
......@@ -15,23 +17,32 @@ router.get('/', function(req, res){
// Post new
router.get('/new', function(req, res){
res.render('posts/new');
router.get('/new', util.isLoggedin, function(req, res){
var post = req.flash('post')[0] || {};
var errors = req.flash('errors')[0] || {};
res.render('posts/new', {post:post, errors:errors});
})
// Post create
router.post('/', function(req, res){
router.post('/', util.isLoggedin, function(req, res){
req.body.author = req.user._id;
Post.create(req.body, function(err, post){
if(err){return res.json(err)};
if(err){
req.flash('post', req.body);
req.flash('errors', util.parseError(err));
return res.redirect('/posts/new');
};
res.redirect('/posts');
});
});
// Post show
router.get('/:id', function(req, res){
Post.findOne({_id:req.params.id}, function(err, post){
router.get('/:id', util.isLoggedin, function(req, res){
Post.findOne({_id:req.params.id})
.populate('author')
.exec(function(err,post){
if(err){return res.json(err)};
res.render('posts/show', {post:post});
});
......@@ -39,32 +50,51 @@ router.get('/:id', function(req, res){
// Post edit
router.get('/:id/edit', function(req, res){
router.get('/:id/edit', util.isLoggedin, checkPermission, function(req, res){
var post = req.flash('post')[0];
var errors = req.flash('errors')[0] || {};
if(!post){
Post.findOne({_id:req.params.id}, function(err, post){
if(err){return res.join(err)};
res.render('posts/edit', {posts:post});
res.render('posts/edit', {posts:post, errors:errors});
});
}
else{
post._id = req.params.id;
res.render('posts/edit', {post:post, errors:errors});
}
});
// Post update
router.get('/:id', function(req, res){
router.put('/:id', util.isLoggedin, checkPermission, function(req, res){
req.body.updatedAt = Date.now();
Post.findOneAndUpdate({_id:req.params.id}, req.body, function(err, post){
if(err){return res.join(err)};
Post.findOneAndUpdate({_id:req.params.id}, req.body, {runValidators:true}, function(err, post){
if(err){
req.flash('post', req.body),
req.flash('errors', util.parseError(err));
return res.redirect('/posts/'+ req.params,id + '/edit');
};
res.redirect('posts/' + req.params.id);
});
});
// Post delete
router.delete('/:id', function(req, res){
router.delete('/:id', util.isLoggedin, checkPermission, function(req, res){
Post.deleteOne({_id:req.params.id}, function(err){
if(err){return res.join(err)};
res.redirect('/posts');
});
});
function checkPermission(req, res, next){
Post.findOne({_id:req.params.id}, function(err, post){
if(err){return res.json(err)};
if(post.author != req.user.id){return util.noPermission(req,res)};
next();
});
}
// Export module
module.exports = router;
\ No newline at end of file
......
var express = require('express');
var router = express.Router();
var User = require('../models/User');
var util = require('../util');
// Index // 1
router.get('/', function(req, res){
User.find({})
.sort({username:1})
.exec(function(err, users){
if(err) return res.json(err);
res.render('users/index', {users:users});
});
});
//Index
// router.get('/', function(req, res){
// User.find({})
// .sort({username:1})
// .exec(function(err, users){
// if(err) return res.json(err);
// res.render('users/index', {users:users});
// });
// });
// New
router.get('/new', function(req, res){
......@@ -24,15 +25,15 @@ router.post('/', function(req, res){
User.create(req.body, function(err, user){
if(err){
req.flash('user', req.body);
req.flash('errors', parseError(err));
req.flash('errors', util.parseError(err));
return res.redirect('/users/new');
}
res.redirect('/users');
res.redirect('/users/login');
});
});
// show
router.get('/:username', function(req, res){
router.get('/:username', util.isLoggedin, function(req, res){
User.findOne({username:req.params.username}, function(err, user){
if(err) return res.json(err);
res.render('users/show', {user:user});
......@@ -40,7 +41,7 @@ router.get('/:username', function(req, res){
});
// edit
router.get('/:username/edit', function(req, res){
router.get('/:username/edit', util.isLoggedin, checkPermission, function(req, res){
var user = req.flash('user')[0];
var errors = req.flash('errors')[0] || {};
if(!user){
......@@ -55,7 +56,7 @@ router.get('/:username/edit', function(req, res){
});
// update
router.put('/:username', function(req, res, next){
router.put('/:username', util.isLoggedin, checkPermission, function(req, res, next){
User.findOne({username:req.params.username}) // 2-1
.select('password') // 2-2
.exec(function(err, user){
......@@ -72,7 +73,7 @@ router.put('/:username', function(req, res, next){
user.save(function(err, user){
if(err){
req.flash('user', req.body);
req.flash('errors', parseError(err));
req.flash('errors', util.parseError(err));
return res.redirect('/users/'+req.params.username+'/edit');
}
res.redirect('/users/'+user.username);
......@@ -81,12 +82,12 @@ router.put('/:username', function(req, res, next){
});
// destroy
router.delete('/:username', function(req, res){
User.deleteOne({username:req.params.username}, function(err){
if(err) return res.json(err);
res.redirect('/users');
});
});
// router.delete('/:username', function(req, res){
// User.deleteOne({username:req.params.username}, function(err){
// if(err) return res.json(err);
// res.redirect('/users');
// });
// });
module.exports = router;
......@@ -106,3 +107,11 @@ function parseError(errors){
}
return parsed;
}
function checkPermission(req, res, next){
User.findOne({username:req.params.username}, function(err, post){
if(err){return res.json(err)};
if(post.id != req.user.id){return util.noPermission(req,res)};
next();
});
}
\ No newline at end of file
......
var util = {};
util.parseError = function(errors){
var parsed = {};
if(errors.name == 'ValidationError'){
for(var name in errors.errors){
var validationError = errors.errors[name];
parsed[name] = { message:validationError.message };
}
}
else if(errors.code == '11000' && errors.errmsg.indexOf('username') > 0) {
parsed.username = { message:'This username already exists!' };
}
else {
parsed.unhandled = JSON.stringify(errors);
}
return parsed;
}
util.isLoggedin = function(req, res, next){
if(req.isAuthenticated()){
next();
}
else {
req.flash('errors', {login:'Please login first'});
res.redirect('/login');
}
}
util.noPermission = function(req, res){
req.flash('errors', {login:"You don't have permission"});
req.logout();
res.redirect('/login');
}
module.exports = util;
\ No newline at end of file
......@@ -10,7 +10,8 @@
<h2 class="mb-3">About</h2>
<P>이 사이트는 <a href="http://a-mean-blog.com/ko/blog/Node-JS-첫걸음/게시판-만들기">http://a-mean-blog.com/ko/blog/Node-JS-첫걸음/게시판-만들기</a>에서 작성되었습니다!</p>
<P>Store and Share your precious memory in MAPMORY!</p>
</div>
</body>
......
......@@ -2,17 +2,54 @@
<html>
<head>
<%- include('../partials/head') %>
<style>
body,html{
margin: 0;
padding: 0;
height: 100%;
}
.img {
border: 0;
padding: 0;
background-image: url('assets/img/bg-callout.jpg');
background-size: cover;
min-height: 100%;
background-size: cover;
background-position: center;
}
.img .content{
position: absolute;
top: 45%;
left: 50%;
font-size: 1rem;
transform: translate(-50%, -50%);
font-family: 'PT Serif', serif;
text-align: center;
}
h1{
font-size: 5rem;
padding:0;
margin:0;
}
h3{
font-size: 2.5rem;
padding:0;
margin:2px;
}
</style>
</head>
<body>
<%- include('../partials/nav') %>
<div class="container mb-3">
<div class="jumbotron">
<h1>My Website</h1>
<P>제 웹사이트를 방문해 주셔서 감사합니다!</p>
<div class="img">
<div class="content">
<h1>Mapmory</h1>
<h3><em>Save your reminiscence with place</em></h3>
</div>
</div>
</body>
</html>
\ No newline at end of file
......
<nav class="navbar navbar-expand-sm navbar-light bg-light mb-3">
<!-- <nav class="navbar navbar-expand-sm navbar-light bg-light mb-3"> -->
<nav class="navbar navbar-expand-sm navbar-dark bg-dark mb-3">
<div class="container">
<div class="navbar-brand">My Website</div>
<div class="navbar-brand">Mapmory</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
......@@ -8,7 +9,7 @@
<ul class="navbar-nav">
<li class="nav-item"><a href="/" class="nav-link">Home</a></li>
<li class="nav-item"><a href="/about" class="nav-link">About</a></li>
<li class="nav-item"><a href="/posts" class="nav-link">Posts</a></li>
<li class="nav-item"><a href="/posts" class="nav-link">Memory</a></li>
</ul>
<ul class="navbar-nav ml-auto"> <!-- 우측 정렬 -->
......
......@@ -21,14 +21,26 @@
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" name="title" value="<%= post.title %>" class="form-control">
<input type="text" id="title" name="title" value="<%= post.title %>" class="form-control <%= (errors.title)?'is-invalid':'' %>">
<% if(errors.title){ %>
<span class="invalid-feedback"><%= errors.title.message %></span>
<% } %>
</div>
<div class="form-group">
<label for="body">Body</label>
<textarea id="body" name="body" rows="5" class="form-control"><%= post.body %></textarea>
<textarea id="body" name="body" rows="5" class="form-control <%= (errors.body)?'is-invalid':''%>"><%= post.body %></textarea>
<% if(errors.body){ %>
<span class="invalid-feedback"><%= errors.body.message %></span>
<% } %>
</div>
<% if(errors.unhandled){ %>
<div class="invalid-feedback b-block">
<%= errors.unhandled %>
</div>
<% } %>
<div>
<a class="btn btn-primary" href="/posts/<%= post._id %>">Back</a>
<button type="submit" class="btn btn-primary">Submit</button>
......
......@@ -15,6 +15,7 @@
<thead class="thead-light">
<tr>
<th scope="col">Title</th>
<th scope="col" class="author">Author</th>
<th scope="col" class="date">Date</th>
</tr>
</thead>
......@@ -30,8 +31,11 @@
<td>
<a href="/posts/<%= post._id %>"><div class="ellipsis"><%= post.title %></div></a>
</td>
<td class="author">
<div class="ellipsis"><%= post.author ? post.author.username : "" %></div>
</td>
<td class="date">
<span data-date="<%= post.createdAt %>"></span>
<span data-date="<%= post.createdAt %>"><%= post.createdAt %></span>
</td>
</tr>
<% }) %>
......@@ -40,7 +44,9 @@
</table>
<div>
<% if(isAuthenticated){ %>
<a class="btn btn-primary" href="/posts/new">New</a>
<% } %>
</div>
</div>
......
......@@ -20,21 +20,31 @@
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" name="title" value="" class="form-control">
<input type="text" id="title" name="title" value="<%= post.title %>" class="form-control <%= (errors.title)?'is-invalid':'' %>">
<% if(errors.title){ %>
<span class="invalid-feedback"><%= errors.title.message %></span>
<% } %>
</div>
<div class="form-group">
<label for="body">Body</label>
<textarea id="body" name="body" rows="5" class="form-control"></textarea>
<textarea id="body" name="body" rows="5" class="form-control <%= (errors.body)?'is-invalid':''%>"><%= post.body %></textarea>
<% if(errors.body){ %>
<span class="invalid-feedback"><%= errors.body.message %></span>
<% } %>
</div>
<% if(errors.unhandled){ %>
<div class="invalid-feedback b-block">
<%= errors.unhandled %>
</div>
<% } %>
<div>
<a class="btn btn-primary" href="/posts">Back</a>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</body>
</html>
\ No newline at end of file
......
......@@ -26,9 +26,12 @@
<div class="col-md-5 col-lg-4 col-xl-3 order-sm-1 order-md-2"> <!-- 1 -->
<div class="post-info card m-2 p-2">
<div><span>Created</span> : <span data-date-time="<%= post.createdAt %>"></span></div> <!-- 2 -->
<div class="border-bottom pb-1 mb-1">
<span>Author</span> : <%= post.author ? post.author.username: "" %>
</div>
<div><span>Created</span> : <span data-date-time="<%= post.createdAt %>"><%= post.createdAt %></span></div>
<% if(post.updatedAt) { %>
<div><span>Updated</span> : <span data-date-time="<%= post.updatedAt %>"></span></div> <!-- 2 -->
<div><span>Updated</span> : <span data-date-time="<%= post.updatedAt %>"><%= post.updatedAt %></span></div>
<% } %>
</div>
</div>
......@@ -38,10 +41,12 @@
<div class="mt-3">
<a class="btn btn-primary" href="/posts">Back</a>
<% if(isAuthenticated && post.author && currentUser.id == post.author.id){ %> <!-- 1 -->
<a class="btn btn-primary" href="/posts/<%= post._id %>/edit">Edit</a>
<form action="/posts/<%= post._id %>?_method=delete" method="post" class="d-inline">
<a class="btn btn-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a>
</form>
<% } %>
</div>
</div>
......
......@@ -78,12 +78,18 @@
<small>*Required</small>
</p>
<div class="buttons">
<a class="btn btn-primary" href="/users/<%= user.username %>">Back</a>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<% if(errors.unhandled){ %> <!-- 4 -->
<div class="alert alert-danger">
<%= errors.unhandled %>
</div>
<% } %>
</form>
</div>
......
......@@ -29,7 +29,9 @@
<div>
<a class="btn btn-primary" href="/users">Back</a>
<% if(isAuthenticated && currentUser.id == user.id){ %>
<a class="btn btn-primary" href="/users/<%= user.username %>/edit">Edit</a>
<% } %>
<form action="/users/<%= user.username %>?_method=delete" method="post" class="d-inline">
<a class="btn btn-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a>
</form>
......