임태민

Merge branch 'release'

# 📒 Mapmory
#### Mapmory provides location-based recording sevices utilizing Google Maps API.
### Mapmory functions
-----------------
- [x] Register
- [x] Log in/out
- [x] Member information edit
- [x] Create your memories(With Title, Date, Author, Address)
- [x] Delete your memories
- [x] Save your memory with map
- [x] Show your memory with map
- [x] Edit your memory with map
- [x] Search your memories
### Stack
-----------------
+ Front end : EJS template engine
+ Back end : Express/NodeJS
+ Database : Mongo DB
+ Server : AWS EC2
## ✏️ Quick Start (build, install, setup manual)
$ git clone http://khuhub.khu.ac.kr/2017101294/Mapmory.git
$ npm install
$ node index.js
If it does not work well
$ npm install nodemon-g
At package.json, add ``` "start" : "nodemon index.js" ```
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start" : "nodemon index.js"
}
$ npm start
### Dependency
-----------------
+ bcryptjs : 2.4.3,
+ body-parser : 1.19.0,
+ connect-flash : 0.1.1,
+ ejs : 3.1.6,
+ express : 4.17.1,
+ express-session : 1.17.1,
+ method-override : 3.0.0,
+ mongoose : 5.12.8,
+ passport : 0.4.1,
+ passport-local : 1.0.0
### 👬 Team members
-----------------
+ Im Taemin (@devTaemin)
+ Hong Jiyoon (@fheldgktpdy)
\ No newline at end of file
......
......@@ -16,7 +16,7 @@ mongoose.set('useCreateIndex', true);
mongoose.set('useUnifiedTopology', true);
// Connect DB environment variable
mongoose.connect('mongodb+srv://Mapmory_admin:admin@cluster0.ncnjj.mongodb.net/Project-Mapmory?retryWrites=true&w=majority');
mongoose.connect('mongodbkey입력하는곳');
// Store DB in the variable 'db'
var db = mongoose.connection;
......
......@@ -7,7 +7,7 @@ var postSchema = mongoose.Schema({
address:{type:String, required:[true, 'address 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()},
createdAt:{type:Date, default:Date.now},
updatedAt:{type:Date}
});
......
......@@ -6,13 +6,31 @@ var util = require('../util');
// Post home
router.get('/', function(req, res){
Post.find({})
router.get('/', async function(req, res){
// Paging 기능 추가
var page = Math.max(1, parseInt(req.query.page));
var limit = Math.max(1, parseInt(req.query.limit));
page = !isNaN(page)?page:1;
limit = !isNaN(limit)?limit:10;
var searchQuery = createSearchQuery(req.query);
var skip = (page-1)*limit;
var count = await Post.countDocuments(searchQuery);
var maxPage = Math.ceil(count/limit);
var posts = await Post.find(searchQuery)
.populate('author')
.sort('-createdAt')
.exec(function(err, posts){
if(err){return res.json(err)};
res.render('posts/index', {posts:posts});
.skip(skip)
.limit(limit)
.exec();
res.render('posts/index', {
posts:posts,
currentPage:page,
maxPage:maxPage,
limit:limit,
searchType:req.query.searchType,
searchText:req.query.searchText
});
});
......@@ -97,5 +115,23 @@ function checkPermission(req, res, next){
});
}
// Search function
function createSearchQuery(queries){
var searchQuery = {};
if(queries.searchType && queries.searchText && queries.searchText.length >= 3){
var searchTypes = queries.searchType.toLowerCase().split(',');
var postQueries = [];
if(searchTypes.indexOf('title')>=0){
postQueries.push({ title: { $regex: new RegExp(queries.searchText, 'i') } });
}
if(searchTypes.indexOf('body')>=0){
postQueries.push({ body: { $regex: new RegExp(queries.searchText, 'i') } });
}
if(postQueries.length > 0) searchQuery = {$or:postQueries};
}
return searchQuery;
}
// Export module
module.exports = router;
\ No newline at end of file
......
......@@ -33,6 +33,26 @@ util.noPermission = function(req, res){
res.redirect('/login');
}
// 검색 기능 추가
util.getPostQueryString = function(req, res, next){
res.locals.getPostQueryString = function(isAppended=false, overwrites={}){
var queryString = '';
var queryArray = [];
var page = overwrites.page?overwrites.page:(req.query.page?req.query.page:'');
var limit = overwrites.limit?overwrites.limit:(req.query.limit?req.query.limit:'');
var searchType = overwrites.searchType?overwrites.searchType:(req.query.searchType?req.query.searchType:'');
var searchText = overwrites.searchText?overwrites.searchText:(req.query.searchText?req.query.searchText:'');
if(page) queryArray.push('page='+page);
if(limit) queryArray.push('limit='+limit);
if(searchType) queryArray.push('searchType='+searchType);
if(searchText) queryArray.push('searchText='+searchText);
if(queryArray.length>0) queryString = (isAppended?'&':'?') + queryArray.join('&');
return queryString;
}
next();
}
module.exports = util;
\ No newline at end of file
......
......@@ -135,7 +135,7 @@
<!-- Async script executes immediately and must be after any DOM elements used in callback. -->
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDK6K4iDdo9cKQdrNoOJaaYg29nEG0BIjw&callback=initMap&libraries=&v=weekly"
src="https://maps.googleapis.com/maps/api/js?key=googlekey값을 입력하세요&callback=initMap&libraries=&v=weekly"
async
></script>
</body>
......
......@@ -142,7 +142,7 @@
<!-- Async script executes immediately and must be after any DOM elements used in callback. -->
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDK6K4iDdo9cKQdrNoOJaaYg29nEG0BIjw&callback=initMap&libraries=&v=weekly"
src="https://maps.googleapis.com/maps/api/js?key=googlekey값을 입력하세요&callback=initMap&libraries=&v=weekly"
async
></script>
......
......@@ -47,15 +47,49 @@
</tr>
<% }) %>
</tbody>
</table>
</div>
<nav class="nav justify-content-center bg-light">
<%
var offset = 2;
var previousBtnEnabled = currentPage>1;
var nextBtnEnabled = currentPage<maxPage;
%>
<ul class="pagination pagination-sm justify-content-center align-items-center h-100 mb-0">
<li class="page-item <%= previousBtnEnabled?'':'disabled' %>">
<a class="page-link" href="/posts?page=<%= currentPage-1 %>&limit=<%= limit %>"<%= previousBtnEnabled?'':'tabindex=-1' %></a>
</li>
<% for(i=1;i<=maxPage;i++){ %>
<% if(i==1 || i==maxPage || (i>=currentPage-offset && i<=currentPage+offset)){ %>
<li class="page-item <%= currentPage==i?'active':'' %>"><a class="page-link" href="/posts?page=<%= i %>&limit=<%= limit %>"><%= i %></a></li>
<% } else if(i==2 || i==maxPage-1){ %>
<li><a class="page-link">...</a></li>
<% } %>
<% } %>
<li class="page-item <%= nextBtnEnabled?'':'disabled' %>">
<a class="page-link" href="/posts?page=<%= currentPage+1 %>&limit=<%= limit %>"<%= nextBtnEnabled?'':'tabindex=-1' %></a>
</li>
</ul>
</nav>
<form action="/posts" method="get" style="float:right">
<div class="form-row">
<div class="form-group">
<div class="input-group">
<select name="searchType" class="custom-select">
<option value="title" <%= searchType=='title'?'selected':'' %>>Title</option>
</select>
<input minLength="3" type="text" name="searchText" value="<%= searchText %>">
<div class="input-group-append">
<button class="btn btn-outline-primary" type="submit">Search</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</body>
</html>
\ No newline at end of file
......
......@@ -13,12 +13,13 @@
<ol class="breadcrumb p-1 pl-2 pr-2">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item"><a href="/posts">Board</a></li>
<li class="breadcrumb-item active" aria-current="page"><%= post.title %></li>
<li class="breadcrumb-item active" aria-current="page"><%= post.address %></li>
</ol>
</nav>
<div class="card">
<h5 class="card-header p-2" style="font-weight: bold; font-family: 'Archivo', sans-serif;"><%= post.title %></h5>
<h5 class="card-header p-2" style="font-weight: bold; font-family: 'Archivo', sans-serif; background-color:goldenrod;"><%= post.title %></h5>
<!-- <h5 class="card-header p-1" style="font-family: 'Archivo', sans-serif;"><%= post.address %></h5> -->
<div class="row"> <!-- 1 -->
<div class="col-md-7 col-lg-8 col-xl-9 order-sm-2 order-md-1"> <!-- 1 -->
......@@ -45,7 +46,7 @@
<% if(isAuthenticated && post.author && currentUser.id == post.author.id){ %> <!-- 1 -->
<a class="btn btn-outline-primary" href="/posts/<%= post._id %>/edit">Edit</a>
<form action="/posts/<%= post._id %>?_method=delete" method="post" class="d-inline">
<a class="btn btn-outline-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a>
<a class="btn btn-outline-primary" href="javascript:void(0)" onclick="confirm('기록을 삭제하시겠습니까?')?this.parentElement.submit():null;">Delete</a>
</form>
<% } %>
</div>
......