박유빈

Merge branch 'develop'

...@@ -5,6 +5,28 @@ ...@@ -5,6 +5,28 @@
5 "version": "0.2.0", 5 "version": "0.2.0",
6 "configurations": [ 6 "configurations": [
7 { 7 {
8 + "name": "Launch via NPM",
9 + "request": "launch",
10 + "runtimeArgs": [
11 + "run-script",
12 + "debug"
13 + ],
14 + "runtimeExecutable": "npm run start:dev",
15 + "skipFiles": [
16 + "<node_internals>/**"
17 + ],
18 + "type": "pwa-node"
19 + },
20 + {"name": "Launch ",
21 + "program": "${workspaceFolder}/app.js",
22 + "request": "launch",
23 + "skipFiles": [
24 + "<node_internals>/**"
25 + ],
26 + "type": "pwa-node"
27 + },
28 +
29 + {
8 "type": "pwa-node", 30 "type": "pwa-node",
9 "request": "launch", 31 "request": "launch",
10 "name": "Launch Program", 32 "name": "Launch Program",
......
...@@ -19,7 +19,7 @@ server use Node.js, Koa.js and frotend use flutter(Dart) ...@@ -19,7 +19,7 @@ server use Node.js, Koa.js and frotend use flutter(Dart)
19 * [Node.js](https://nodejs.org/) 19 * [Node.js](https://nodejs.org/)
20 * [Koa.js](https://koajs.com/) 20 * [Koa.js](https://koajs.com/)
21 21
22 -* [flutter](https://flutter.dev/) 22 +
23 23
24 24
25 25
...@@ -31,7 +31,6 @@ server use Node.js, Koa.js and frotend use flutter(Dart) ...@@ -31,7 +31,6 @@ server use Node.js, Koa.js and frotend use flutter(Dart)
31 ## Getting Started 31 ## Getting Started
32 32
33 Go to the Server [Project folder](http://khuhub.khu.ac.kr/2018102946/likeBack), and git clone. 33 Go to the Server [Project folder](http://khuhub.khu.ac.kr/2018102946/likeBack), and git clone.
34 -And go the Frontend [Project folder](http://khuhub.khu.ac.kr/2018102946/likefront)and git clone.
35 34
36 35
37 ### Installation 36 ### Installation
...@@ -72,7 +71,7 @@ _Below is an example of how you can instruct your audience on installing and set ...@@ -72,7 +71,7 @@ _Below is an example of how you can instruct your audience on installing and set
72 Email -- yoobinpark@khu.ac.kr 71 Email -- yoobinpark@khu.ac.kr
73 72
74 Project Link: [http://khuhub.khu.ac.kr/2018102946/likeBack](http://khuhub.khu.ac.kr/2018102946/likeBack) 73 Project Link: [http://khuhub.khu.ac.kr/2018102946/likeBack](http://khuhub.khu.ac.kr/2018102946/likeBack)
75 - [http://khuhub.khu.ac.kr/2018102946/likefront](http://khuhub.khu.ac.kr/2018102946/likefront) 74 +
76 75
77 <p align="right">(<a href="#top">back to top</a>)</p> 76 <p align="right">(<a href="#top">back to top</a>)</p>
78 77
......
1 +[debug] [2021-12-06T16:19:50.991Z] ----------------------------------------------------------------------
2 +[debug] [2021-12-06T16:19:50.996Z] Command: /usr/local/bin/node /usr/local/bin/firebase login
3 +[debug] [2021-12-06T16:19:50.997Z] CLI Version: 9.23.0
4 +[debug] [2021-12-06T16:19:50.997Z] Platform: linux
5 +[debug] [2021-12-06T16:19:50.998Z] Node Version: v16.13.0
6 +[debug] [2021-12-06T16:19:51.007Z] Time: Tue Dec 07 2021 01:19:50 GMT+0900 (Korean Standard Time)
7 +[debug] [2021-12-06T16:19:51.008Z] ----------------------------------------------------------------------
8 +[debug]
9 +[debug] [2021-12-06T16:19:51.014Z] >>> [apiv2][query] GET https://firebase-public.firebaseio.com/cli.json [none]
10 +[info] i Firebase optionally collects CLI usage and error reporting information to help improve our products. Data is collected in accordance with Google's privacy policy (https://policies.google.com/privacy) and is not used to identify you.
11 +
12 +[debug] [2021-12-06T16:19:52.476Z] <<< [apiv2][status] GET https://firebase-public.firebaseio.com/cli.json 200
13 +[debug] [2021-12-06T16:19:52.476Z] <<< [apiv2][body] GET https://firebase-public.firebaseio.com/cli.json {"cloudBuildErrorAfter":1594252800000,"cloudBuildWarnAfter":1590019200000,"defaultNode10After":1594252800000,"minVersion":"3.0.5","node8DeploysDisabledAfter":1613390400000,"node8RuntimeDisabledAfter":1615809600000,"node8WarnAfter":1600128000000}
14 +[info] i To change your data collection preference at any time, run `firebase logout` and log in again.
15 +[info]
16 +[info] Visit this URL on this device to log in:
17 +[info] https://accounts.google.com/o/oauth2/auth?client_id=563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com&scope=email%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloudplatformprojects.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Ffirebase%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&response_type=code&state=406504610&redirect_uri=http%3A%2F%2Flocalhost%3A9005
18 +[info]
19 +[info] Waiting for authentication...
This diff is collapsed. Click to expand it.
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
6 "scripts": { 6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1", 7 "test": "echo \"Error: no test specified\" && exit 1",
8 "start": "node src", 8 "start": "node src",
9 - "start:dev": "nodemon --watch src/ src/index.js" 9 + "start:dev": "NODE_PATH=src nodemon --legacy-watch src/ src/index.js"
10 }, 10 },
11 "author": "", 11 "author": "",
12 "license": "ISC", 12 "license": "ISC",
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 "bcrypt": "^5.0.1", 16 "bcrypt": "^5.0.1",
17 "crypto": "^1.0.1", 17 "crypto": "^1.0.1",
18 "dotenv": "^10.0.0", 18 "dotenv": "^10.0.0",
19 + "ejs": "^3.1.6",
19 "express": "^4.17.1", 20 "express": "^4.17.1",
20 "firebase": "^9.6.0", 21 "firebase": "^9.6.0",
21 "http2": "^3.3.7", 22 "http2": "^3.3.7",
...@@ -23,12 +24,16 @@ ...@@ -23,12 +24,16 @@
23 "koa": "^2.13.4", 24 "koa": "^2.13.4",
24 "koa-body": "^4.2.0", 25 "koa-body": "^4.2.0",
25 "koa-bodyparser": "^4.3.0", 26 "koa-bodyparser": "^4.3.0",
27 + "koa-ejs": "^4.3.0",
28 + "koa-passport": "^4.1.4",
26 "koa-router": "^10.1.1", 29 "koa-router": "^10.1.1",
27 "koa-send": "^5.0.1", 30 "koa-send": "^5.0.1",
31 + "koa-views": "^8.0.0",
28 "mongoose": "^6.0.12", 32 "mongoose": "^6.0.12",
29 "nodemailer": "^6.7.1", 33 "nodemailer": "^6.7.1",
30 "passport": "^0.5.0", 34 "passport": "^0.5.0",
31 "passport-google-oauth": "^2.0.0", 35 "passport-google-oauth": "^2.0.0",
36 + "passport-local": "^1.0.0",
32 "spdy": "^4.0.2" 37 "spdy": "^4.0.2"
33 }, 38 },
34 "devDependencies": { 39 "devDependencies": {
......
...@@ -6,6 +6,9 @@ const nodemailer = require('nodemailer'); ...@@ -6,6 +6,9 @@ const nodemailer = require('nodemailer');
6 const config = require('../../lib/config'); 6 const config = require('../../lib/config');
7 const { Mongoose } = require('mongoose'); 7 const { Mongoose } = require('mongoose');
8 const { exist, allow } = require('@hapi/joi'); 8 const { exist, allow } = require('@hapi/joi');
9 +const index = require('../../../src/index');
10 +const render = index.render;
11 +
9 const fs = require('fs'); 12 const fs = require('fs');
10 exports.test = async (ctx) => { 13 exports.test = async (ctx) => {
11 console.log('cookie'); 14 console.log('cookie');
...@@ -61,7 +64,7 @@ exports.signupLocal = async (ctx) => { ...@@ -61,7 +64,7 @@ exports.signupLocal = async (ctx) => {
61 64
62 // 응답할 데이터에서 hashedPassword 필드 제거 65 // 응답할 데이터에서 hashedPassword 필드 제거
63 ctx.status = 200; 66 ctx.status = 200;
64 - ctx.body = await account.serialize(); 67 + await ctx.render('users/new');
65 } catch (e) { 68 } catch (e) {
66 ctx.throw(500, e); 69 ctx.throw(500, e);
67 } 70 }
...@@ -97,6 +100,7 @@ exports.signinLocal = async (ctx) => { ...@@ -97,6 +100,7 @@ exports.signinLocal = async (ctx) => {
97 httpOnly: false, 100 httpOnly: false,
98 }); 101 });
99 ctx.status = 200; 102 ctx.status = 200;
103 + await ctx.render('users/index');
100 console.log('토큰나옴'); 104 console.log('토큰나옴');
101 } catch (e) { 105 } catch (e) {
102 ctx.throw(500, e); 106 ctx.throw(500, e);
......
1 +
2 +
1 require('dotenv').config() 3 require('dotenv').config()
2 const Router = require("@koa/router"); 4 const Router = require("@koa/router");
3 const authCtrl = require("./auth.ctrl"); 5 const authCtrl = require("./auth.ctrl");
4 const checkLoggedIn = require("../../lib/checkLoggedIn"); 6 const checkLoggedIn = require("../../lib/checkLoggedIn");
5 var passport = require('passport'); 7 var passport = require('passport');
8 +const User = require("../../models/user");
6 var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy; 9 var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
7 const auth = new Router(); 10 const auth = new Router();
8 11
...@@ -26,12 +29,73 @@ auth.get('/book',checkLoggedIn,authCtrl.getUserBook); ...@@ -26,12 +29,73 @@ auth.get('/book',checkLoggedIn,authCtrl.getUserBook);
26 auth.get('/user', checkLoggedIn,authCtrl.userinfo); // show user information 29 auth.get('/user', checkLoggedIn,authCtrl.userinfo); // show user information
27 auth.patch('/user', checkLoggedIn, authCtrl.updateUser); // modify user information 30 auth.patch('/user', checkLoggedIn, authCtrl.updateUser); // modify user information
28 auth.patch('/user/password', authCtrl.changePassword); // change password 31 auth.patch('/user/password', authCtrl.changePassword); // change password
29 - 32 +auth.post('/find/password', authCtrl.findPassword); // 비밀번호 찾기
30 auth.get('/favorite',checkLoggedIn, authCtrl.showFavorite); // show a list of user's favorites 33 auth.get('/favorite',checkLoggedIn, authCtrl.showFavorite); // show a list of user's favorites
31 auth.post('/favorite',checkLoggedIn, authCtrl.addFavorite); // add favorite 34 auth.post('/favorite',checkLoggedIn, authCtrl.addFavorite); // add favorite
32 auth.delete('/favorite',checkLoggedIn, authCtrl.delFavorite); // delete favorite 35 auth.delete('/favorite',checkLoggedIn, authCtrl.delFavorite); // delete favorite
33 36
34 auth.post('/find/password', authCtrl.findPassword); // 비밀번호 찾기 37 auth.post('/find/password', authCtrl.findPassword); // 비밀번호 찾기
38 +auth.get('/new', async (ctx) => {
39 + ctx.render('users/new');
40 + });
41 +
42 +
43 +auth.get('/', async (ctx) => {
44 + try{
45 + const users = await User.find({}).sort({email:-1}).exec();//1 오름차순 -1내림차순
46 + ctx.render('users/index', {users});
47 + }catch(e){
48 + ctx.throw(500, e);
49 + }
50 + }
51 +);
52 +
53 +
54 +// show
55 +auth.get('/:id', async (ctx) => {
56 + const user = await User.findOne({_id:ctx.params._id});
57 + ctx.render('users/show', {user});
58 + });
59 +
60 +
61 +
62 + // edit
63 + auth.get('/:id/edit', async (ctx) => {
64 + const user = await User.findOne({username:ctx.params.username});
65 + ctx.render('users/edit', {user:user});
66 + });
67 +
68 +
69 + // update // 2
70 + auth.post('/:id', async (ctx) => {
71 + await User.findOne({username:ctx.params.username}).select('password').exec();
72 +
73 +
74 + // update user object
75 + user.originalPassword = user.password;
76 + user.password = ctx.body.newPassword? ctx.body.newPassword : user.password; // 2-3
77 + for(var p in ctx.body) // 2-4
78 + user[p] = ctx.body[p];
79 +
80 +
81 + // save updated user
82 + await user.save();
83 +
84 + ctx.redirect('/users/'+user.nickname);
85 + });
86 +
87 +
88 + // destroy
35 89
36 90
37 module.exports = auth; 91 module.exports = auth;
92 +
93 +function checkPermission(ctx, next){
94 + User.findOne({username:ctx.params.username}, function(err, user){
95 + if(err) return res.json(err);
96 + if(user.id != req.user.id) return util.noPermission(ctx);
97 +
98 + next();
99 + });
100 + }
101 +
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -5,17 +5,17 @@ const Joi = require('@hapi/joi'); ...@@ -5,17 +5,17 @@ const Joi = require('@hapi/joi');
5 const config = require('../../lib/config'); 5 const config = require('../../lib/config');
6 const { Mongoose } = require('mongoose'); 6 const { Mongoose } = require('mongoose');
7 exports.booklist = async (ctx) => { 7 exports.booklist = async (ctx) => {
8 - let user; 8 +
9 9
10 try { 10 try {
11 - book = await Book.find() 11 + const books = await Book.find({}).sort({createDate: -1}).exec();
12 - .sort({created: -1}) 12 + await ctx.render('books/index', {book:books});
13 - .exec(); 13 + console.log('get /book/');
14 + //ctx.body = books;
14 } catch (e) { 15 } catch (e) {
15 return ctx.throw(500, e); 16 return ctx.throw(500, e);
16 - }
17 17
18 - ctx.body = book; 18 + }
19 } 19 }
20 20
21 21
...@@ -55,28 +55,24 @@ exports.addBook = async (ctx) => { ...@@ -55,28 +55,24 @@ exports.addBook = async (ctx) => {
55 if (err) throw err; 55 if (err) throw err;
56 const user = await User.findById(ctx.state.user._id).exec(); 56 const user = await User.findById(ctx.state.user._id).exec();
57 console.log(book._id); 57 console.log(book._id);
58 - 58 + ctx.redirect('/books');
59 }); 59 });
60 } catch (e) { 60 } catch (e) {
61 ctx.throw(500, e); 61 ctx.throw(500, e);
62 } 62 }
63 - ctx.body = book; 63 + console.log('저장 성공!');
64 ctx.status = 200; 64 ctx.status = 200;
65 }; 65 };
66 66
67 exports.getOneBook = async (ctx) => { 67 exports.getOneBook = async (ctx) => {
68 68
69 - const bookid = ctx.request.body._id; 69 + const bookid = ctx.params.id;//bookid by parameter
70 console.log(bookid); 70 console.log(bookid);
71 try { 71 try {
72 const mybook = await Book.findById(bookid).exec(); 72 const mybook = await Book.findById(bookid).exec();
73 const user = await User.findById(mybook.author).exec();//작가정보 73 const user = await User.findById(mybook.author).exec();//작가정보
74 - const author_name = user.nickname;
75 - const bookInfo = {
76 - "author_name":author_name
77 - }
78 - ctx.body = {mybook,author_name:author_name}
79 console.log(mybook) 74 console.log(mybook)
75 + ctx.render('books/show', {book:mybook, user:user});
80 //ctx.body.authorname = user.nickname; 76 //ctx.body.authorname = user.nickname;
81 //ctx.body = mybook; 77 //ctx.body = mybook;
82 } catch (e) { 78 } catch (e) {
...@@ -84,7 +80,8 @@ exports.getOneBook = async (ctx) => { ...@@ -84,7 +80,8 @@ exports.getOneBook = async (ctx) => {
84 } 80 }
85 //const user = await User.findById(mybook.author).exec(); 81 //const user = await User.findById(mybook.author).exec();
86 //ctx.body = mybook; 82 //ctx.body = mybook;
87 - 83 + console.log('책 정보 하나! 얻기 성공');
84 + ctx.status = 200;
88 85
89 }; 86 };
90 87
...@@ -95,46 +92,72 @@ exports.updateBook = async (ctx) => { ...@@ -95,46 +92,72 @@ exports.updateBook = async (ctx) => {
95 const file = ctx.request.files; 92 const file = ctx.request.files;
96 if(ctx.request.body.cover != undefined) // when user add a new pet image 93 if(ctx.request.body.cover != undefined) // when user add a new pet image
97 ctx.request.body.cover = fs.readFileSync(file.image.path); 94 ctx.request.body.cover = fs.readFileSync(file.image.path);
98 - else 95 + else{
99 ctx.request.body.cover = "" 96 ctx.request.body.cover = ""
100 - 97 + }
101 var book = ctx.request.body; //require books's _id 98 var book = ctx.request.body; //require books's _id
102 try { 99 try {
103 - const mybook = await Book.findOne({ _id: book._id }); 100 + const mybook = await Book.findOne({ _id: ctx.params.id });
104 - 101 + if(ctx.state.user._id == mybook.author){//작성한 사람이 맞을 때만
105 - mybook.updateB(book); 102 + await mybook.updateB(book);
106 - 103 + await ctx.redirect('/api/page/detail/'+ctx.params.id);
107 - ctx.body = book._id; 104 + ctx.status = 200;}
108 - } catch (e) { 105 + else{
109 - ctx.throw(500, e); 106 + console.log('작성자가 아니다. ');
107 + ctx.status = 400;
110 } 108 }
111 - ctx.body = book._id; 109 + }
110 + catch (e) {
111 + ctx.throw(500, e);
112 112
113 - console.log(book); 113 + ctx.status = 400;
114 - ctx.status = 200; 114 + ctx.body = {
115 + message: "작성자가 아닙니다. " }
116 + }
115 }; 117 };
116 118
117 119
118 exports.deleteBook = async (ctx) => { 120 exports.deleteBook = async (ctx) => {
121 +
122 + let bookid =ctx.params.id;
119 try { 123 try {
120 - var b = await Book.find(ctx.params.id); 124 +
121 - await Page.deleteMany({"pages":{$in:b.pages}});//book에 있던 페이지 다 지우기 125 + //var foundB = await Book.findById(bookid).exec();
126 + //북작가에게서 책 정보 지우기
127 + var author = await User.findById(bookid);//북 작가.
128 + console.log(author);
129 + console.log(author.books);
130 + await User.delBook(ctx.state.user.email,bookid);
131 + //book에 있던 페이지 다 지우기
132 + await Page.deleteMany({"pages":{$in:b.pages}});
133 +
134 +
135 + //최종 book지우기
136 + var b = await Book.deleteOne({_id:bookid});
122 } catch (e) { 137 } catch (e) {
123 if(e.name === 'CastError') { 138 if(e.name === 'CastError') {
124 ctx.status = 400; 139 ctx.status = 400;
125 return; 140 return;
126 } 141 }
127 } 142 }
128 - ctx.status = 204; // No Content 143 + console.log('delete success');
144 + ctx.body = {
145 + message: "Delete"
146 + }
129 }; 147 };
130 148
131 exports.detailBook = async (ctx) => { 149 exports.detailBook = async (ctx) => {
132 - var ObjectId = require('mongodb').ObjectId; 150 + try{
133 - var id = req.params.book_id; 151 + var id = ctx.params.id;
134 - var o_id = new ObjectId(id); 152 + const book = await dBook.find({_id:id});
135 - const book = await db.Book.find({_id:o_id}); 153 + ctx.render('books/show', {book:book});
136 - ctx.body = book;
137 ctx.status = 200; 154 ctx.status = 200;
155 + } catch (e) {
156 + ctx.status = 400;
157 + console.log('북 삭제 오류');
158 + }
159 +
160 +
138 }; 161 };
139 162
140 exports.scrollBook = async (ctx) => { 163 exports.scrollBook = async (ctx) => {
...@@ -154,8 +177,4 @@ exports.scrollBook = async (ctx) => { ...@@ -154,8 +177,4 @@ exports.scrollBook = async (ctx) => {
154 ctx.throw(500, e); 177 ctx.throw(500, e);
155 } 178 }
156 } 179 }
157 -
158 - else if(filter === "추천순") { //추천순
159 -
160 - }
161 }; 180 };
......
...@@ -2,14 +2,154 @@ const Router = require("@koa/router"); ...@@ -2,14 +2,154 @@ const Router = require("@koa/router");
2 const checkLoggedIn = require("../../lib/checkLoggedIn"); 2 const checkLoggedIn = require("../../lib/checkLoggedIn");
3 const bookCtrl = require("./book.ctrl"); 3 const bookCtrl = require("./book.ctrl");
4 const book = new Router(); 4 const book = new Router();
5 +const User = require("../../models/user");
6 +const Book = require("../../models/book");
7 +//북id params로 전달. Render books/show
5 8
6 -book.get('/',bookCtrl.getOneBook); 9 +book.get('/:id', async (ctx) => {
10 + const bookid = ctx.params.id;//bookid by parameter
11 + console.log(bookid);
12 + try {
13 + const book = await Book.findOne({_id:ctx.params.id});
14 + ctx.render('books/show', {book});
15 + //ctx.body.authorname = user.nickname;
16 + //ctx.body = mybook;
17 + } catch (e) {
18 + ctx.throw(500, e);
19 + }
20 + //const user = await User.findById(mybook.author).exec();
21 + //ctx.body = mybook;
22 + console.log('책 정보 하나! 얻기 성공');
23 + ctx.status = 200;
24 +});
7 //mybook=page,title 25 //mybook=page,title
8 //author_name = author name 26 //author_name = author name
9 -book.post('/',checkLoggedIn,bookCtrl.addBook); // add book 27 +book.post('/',checkLoggedIn,async (ctx) => {
10 -book.patch('/', checkLoggedIn, bookCtrl.updateBook); // modify book information
11 -book.delete('/',checkLoggedIn,bookCtrl.deleteBook); // delete book
12 -book.get('/booklist',bookCtrl.booklist);
13 -book.get('/search',bookCtrl.scrollBook);
14 28
29 + const {
30 + title,
31 + author,
32 + contents,
33 + cover,
34 + hashtag,
35 + } = ctx.request.body;
36 +
37 + const schema = Joi.object().keys({
38 + title: Joi.string().required(),
39 + author: Joi.string(),
40 + contents: Joi.string().allow(null, ''),
41 + hashtag: Joi.string().allow(null, ''),
42 + cover: Joi.allow(null, ''),
43 + });
44 + try {
45 + await schema.validateAsync(ctx.request.body);
46 + } catch (err) {
47 + console.log('add book validaton' + err);
48 + ctx.status = 400;
49 + return;
50 + }
51 + ctx.request.body.author = ctx.state.user;
52 + const book = new Book(ctx.request.body);
53 +
54 + try {
55 + book.save(async (err) => {
56 + if (err) throw err;
57 + const user = await User.findById(ctx.state.user._id).exec();
58 + console.log(book._id);
59 + ctx.redirect('/books');
60 + });
61 + } catch (e) {
62 + ctx.throw(500, e);
63 + }
64 + console.log('저장 성공!');
65 + ctx.status = 200;
66 +});
67 +// add book
68 +// redirect posts (create)
69 +
70 +book.patch('/:id', checkLoggedIn, async (ctx) => {
71 + const file = ctx.request.files;
72 + if(ctx.request.body.cover != undefined) // when user add a new pet image
73 + ctx.request.body.cover = fs.readFileSync(file.image.path);
74 + else{
75 + ctx.request.body.cover = ""
76 + }
77 + var book = ctx.request.body; //require books's _id
78 + try {
79 + const mybook = await Book.findOne({ _id: ctx.params.id });
80 + if(ctx.state.user._id == mybook.author){//작성한 사람이 맞을 때만
81 + await mybook.updateB(book);
82 + await ctx.redirect('/api/page/detail/'+ctx.params.id);
83 + ctx.status = 200;}
84 + else{
85 + console.log('작성자가 아니다. ');
86 + ctx.status = 400;
87 + }
88 + }
89 + catch (e) {
90 + ctx.throw(500, e);
91 +
92 + ctx.status = 400;
93 + ctx.body = {
94 + message: "작성자가 아닙니다. " }
95 + }
96 +});// modify book information
97 +//update book rediret:"/books/"+ctx.params.id
98 +//book.id params
99 +book.delete('/:id',checkLoggedIn,async (ctx) => {
100 + let bookid =ctx.params.id;
101 + try {
102 +
103 + //var foundB = await Book.findById(bookid).exec();
104 + //북작가에게서 책 정보 지우기
105 + var author = await User.findById(bookid);//북 작가.
106 + console.log(author);
107 + console.log(author.books);
108 + await User.delBook(ctx.state.user.email,bookid);
109 + //book에 있던 페이지 다 지우기
110 + await Page.deleteMany({"pages":{$in:b.pages}});
111 +
112 +
113 + //최종 book지우기
114 + var b = await Book.deleteOne({_id:bookid});
115 + } catch (e) {
116 + if(e.name === 'CastError') {
117 + ctx.status = 400;
118 + return;
119 + }
120 + }
121 + console.log('delete success');
122 + ctx.body = {
123 + message: "Delete"
124 + }
125 +}); // delete book
126 +// params.id
127 +//redirect('/books')<-index
128 +book.get('/',async (ctx) => {
129 + try {
130 + const books = await Book.find({}).sort({createDate: -1}).exec();
131 + ctx.render('books/index', {books:books});
132 + } catch (e) {
133 + return ctx.throw(500, e);
134 + }
135 + }
136 +);
137 +book.get('/search', async (ctx) => {
138 + const {filter, renewal} = ctx.query
139 +
140 + if(filter === "조회순") { //조회순
141 + try {
142 + const books = await Book.find().sort({'views': -1}).skip(parseInt(renewal)*10).limit(10)
143 + let result = await bookInfo(books);
144 + ctx.status = 200;
145 + ctx.body = result;
146 + } catch (e) {
147 + ctx.throw(500, e);
148 + }
149 + }
150 +});
151 +
152 +book.get('/new', async (ctx) => {
153 + ctx.render('books/new');
154 + });
15 module.exports = book; 155 module.exports = book;
......
1 const Router = require("koa-router"); 1 const Router = require("koa-router");
2 -const page = require("./page"); 2 +//const page = require("./page");
3 -const auth = require("./auth"); 3 +//const auth = require("./auth");
4 -const book = require("./book"); 4 +//const book = require("./book");
5 +//const render = require('../index/');
5 const api = new Router(); 6 const api = new Router();
7 +//const path = require('path');
6 8
7 -api.use("/auth", auth.routes());
8 -api.use("/book", book.routes());
9 -api.use("/page", page.routes());
10 9
11 -api.get('/test', (ctx) => (ctx.body = 'hi')); 10 +
12 -module.exports = api; 11 +///api.use("/auth", auth.routes());
12 +//api.use("/book", book.routes());
13 +//api.use("/page", page.routes());
14 +
15 +
16 +
17 +//module.exports = api;
13 18
......
...@@ -2,8 +2,12 @@ const Router = require("@koa/router"); ...@@ -2,8 +2,12 @@ const Router = require("@koa/router");
2 const checkLoggedIn = require("../../lib/checkLoggedIn"); 2 const checkLoggedIn = require("../../lib/checkLoggedIn");
3 const pageCtrl = require("./page.ctrl"); 3 const pageCtrl = require("./page.ctrl");
4 const page = new Router(); 4 const page = new Router();
5 - 5 +const Page = require("../../models/page");
6 +const index = require('../../../src/index');
7 +//const render = require('koa-ejs');
6 //Page api 8 //Page api
9 +
10 +/*
7 page.get('/',pageCtrl.getPage); // show a list of user's pages 11 page.get('/',pageCtrl.getPage); // show a list of user's pages
8 page.post('/',checkLoggedIn,pageCtrl.addPage); // add page 12 page.post('/',checkLoggedIn,pageCtrl.addPage); // add page
9 page.patch('/', checkLoggedIn,pageCtrl.updatePage); // modify page information 13 page.patch('/', checkLoggedIn,pageCtrl.updatePage); // modify page information
...@@ -11,12 +15,61 @@ page.delete('/',checkLoggedIn,pageCtrl.deletePage); // delete book ...@@ -11,12 +15,61 @@ page.delete('/',checkLoggedIn,pageCtrl.deletePage); // delete book
11 15
12 page.get('/search', pageCtrl.search); // /search?title=search_query&petType=petType 16 page.get('/search', pageCtrl.search); // /search?title=search_query&petType=petType
13 //page.post('/search/filter', pageCtrl.searchFilter); //아직 구현 안함 17 //page.post('/search/filter', pageCtrl.searchFilter); //아직 구현 안함
14 -page.post('/detail', pageCtrl.detailPage); // detail recipe page 18 +page.post('/:id', pageCtrl.detailPage); // detail recipe page
15 19
16 //page.get('/recipe/slide', pageCtrl.slidRecipe); // 5 recommended videos in main page 20 //page.get('/recipe/slide', pageCtrl.slidRecipe); // 5 recommended videos in main page
17 21
18 // /recipe/scroll?filter=filter_query&renewal=count (filter_query: 추천순 or 조회순) 22 // /recipe/scroll?filter=filter_query&renewal=count (filter_query: 추천순 or 조회순)
19 page.get('/recipe/scroll', pageCtrl.scrollPage); // video list sorted by 추천순 or 조회순 in main page 23 page.get('/recipe/scroll', pageCtrl.scrollPage); // video list sorted by 추천순 or 조회순 in main page
24 +*/
25 +
26 +
27 + page.get('/', async (ctx) => {
28 +
29 + const page = await Page.find({}).sort({createDate:-1}).exec();
30 + console.log(page);
31 + await ctx.render('posts/index', {page});
32 + });
33 +
34 +
35 + page.get('/new', async (ctx) => {
36 + await ctx.render('posts/new');
37 + });
38 +
39 + // create
40 + page.post('/', async (ctx, next) => {
41 + await Page.create(ctx.body);
42 + ctx.redirect('/posts');
43 + });
44 +
45 +
46 +
47 + // show
48 + page.get('/:id', async (ctx, next) => {
49 + const page = await Page.findOne({_id:ctx.params.id});
50 + console.log('찾은 페이지',page);
51 + ctx.render('posts/show', {page:page,user:ctx.state.user});
52 + });
53 +
54 +
55 +// update
56 +page.put('/:id', async (ctx, next) => {
57 + ctx.body.updatedAt = Date.now(); //2
58 + const page = await Page.findOneAndUpdate({_id:ctx.params.id}, ctx.body);
59 + ctx.redirect("/posts/"+ctx.params.id);
60 + });
61 +
62 + // destroy
63 + page.delete('/:id', async (ctx, next) => {
64 + try{
65 + await Page.deleteOne({_id:ctx.params.id})
66 + ctx.redirect('/posts');
67 + }catch(e){
68 + ctx.throw(500, e);
69 + }
70 + });
71 +
72 +
20 73
21 //page.post('/postinfo', pageCtrl.uploadInfo); 74 //page.post('/postinfo', pageCtrl.uploadInfo);
22 //page.get('/info',pageCtrl.getbyurl);//url로 recipe정보 가져오기 (flutter 내 레시피에서 쓰임.) 75 //page.get('/info',pageCtrl.getbyurl);//url로 recipe정보 가져오기 (flutter 내 레시피에서 쓰임.)
......
...@@ -4,6 +4,8 @@ const Book = require("../../models/book"); ...@@ -4,6 +4,8 @@ const Book = require("../../models/book");
4 const Joi = require('@hapi/joi'); 4 const Joi = require('@hapi/joi');
5 const config = require('../../lib/config'); 5 const config = require('../../lib/config');
6 const { Mongoose } = require('mongoose'); 6 const { Mongoose } = require('mongoose');
7 +
8 +
7 exports.search = async (ctx) => { 9 exports.search = async (ctx) => {
8 const { title } = ctx.query; //search word 10 const { title } = ctx.query; //search word
9 console.log(title) 11 console.log(title)
...@@ -19,12 +21,18 @@ exports.search = async (ctx) => { ...@@ -19,12 +21,18 @@ exports.search = async (ctx) => {
19 21
20 22
21 exports.detailPage = async (ctx) => { 23 exports.detailPage = async (ctx) => {
24 + try{
22 var ObjectId = require('mongodb').ObjectId; 25 var ObjectId = require('mongodb').ObjectId;
23 - var id = req.params.page_id; 26 + var id = req.params.id;
24 var o_id = new ObjectId(id); 27 var o_id = new ObjectId(id);
25 const page = await db.Page.find({_id:o_id}); 28 const page = await db.Page.find({_id:o_id});
29 + await ctx.redirect('/api/page/detail/'+req.params.id);
26 ctx.body = page; 30 ctx.body = page;
27 ctx.status = 200; 31 ctx.status = 200;
32 + }catch(e){
33 + ctx.throw(500,e);
34 + }
35 +
28 }; 36 };
29 37
30 // parameters: array with book objects as elements 38 // parameters: array with book objects as elements
...@@ -149,19 +157,21 @@ exports.scrollPage = async (ctx) => { ...@@ -149,19 +157,21 @@ exports.scrollPage = async (ctx) => {
149 else 157 else
150 ctx.request.body.image = "" 158 ctx.request.body.image = ""
151 159
152 - var user = ctx.request.body; //require user id 160 + var page = ctx.request.body;
153 try { 161 try {
154 - const mybook = await Book.findOne({ _id: book._id }); 162 + const mypage = await Page.findOne({ _id: page._id });//require page's _id
163 + if(ctx.state.user._id == mypage.author){
164 + mypage.updateP(book);
165 + ctx.body = mypage;
166 + console.log(mypage);
167 + ctx.status = 200;}
155 168
156 - mybook.updateP(book);
157 -
158 - ctx.body = user._id;
159 } catch (e) { 169 } catch (e) {
160 ctx.throw(500, e); 170 ctx.throw(500, e);
171 + ctx.body = {
172 + message: "작성자가 아닙니다. " }
161 } 173 }
162 - ctx.body = user._id;
163 174
164 - console.log(user);
165 ctx.status = 200; 175 ctx.status = 200;
166 }; 176 };
167 177
...@@ -178,18 +188,3 @@ exports.deletePage = async (ctx) => { ...@@ -178,18 +188,3 @@ exports.deletePage = async (ctx) => {
178 } 188 }
179 ctx.status = 204; // No Content 189 ctx.status = 204; // No Content
180 }; 190 };
181 -
182 -exports.getPage = async (ctx) => {
183 - const pageid = ctx.request.body._id;
184 - try {
185 - const mypage = await Book.findById(pageid).exec();
186 - const user = await User.findById(mypage.author).exec();//작가정보
187 - const author_name = user.nickname;
188 - ctx.body = {mypage,author_name:author_name}
189 - console.log(mypage)
190 -
191 - } catch (e) {
192 - ctx.throw(500, e);
193 - }
194 -
195 -};
......
...@@ -9,8 +9,17 @@ const mongoose = require('mongoose'); ...@@ -9,8 +9,17 @@ const mongoose = require('mongoose');
9 const bodyParser = require('koa-bodyparser'); 9 const bodyParser = require('koa-bodyparser');
10 const port = process.env.PORT || 3000 10 const port = process.env.PORT || 3000
11 //443 11 //443
12 +const passport = require('koa-passport')
12 const app = new Koa(); 13 const app = new Koa();
13 const router = new Router() 14 const router = new Router()
15 +const render = require('koa-ejs');
16 +const path = require('path');
17 +//const User = require('../models/user');
18 +//const views = require('koa-views');
19 +const book = require('./routes/book');
20 +const auth = require('./routes/user');
21 +const page = require('./routes/page');
22 +
14 const send = require('koa-send'); 23 const send = require('koa-send');
15 var options = { 24 var options = {
16 key: fs.readFileSync('./server.key'), 25 key: fs.readFileSync('./server.key'),
...@@ -29,53 +38,90 @@ mongoose.connect(process.env.MONGO_URI).then( ...@@ -29,53 +38,90 @@ mongoose.connect(process.env.MONGO_URI).then(
29 console.error(e); 38 console.error(e);
30 }); 39 });
31 40
32 -var readFileThunk = function(src) { 41 +
33 - return new Promise(function (resolve, reject) {
34 - fs.readFile(src, {'encoding': 'utf8'}, function (err, data) {
35 - if(err) return reject(err);
36 - resolve(data);
37 - });
38 - });
39 -}
40 //app.use(router.routes()) 42 //app.use(router.routes())
41 /* 43 /*
42 app 44 app
43 .use(router.routes()) 45 .use(router.routes())
44 .use(router.allowedMethods()); 46 .use(router.allowedMethods());
47 +
48 +
49 + render(app, {
50 + root: path.join(__dirname, 'views'),
51 + layout: false,
52 + viewExt: 'ejs',
53 + cache: false,
54 + debug: true
55 + });
45 */ 56 */
57 +// app.use(async function (ctx) {
58 + // await ctx.render('home/welcome');
59 + // });
46 app 60 app
47 .use(jwtMiddleware) 61 .use(jwtMiddleware)
48 - .use(bodyParser()) // bodyParser는 라우터 코드보다 상단에 있어야 합니다. 62 + .use(bodyParser()); // bodyParser는 라우터 코드보다 상단에 있어야 합니다.
49 - .use(router.routes())
50 - .use(router.allowedMethods());
51 63
64 + render(app, {
65 + root: path.join(__dirname, 'views'),
66 + layout: false,
67 + viewExt: 'ejs',
68 + cache: false,
69 + debug: true
70 +});
52 71
72 +app
73 +.use(router.routes())
74 +.use(router.allowedMethods())
75 +.use(passport.initialize());
53 76
54 -/* 77 + // .use(views(path.join(__dirname, 'views'), {
55 -router.get('/', (ctx, next) => { 78 + //extension: 'ejs'
56 - ctx.body = '루트 페이지 입니다.'; 79 +//}))
80 +
81 + //.use(views('views', { map: { html: 'ejs' } }));
82 + router.get('/', async ctx =>{
83 + await ctx.render('home/welcome');
57 }); 84 });
58 -*/ 85 +
59 -/* 86 +router.get('/about', async ctx =>{
60 -router.get('/', async (ctx, next) => { 87 + ctx.render('home/about');
61 - const rawContent = fs.readFileSync('index.html').toString('utf8') 88 +});
62 - ctx.body = rawContent 89 +router.get('/auth', async ctx =>{
63 -})*/ 90 + ctx.render('users/login');
64 - 91 +});
65 -router.get('/', function *(){ 92 +
66 - this.body = yield readFileThunk(__dirname + '/public/index.html'); 93 +//router.use('/', api.routes());
67 -}) 94 +//router.use("/book", require("./routes/book"));
95 +//router.use("/page", require("./routes/page"));
96 +//router.use("/auth", require("./routes/user"));
97 +router.use('/auth', auth.routes());
98 +router.use('/book', book.routes());
99 +router.use('/page', page.routes());
100 +app.use(router.routes()).use(router.allowedMethods());
101 +
102 +
103 +
104 +
105 +//router.get('/', async (ctx, next) => {
106 + // const rawContent = fs.readFileSync('index.html').toString('utf8')
107 + //ctx.body = rawContent
108 +//})
109 +
110 +
68 //router.use(api.routes()); 111 //router.use(api.routes());
69 //app.use(router.routes()).use(router.allowedMethods()) 112 //app.use(router.routes()).use(router.allowedMethods())
70 -router.use('/api', api.routes()); 113 +
71 -app.use(router.routes()).use(router.allowedMethods()); 114 +//app.use(router.routes()).use(router.allowedMethods());
115 +
72 116
73 http2 117 http2
74 .createSecureServer(options, app.callback()) 118 .createSecureServer(options, app.callback())
75 .listen(port, () => console.log("listening on port %i", port)); 119 .listen(port, () => console.log("listening on port %i", port));
76 -//app.listen(port, function () {
77 -//console.log('server listening on port %d', port);
78 - //});
79 120
121 + /*
122 + app.listen(port, function () {
123 +console.log('server listening on port %d', port);
124 + });
125 +*/
80 126
81 127
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -10,3 +10,13 @@ const checkLoggedIn = (ctx, next) => { ...@@ -10,3 +10,13 @@ const checkLoggedIn = (ctx, next) => {
10 module.exports = checkLoggedIn; 10 module.exports = checkLoggedIn;
11 11
12 12
13 + const User = require('../models/user')
14 + const checkPermission = (ctx, next) => {
15 + const user = User.findOne({username:ctx.params.username});
16 + if(user._id != ctx.status.user._id)
17 + return util.noPermission(ctx);
18 + next();
19 + };
20 + module.exports = checkPermission;
21 +
22 +
...\ No newline at end of file ...\ No newline at end of file
......
1 const config = { 1 const config = {
2 mailer: { 2 mailer: {
3 - user: "yoobinpark@khu.ac.kr", 3 + user: "like01test@gmail.com",
4 - password: "1q2w3e4r!@", 4 + password: "like1412",
5 expiresIn: 60 * 5, 5 expiresIn: 60 * 5,
6 }, 6 },
7 }; 7 };
......
...@@ -5,9 +5,9 @@ const { Schema } = mongoose; ...@@ -5,9 +5,9 @@ const { Schema } = mongoose;
5 5
6 const bookSchema = new Schema({ 6 const bookSchema = new Schema({
7 id: mongoose.Schema.Types.ObjectId,//unique number of page 7 id: mongoose.Schema.Types.ObjectId,//unique number of page
8 - pages:[{type:String}],//book contains several pages. pages is the list of page id 8 + pages:[{type:mongoose.Schema.Types.ObjectId, ref:'page', required:false}],//book contains several pages. pages is the list of page id
9 title: {type:String, require:true}, // book title 9 title: {type:String, require:true}, // book title
10 - author: [{type:String, require:true, default:user.user}],//작가복수 가능 10 + author: [{type:mongoose.Schema.Types.ObjectId, ref:'user', required:true}],//작가복수 가능
11 contents: {type:String}, // book subtitle or detail 11 contents: {type:String}, // book subtitle or detail
12 createDate: {type:Date, require:true, default:Date.now}, 12 createDate: {type:Date, require:true, default:Date.now},
13 updateDate: {type:Date, default:Date.now}, 13 updateDate: {type:Date, default:Date.now},
...@@ -103,4 +103,5 @@ const bookSchema = new Schema({ ...@@ -103,4 +103,5 @@ const bookSchema = new Schema({
103 103
104 104
105 105
106 +
106 module.exports = mongoose.model("Book", bookSchema); 107 module.exports = mongoose.model("Book", bookSchema);
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -4,9 +4,9 @@ const { Schema } = mongoose; ...@@ -4,9 +4,9 @@ const { Schema } = mongoose;
4 4
5 const PageSchema = new Schema({ 5 const PageSchema = new Schema({
6 id: mongoose.Schema.Types.ObjectId,//unique number of page 6 id: mongoose.Schema.Types.ObjectId,//unique number of page
7 - book:{type:String},//book contains several pages 7 + book:[{type:mongoose.Schema.Types.ObjectId, ref:'book', required:false}],//book contains several pages
8 title: {type:String, require:true}, // title of post 8 title: {type:String, require:true}, // title of post
9 - author: [{type:String, require:true, default:user.user}], 9 + author: [{type:mongoose.Schema.Types.ObjectId, ref:'user', required:true}],
10 contents: {type:String}, // contents of page 10 contents: {type:String}, // contents of page
11 createDate: {type:Date, require:true, default:Date.now}, 11 createDate: {type:Date, require:true, default:Date.now},
12 updateDate: {type:Date, default:Date.now}, 12 updateDate: {type:Date, default:Date.now},
......
...@@ -12,10 +12,10 @@ const UserSchema = new Schema({ ...@@ -12,10 +12,10 @@ const UserSchema = new Schema({
12 password: String, 12 password: String,
13 token: String, 13 token: String,
14 //user info 14 //user info
15 - username: String,
16 phone: {type: String, require: true}, 15 phone: {type: String, require: true},
17 nickname: {type: String, default: ""}, 16 nickname: {type: String, default: ""},
18 - books: [{type: String}],//array of bookid 17 + books: [{type:mongoose.Schema.Types.ObjectId, ref:'book', required:false}],//array of bookid
18 + pages:[{type:mongoose.Schema.Types.ObjectId, ref:'page', required:false}],
19 favorite: [{ type: String }],//String of url(book, page) 19 favorite: [{ type: String }],//String of url(book, page)
20 }); 20 });
21 21
......
1 +/* global style */
2 +form label{
3 + padding: 3px;
4 + margin-bottom: 0;
5 + font-weight: 300;
6 + }
7 + form .form-control{
8 + padding: 3px 7px;
9 + font-size: inherit;
10 + line-height: 20px;
11 + height: auto;
12 + border: 1px solid #ccc;
13 + border-radius: 3px;
14 + width: 100%;
15 + }
16 + form fieldset{
17 + margin: 7px 0 1px;
18 + padding: 0px 15px;
19 + }
20 + form .form-group{
21 + margin: 0;
22 + padding-bottom: 6px;
23 + }
24 + .ellipsis{
25 + display: block;
26 + width: 100%;
27 + white-space: nowrap;
28 + overflow: hidden;
29 + text-overflow: ellipsis;
30 + }
31 + .form-horizontal .control-label {
32 + padding-top: 5px;
33 + text-align: left;
34 + }
35 + .buttons {
36 + margin: 7px 0;
37 + padding: 0 5px;
38 + }
39 + .buttons form{
40 + display: inline-block;
41 + }
42 + .buttons .btn-default{
43 + min-width: 55px;
44 + color: #165751;
45 + border: 1px solid #ccc;
46 + border-right: 1px solid #aaa;
47 + border-bottom: 1px solid #aaa;
48 + border-radius: 4px;
49 + padding: 5px 10px;
50 + background-color: #f5f5f5;
51 + font-size: 0.8em;
52 + font-weight: bold;
53 + }
54 + .buttons .btn-default:hover{
55 + text-decoration: none;
56 + position: relative;
57 + top: 1px;
58 + left: 1px;
59 + border: 1px solid #ccc;
60 + border-top: 1px solid #aaa;
61 + border-left: 1px solid #aaa;
62 + }
63 + .contentBox{
64 + border-top: 1px solid #ccc;
65 + border-bottom: 1px solid #ccc;
66 + }
67 + .contentBoxTop {
68 + font-size: 14px;
69 + font-weight: 600;
70 + margin: 0;
71 + border-bottom: 1px solid #ccc;
72 + background-color: #F5F5F5;
73 + padding: 6px 15px;
74 + }
75 +
76 + /* home style */
77 + .home h1{
78 + color: darkseagreen;
79 + }
80 + .home-login{
81 + max-width: 330px;
82 + font-family: 'Open Sans', sans-serif;
83 + font-size: 12px;
84 + }
85 +
86 + /* post style */
87 + .post {
88 + max-width: 670px;
89 + font-family: 'Open Sans', sans-serif;
90 + font-size: 12px;
91 + }
92 + .post h2 {
93 + color: tomato;
94 + text-align: center;
95 + }
96 + .post .post-body {
97 + white-space: pre-line;
98 + padding: 6px 15px 20px;
99 + }
100 + .post .post-info{
101 + font-size: 11px;
102 + margin: 5px;
103 + padding: 5px 10px;
104 + background-color: #E4E4E4;
105 + border: 0 solid black;
106 + border-radius: 5px;
107 + }
108 + .post .post-info>div{
109 + padding: 5px 0;
110 + border-top: 1px dotted #999;
111 + }
112 + .post .post-info>div:first-child{
113 + border-top: none;
114 + }
115 + .post .post-info span{
116 + display: inline-block;
117 + width: 40px;
118 + }
119 + .post-index .posts{
120 + border-top: 1px solid #ccc;
121 + border-bottom: 1px solid #ccc;
122 + table-layout: fixed;
123 + }
124 + .post .posts th,
125 + .post .posts td
126 + {
127 + padding: 7px 5px;
128 + }
129 + .post .posts th:first-child,
130 + .post .posts td:first-child
131 + {
132 + padding-left: 15px;
133 + }
134 + .post .posts thead{
135 + background-color: #F5F5F5;
136 + }
137 + .post .posts thead tr th{
138 + border-bottom: 1px solid #ccc;
139 + }
140 + .post .posts tbody tr:nth-child(odd){
141 + background-color: #F4FFFF;
142 + }
143 + .post .posts tbody .noData{
144 + background-color: #FFFFFF;
145 + text-align: center;
146 + }
147 + .post .posts .author{
148 + text-align: center;
149 + width: 80px;
150 + }
151 + .post .posts .date{
152 + text-align: center;
153 + width: 100px;
154 + padding-right: 15px;
155 + }
156 + .post-new,
157 + .post-edit{
158 + max-width: 520px;
159 + }
160 +
161 + /* user style */
162 + .user {
163 + max-width: 320px;
164 + font-family: 'Open Sans', sans-serif;
165 + font-size: 12px;
166 + }
167 + .user-index ul{
168 + margin: 0;
169 + padding: 3px 12px;
170 + }
171 + .user-index ul:after {
172 + content: "";
173 + display: block;
174 + clear: both;
175 + }
176 + .user-index ul li{
177 + display: inline-block;
178 + list-style-type: none;
179 + }
180 + .user-index ul li a{
181 + display: inline-block;
182 + text-decoration:none;
183 + margin: 3px;
184 + background-color: #eee;
185 + padding: 3px 10px;
186 + border-radius: 3px;
187 + }
188 + .user-index ul li a:hover{
189 + background-color: #ccc;
190 + }
191 + .user-edit hr{
192 + margin-top: 5px;
193 + margin-bottom: 11px;
194 + }
195 + body {
196 + padding: 50px;
197 + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
198 +}
199 +
200 +a {
201 + color: #00B7FF;
202 +}
203 +
204 +.wrapper {
205 + margin-top: 80px;
206 + margin-bottom: 20px;
207 +}
208 +
209 +.form-signin {
210 + max-width: 420px;
211 + padding: 30px 38px 66px;
212 + margin: 0 auto;
213 + background-color: #eee;
214 + border: 3px dotted rgba(0,0,0,0.1);
215 +}
216 +
217 +.form-signin-heading {
218 + text-align:center;
219 + margin-bottom: 30px;
220 +}
221 +
222 +.form-control {
223 + position: relative;
224 + font-size: 16px;
225 + height: auto;
226 + padding: 10px;
227 +}
228 +
229 +input[type="text"] {
230 + margin-bottom: 0px;
231 + border-bottom-left-radius: 0;
232 + border-bottom-right-radius: 0;
233 +}
234 +
235 +input[type="password"] {
236 + margin-bottom: 20px;
237 + border-top-left-radius: 0;
238 + border-top-right-radius: 0;
239 +}
1 +
2 +$(function(){
3 + function get2digits (num){
4 + return ('0' + num).slice(-2);
5 + }
6 +
7 + function getDate(dateObj){
8 + if(dateObj instanceof Date)
9 + return dateObj.getFullYear() + '-' + get2digits(dateObj.getMonth()+1)+ '-' + get2digits(dateObj.getDate());
10 + }
11 +
12 + function getTime(dateObj){
13 + if(dateObj instanceof Date)
14 + return get2digits(dateObj.getHours()) + ':' + get2digits(dateObj.getMinutes())+ ':' + get2digits(dateObj.getSeconds());
15 + }
16 +
17 + function convertDate(){
18 + $('[data-date]').each(function(index,element){
19 + var dateString = $(element).data('date');
20 + if(dateString){
21 + var date = new Date(dateString);
22 + $(element).html(getDate(date));
23 + }
24 + });
25 + }
26 +
27 + function convertDateTime(){
28 + $('[data-date-time]').each(function(index,element){
29 + var dateString = $(element).data('date-time');
30 + if(dateString){
31 + var date = new Date(dateString);
32 + $(element).html(getDate(date)+' '+getTime(date));
33 + }
34 + });
35 + }
36 +
37 + convertDate();
38 + convertDateTime();
39 + });
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require("@koa/router");
2 +const checkLoggedIn = require("../../src/lib/checkLoggedIn");
3 +//const bookCtrl = require("./book.ctrl");
4 +const book = new Router();
5 +const User = require("../models/user")
6 +const Book = require("../models/book");
7 +//북id params로 전달. Render books/show
8 +book.get('/:id', async (ctx) => {
9 + const bookid = ctx.params.id;//bookid by parameter
10 + console.log(bookid);
11 + try {
12 + const book = await Book.findOne({_id:ctx.params.id});
13 + ctx.render('books/show', {book});
14 + //ctx.body.authorname = user.nickname;
15 + //ctx.body = mybook;
16 + } catch (e) {
17 + ctx.throw(500, e);
18 + }
19 + //const user = await User.findById(mybook.author).exec();
20 + //ctx.body = mybook;
21 + console.log('책 정보 하나! 얻기 성공');
22 + ctx.status = 200;
23 +});
24 +//mybook=page,title
25 +//author_name = author name
26 +book.post('/',checkLoggedIn,async (ctx) => {
27 +
28 + const {
29 + title,
30 + author,
31 + contents,
32 + cover,
33 + hashtag,
34 + } = ctx.request.body;
35 +
36 + const schema = Joi.object().keys({
37 + title: Joi.string().required(),
38 + author: Joi.string(),
39 + contents: Joi.string().allow(null, ''),
40 + hashtag: Joi.string().allow(null, ''),
41 + cover: Joi.allow(null, ''),
42 + });
43 + try {
44 + await schema.validateAsync(ctx.request.body);
45 + } catch (err) {
46 + console.log('add book validaton' + err);
47 + ctx.status = 400;
48 + return;
49 + }
50 + ctx.request.body.author = ctx.state.user;
51 + const book = new Book(ctx.request.body);
52 +
53 + try {
54 + book.save(async (err) => {
55 + if (err) throw err;
56 + const user = await User.findById(ctx.state.user._id).exec();
57 + console.log(book._id);
58 + ctx.redirect('/books');
59 + });
60 + } catch (e) {
61 + ctx.throw(500, e);
62 + }
63 + console.log('저장 성공!');
64 + ctx.status = 200;
65 +});
66 +// add book
67 +// redirect posts (create)
68 +
69 +book.patch('/:id', checkLoggedIn, async (ctx) => {
70 + const file = ctx.request.files;
71 + if(ctx.request.body.cover != undefined) // when user add a new pet image
72 + ctx.request.body.cover = fs.readFileSync(file.image.path);
73 + else{
74 + ctx.request.body.cover = ""
75 + }
76 + var book = ctx.request.body; //require books's _id
77 + try {
78 + const mybook = await Book.findOne({ _id: ctx.params.id });
79 + if(ctx.state.user._id == mybook.author){//작성한 사람이 맞을 때만
80 + await mybook.updateB(book);
81 + await ctx.redirect('/api/page/detail/'+ctx.params.id);
82 + ctx.status = 200;}
83 + else{
84 + console.log('작성자가 아니다. ');
85 + ctx.status = 400;
86 + }
87 + }
88 + catch (e) {
89 + ctx.throw(500, e);
90 +
91 + ctx.status = 400;
92 + ctx.body = {
93 + message: "작성자가 아닙니다. " }
94 + }
95 +});// modify book information
96 +//update book rediret:"/books/"+ctx.params.id
97 +//book.id params
98 +book.delete('/:id',checkLoggedIn,async (ctx) => {
99 + let bookid =ctx.params.id;
100 + try {
101 +
102 + //var foundB = await Book.findById(bookid).exec();
103 + //북작가에게서 책 정보 지우기
104 + var author = await User.findById(bookid);//북 작가.
105 + console.log(author);
106 + console.log(author.books);
107 + await User.delBook(ctx.state.user.email,bookid);
108 + //book에 있던 페이지 다 지우기
109 + await Page.deleteMany({"pages":{$in:b.pages}});
110 +
111 +
112 + //최종 book지우기
113 + var b = await Book.deleteOne({_id:bookid});
114 + } catch (e) {
115 + if(e.name === 'CastError') {
116 + ctx.status = 400;
117 + return;
118 + }
119 + }
120 + console.log('delete success');
121 + ctx.body = {
122 + message: "Delete"
123 + }
124 +}); // delete book
125 +// params.id
126 +//redirect('/books')<-index
127 +book.get('/',async (ctx) => {
128 + try {
129 + const books = await Book.find({}).sort({createDate: -1}).exec();
130 + ctx.render('books/index', {books:books});
131 + } catch (e) {
132 + return ctx.throw(500, e);
133 + }
134 + }
135 +);
136 +book.get('/search', async (ctx) => {
137 + const {filter, renewal} = ctx.query
138 +
139 + if(filter === "조회순") { //조회순
140 + try {
141 + const books = await Book.find().sort({'views': -1}).skip(parseInt(renewal)*10).limit(10)
142 + let result = await bookInfo(books);
143 + ctx.status = 200;
144 + ctx.body = result;
145 + } catch (e) {
146 + ctx.throw(500, e);
147 + }
148 + }
149 +});
150 +
151 +book.get('/new', async (ctx) => {
152 + ctx.render('books/new');
153 + });
154 +module.exports = book;
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require("@koa/router");
2 +const checkLoggedIn = require("../../src/lib/checkLoggedIn");
3 +// const pageCtrl = require("./page.ctrl");
4 +
5 +const page = new Router();
6 +const Page = require("../models/page");
7 +//const index = require('../../../src/index');
8 +//const render = require('koa-ejs');
9 +//Page api
10 +
11 +/*
12 +page.get('/',pageCtrl.getPage); // show a list of user's pages
13 +page.post('/',checkLoggedIn,pageCtrl.addPage); // add page
14 +page.patch('/', checkLoggedIn,pageCtrl.updatePage); // modify page information
15 +page.delete('/',checkLoggedIn,pageCtrl.deletePage); // delete book
16 +
17 +page.get('/search', pageCtrl.search); // /search?title=search_query&petType=petType
18 +//page.post('/search/filter', pageCtrl.searchFilter); //아직 구현 안함
19 +page.post('/:id', pageCtrl.detailPage); // detail recipe page
20 +
21 +//page.get('/recipe/slide', pageCtrl.slidRecipe); // 5 recommended videos in main page
22 +
23 +// /recipe/scroll?filter=filter_query&renewal=count (filter_query: 추천순 or 조회순)
24 +page.get('/recipe/scroll', pageCtrl.scrollPage); // video list sorted by 추천순 or 조회순 in main page
25 +*/
26 +
27 +page.get('/tset', async (ctx) => {
28 +
29 + console.log('testtest');
30 +});
31 +
32 + page.get('/', async (ctx) => {
33 +
34 + const page = await Page.find({}).sort({createDate:-1}).exec();
35 + console.log(page);
36 + await ctx.render('posts/index', {page});
37 + });
38 +
39 +
40 + page.get('/new', async (ctx) => {
41 + await ctx.render('posts/new');
42 + });
43 +
44 + // create
45 + page.put('/', checkLoggedIn, async (ctx, next) => {
46 + const {
47 + title,
48 + author,
49 + contents,
50 + cover,
51 + hashtag,
52 + } = ctx.request.body;
53 +
54 + const schema = Joi.object().keys({
55 + title: Joi.string().required(),
56 + author: Joi.string(),
57 + contents: Joi.string().allow(null, ''),
58 + hashtag: Joi.string().allow(null, ''),
59 + cover: Joi.allow(null, ''),
60 + });
61 + try {
62 + await schema.validateAsync(ctx.request.body);
63 + } catch (err) {
64 + console.log('add book validaton' + err);
65 + ctx.status = 400;
66 + return;
67 + }
68 + ctx.request.body.author = ctx.state.user;
69 + const book = new Book(ctx.request.body);
70 +
71 + try {
72 + book.save(async (err) => {
73 + if (err) throw err;
74 + const user = await User.findById(ctx.state.user._id).exec();
75 + console.log(book._id);
76 + ctx.redirect('/page');
77 + });
78 + } catch (e) {
79 + ctx.throw(500, e);
80 + }
81 + console.log('저장 성공!');
82 + ctx.status = 200;
83 +
84 + ctx.redirect('/page');
85 + });
86 +
87 +
88 +
89 + // show
90 + page.get('/:id', async (ctx, next) => {
91 + try{
92 + var id = ctx.params.id;
93 + const page = await Page.findById(id).exec();
94 + await ctx.render('posts/show', {page:page});
95 + ctx.status = 200;
96 + }catch(e){
97 + ctx.throw(500,e);
98 + }
99 +
100 +
101 + });
102 +
103 +
104 +// update
105 +page.patch('/:id',checkLoggedIn, async (ctx, next) => {
106 + const id = ctx.params.id;
107 + if(ctx.request.files != undefined) // when user add a new pet image
108 + ctx.request.body.image = fs.readFileSync(file.image.path);
109 + else
110 + ctx.request.body.image = ""
111 + ctx.body.updateDate = Date.now();
112 + var page = ctx.request.body;
113 + try {
114 + const mypage = await Page.findOne({ _id: id });//require page's _id
115 + if(ctx.state.user._id == mypage.author){
116 + mypage.updateP(page);
117 +
118 + console.log(mypage);
119 + ctx.redirect("/page/"+id);
120 + ctx.status = 200;}
121 +
122 + } catch (e) {
123 + ctx.throw(500, e);
124 + ctx.body = {
125 + message: "작성자가 아닙니다. " }
126 + }
127 + });
128 +
129 + // destroy
130 + page.delete('/:id', async (ctx, next) => {
131 + try{
132 + await Page.deleteOne({_id:ctx.params.id})
133 + ctx.redirect('/page');
134 + }catch(e){
135 + ctx.throw(500, e);
136 + }
137 + });
138 +
139 +
140 +
141 +//page.post('/postinfo', pageCtrl.uploadInfo);
142 +//page.get('/info',pageCtrl.getbyurl);//url로 recipe정보 가져오기 (flutter 내 레시피에서 쓰임.)
143 +module.exports = page;
1 +require('dotenv').config()
2 +const Router = require("@koa/router");
3 +//const authCtrl = require("auth.ctrl");
4 +const checkLoggedIn = require("../../src/lib/checkLoggedIn");
5 +//var passport = require('passport');
6 +const User = require("../models/user");
7 +//var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
8 +const auth = new Router();
9 +
10 +// 회원가입- 로컬 이메일 인증번호 발송 POST auth/signup/email/demand
11 +// 회원가입 이메일 인증번호 확인 POST auth/signup/email/verify
12 +// 로그인 "소셜 로그인
13 +// (페이스북 구글 네이버 카카오)" POST auth/signin/social
14 +// 회원정보갱신 설문이나 설정에서 개인정보 바꾸면 적용 PATCH auth/update/pet
15 +// 회원정보갱신 설문이나 설정에서 개인정보 바꾸면 적용 PATCH auth/update/user
16 +/*
17 +auth.get('/userlist', authCtrl.userlist);
18 +auth.get('/test', authCtrl.test);
19 +auth.post('/signup', authCtrl.signupLocal);
20 +auth.post('/signin', authCtrl.signinLocal);
21 +auth.get('/signout',checkLoggedIn, authCtrl.signout);
22 +auth.get('/check', authCtrl.check2);
23 +auth.delete('/user',checkLoggedIn,authCtrl.Withdrawal); // 회원 탈퇴
24 +auth.post('/validate', authCtrl.exists);
25 +auth.post('/checkpassword', authCtrl.checkPassword);
26 +auth.get('/book',checkLoggedIn,authCtrl.getUserBook);
27 +auth.get('/user', checkLoggedIn,authCtrl.userinfo); // show user information
28 +auth.patch('/user', checkLoggedIn, authCtrl.updateUser); // modify user information
29 +auth.patch('/user/password', authCtrl.changePassword); // change password
30 +auth.post('/find/password', authCtrl.findPassword); // 비밀번호 찾기
31 +auth.get('/favorite',checkLoggedIn, authCtrl.showFavorite); // show a list of user's favorites
32 +auth.post('/favorite',checkLoggedIn, authCtrl.addFavorite); // add favorite
33 +auth.delete('/favorite',checkLoggedIn, authCtrl.delFavorite); // delete favorite
34 +
35 +auth.post('/find/password', authCtrl.findPassword); // 비밀번호 찾기
36 +*/
37 +auth.get('/new', async (ctx) => {
38 + ctx.render('users/new');
39 + });
40 +
41 +auth.get('/test', async (ctx) => {
42 + ctx.render('users/login');
43 + });
44 +
45 +
46 +// show
47 +/*
48 +auth.get('/:id', async (ctx) => {
49 + try{
50 + const user = await User.findOne({_id:ctx.params._id});
51 + ctx.render('users/show', {user});
52 + }catch(e){
53 + ctx.throw(500, e);
54 + console.log(e); }
55 +
56 + });
57 +*/
58 +auth.get('/login', async (ctx) => {
59 +
60 + await ctx.render('home/login')
61 +});
62 +
63 +
64 +
65 + // edit
66 + auth.get('/:id/edit', async (ctx) => {
67 + const user = await User.find({email:ctx.params.email}).exec();
68 + ctx.render('users/edit', {user:user});
69 + });
70 +
71 +
72 + auth.post('/login',async(ctx) =>{
73 + const { email, password } = ctx.request.body;
74 + const errors ='';
75 + //handle error
76 + if (!email || !password) {
77 + ctx.status = 401; //Unauthorized
78 + errors= '비밀번호, 이메일 중 하나가 틀렸습니다. '
79 + return;
80 + }
81 + try {
82 + //const user = User.findOne({ email: email });
83 + const user = await User.findByEmail(email);
84 + //계정없으면 에러처리
85 + console.log(user);
86 + if (!user) {
87 + ctx.status = 401;
88 + return;
89 + }
90 +
91 + const valid = await user.checkPassword(password);
92 + if (!valid) {
93 + ctx.status = 401;
94 + return;
95 + }
96 + ctx.body = await user.serialize();
97 + const token = user.generateToken();
98 + ctx.cookies.set('access_token', token, {
99 + maxAge: 1000 * 60 * 60 * 24 * 7, // 7일
100 + httpOnly: false,
101 + });
102 + ctx.status = 200;
103 + ctx.redirect('/page');
104 + console.log('토큰나옴, 로그인');
105 + } catch (e) {
106 + ctx.throw(500, e);
107 + ctx.redirect('/')
108 + }
109 +
110 + });
111 +
112 + auth.post('/signup',async(ctx)=>{const { email, password, address } = ctx.request.body;
113 + console.log(ctx.request.body);
114 + const schema = Joi.object().keys({
115 + email: Joi.string().min(3).required(),
116 + password: Joi.string().required(),
117 + phone: Joi.string().allow(null, ''),
118 + nickname:Joi.string().allow(null, '')
119 + });
120 +
121 + //검증 결과
122 + try {
123 + const value = await schema.validateAsync(ctx.request.body);
124 + } catch (err) {
125 + console.log(err);
126 + ctx.status = 400;
127 + return;
128 + }
129 +
130 + try {
131 + // email 이미 존재하는지 확인
132 + const exists = await User.findByEmail(email);
133 + if (exists) {
134 + ctx.status = 409; // Conflict
135 + return;
136 + }
137 +
138 + let account = null;
139 + try {
140 + account = await User.localRegister(ctx.request.body);
141 + } catch (e) {
142 + ctx.throw(500, e);
143 + }
144 +
145 + let token = null;
146 + try {
147 + token = await account.generateToken();
148 + console.log('token ok');
149 + } catch (e) {
150 + ctx.throw(500, e);
151 + }
152 + ctx.cookies.set('access_token', token, { maxAge: 1000 * 60 * 60 * 24 * 7 ,httpOnly: true,});
153 + console.log('set cookie ok');
154 +
155 + // 응답할 데이터에서 hashedPassword 필드 제거
156 + ctx.status = 200;
157 + await ctx.render('users/new');
158 + } catch (e) {
159 + ctx.throw(500, e);
160 + }});
161 +
162 + // update // 2
163 + auth.post('/:id', async (ctx) => {
164 + await User.findOne({username:ctx.params.username}).select('password').exec();
165 +
166 +
167 + // update user object
168 + user.originalPassword = user.password;
169 + user.password = ctx.body.newPassword? ctx.body.newPassword : user.password; // 2-3
170 + for(var p in ctx.body) // 2-4
171 + user[p] = ctx.body[p];
172 +
173 +
174 + // save updated user
175 + await user.save();
176 +
177 + ctx.redirect('/users/'+user.nickname);
178 + });
179 +
180 +
181 + // destroy
182 + auth.get('/logout', async (ctx) => {
183 + ctx.cookies.set('access_token');
184 + ctx.status = 204;
185 + ctx.redirect('/');
186 + });
187 +
188 +
189 +module.exports = auth;
190 +
191 +function checkPermission(ctx, next){
192 + User.findOne({username:ctx.params.username}, function(err, user){
193 + if(err) return res.json(err);
194 + if(user.id != req.user.id) return util.noPermission(ctx);
195 +
196 + next();
197 + });
198 + }
...\ No newline at end of file ...\ No newline at end of file
1 +var util = {};
2 +
3 +util.parseError = function(errors){
4 + var parsed = {};
5 + if(errors.name == 'ValidationError'){
6 + for(var name in errors.errors){
7 + var validationError = errors.errors[name];
8 + parsed[name] = { message:validationError.message };
9 + }
10 + }
11 + else if(errors.code == '11000' && errors.errmsg.indexOf('username') > 0) {
12 + parsed.username = { message:'This username already exists!' };
13 + }
14 + else {
15 + parsed.unhandled = JSON.stringify(errors);
16 + }
17 + return parsed;
18 +}
19 +
20 +util.isLoggedin = function(req, res, next){
21 + if(req.isAuthenticated()){
22 + next();
23 + }
24 + else {
25 + req.flash('errors', {login:'Please login first'});
26 + res.redirect('/login');
27 + }
28 +}
29 +
30 +util.noPermission = function(ctx){
31 + ctx.request.flash('errors', {login:"You don't have permission"});
32 + ctx.request.logout();
33 + ctx.request.redirect('/login');
34 +}
35 +
36 +module.exports = util;
...\ No newline at end of file ...\ No newline at end of file
1 +<head>
2 + <%- include('../partials/head') %>
3 + </head>
4 + <body>
5 + <%- include('../partials/nav') %>
6 +
7 + <div class="container mb-3">
8 +
9 + <nav aria-label="breadcrumb">
10 + <ol class="breadcrumb p-1 pl-2 pr-2">
11 + <li class="breadcrumb-item"><a href="/">Home</a></li>
12 + <li class="breadcrumb-item"><a href="/api/page">Page</a></li>
13 + <li class="breadcrumb-item"><a href="/api/book/<%= book._id %>"><%= book.title %></a></li>
14 + <li class="breadcrumb-item active" aria-current="page">Edit Post</li>
15 + </ol>
16 + </nav>
17 +
18 + <form action="/api/book/<%= book._id %>?_method=patch" method="patch">
19 +
20 + <div class="form-group">
21 + <label for="title">Title</label>
22 + <input type="text" id="title" name="title" value="<%= book.title %>" class="form-control">
23 + </div>
24 +
25 + <div class="form-group">
26 + <label for="body">Body</label>
27 + <textarea id="body" name="body" rows="5" class="form-control"><%= book.contents %></textarea>
28 + </div>
29 +
30 + <div>
31 + <a class="btn btn-primary" href="/api/book/<%= book._id %>">Back</a>
32 + <button type="submit" class="btn btn-primary">Submit</button>
33 + </div>
34 +
35 + </form>
36 +
37 + </div>
38 + </body>
39 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +<!-- views/books/index.ejs -->
2 +<!DOCTYPE html>
3 +<html>
4 + <head>
5 + <%- include('../partials/head') %>
6 + </head>
7 + <body>
8 + <%- include('../partials/nav') %>
9 +
10 + <div class="container mb-3">
11 +
12 + <h2 class="mb-3">Board</h2>
13 +
14 + <table class="board-table table table-sm border-bottom">
15 +
16 + <thead class="thead-light">
17 + <tr>
18 + <th scope="col">Title</th>
19 + <th scope="col" class="date">Date</th>
20 + </tr>
21 + </thead>
22 +
23 + <tbody>
24 + <% if(books == null || books.length == 0){ %>
25 + <tr>
26 + <td colspan=2> There is no data to show :( </td>
27 + </tr>
28 + <% } %>
29 + <% books.forEach(function(book) { %>
30 + <tr>
31 + <td>
32 + <a href="/api/book/<%= book._id %>"><div class="ellipsis"><%= book.title %></div></a>
33 + </td>
34 + <td class="date">
35 + <span data-date="<%= book.createDate %>"></span> <!-- 1 -->
36 + </td>
37 + </tr>
38 + <% }) %>
39 + </tbody>
40 +
41 + </table>
42 +
43 + <div>
44 + <a class="btn btn-primary" href="/api/book/new">New</a>
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 + <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="/api/page">Page</a></li>
15 + <li class="breadcrumb-item active" aria-current="page">New Book</li>
16 + </ol>
17 + </nav>
18 +
19 + <form action="/api/book" 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="/api/book">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="/api/page">Page</a></li>
15 + <li class="breadcrumb-item active" aria-current="page"><%= book.title %></li>
16 + </ol>
17 + </nav>
18 +
19 + <div class="card">
20 + <h5 class="card-header p-2"><%= book.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"><%= book.contents %></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="<%= book.createDate %>"></span></div> <!-- 2 -->
30 + <% if(book.createDate) { %>
31 + <div><span>Updated</span> : <span data-date-time="<%= book.updateDate %>"></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="/api/book">Back</a>
41 + <a class="btn btn-primary" href="/api/book/<%= book._id %>/edit">Edit</a>
42 + <form action="/api/book/<%= book._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 + <h2 class="mb-3">About</h2>
12 +
13 + <P>이 사이트는 Like Project 임시 페이지 입니다. </p>
14 +
15 + </div>
16 + </body>
17 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +<!DOCTYPE html>
2 +<html>
3 +<head>
4 + <meta charset="utf-8">
5 + <meta http-equiv="x-ua-compatible" content="ie=edge">
6 + <title>Login Example</title>
7 + <meta name="description" content="">
8 + <meta name="viewport" content="width=device-width, initial-scale=1">
9 +
10 + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
11 + <link rel="stylesheet" href="../stylesheets/style.css">
12 +
13 + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
14 + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
15 +</head>
16 +<body>
17 + <div class="container">
18 + <div class="wrapper">
19 + <form action="/auth/login" method="post" name="Login_Form" class="form-signin">
20 + <h3 class="form-signin-heading">Welcome! Please Sign In</h3>
21 +
22 + <input type="text" class="form-control" name="email" placeholder="Email Address" required="" autofocus="" />
23 + <input type="password" class="form-control" name="password" placeholder="Password" required=""/>
24 +
25 + <button class="btn btn-lg btn-primary btn-block" name="Submit" value="Login" type="Submit">Login</button>
26 + </form>
27 + </div>
28 + </div>
29 +</body>
30 +</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 + <div class="jumbotron">
12 + <h1>My Website</h1>
13 + <P>제 웹사이트를 방문해 주셔서 감사합니다!</p>
14 + </div>
15 +
16 + </div>
17 + </body>
18 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +<!-- views/partials/head.ejs -->
2 +
3 +<meta name="viewport" content="width=device-width,initial-scale=1">
4 +
5 +<!-- jquery & bootstrap -->
6 +<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
7 +<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
8 +<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
9 +<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
10 +
11 +<!-- web font --> <!-- 1 -->
12 +<link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet">
13 +
14 +<!-- my css -->
15 +<script src="/src/public/js/script.js"></script>
16 +
17 +<title>My Website</title>
...\ No newline at end of file ...\ No newline at end of file
1 +<nav class="navbar navbar-expand-sm navbar-light bg-light mb-3">
2 + <div class="container">
3 + <div class="navbar-brand"></a>Like</div>
4 + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
5 + <span class="navbar-toggler-icon"></span>
6 + </button>
7 + <div class="collapse navbar-collapse" id="navbarSupportedContent">
8 + <ul class="navbar-nav">
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>
11 + <li class="nav-item"><a href="/book" class="nav-link">Book</a></li>
12 + <li class="nav-item"><a href="/page" class="nav-link">Page</a></li>
13 + </ul>
14 + <ul class="navbar-nav ml-auto">
15 +
16 + <li class="nav-item"><a href="/auth/new" class="nav-link">Sign Up</a></li>
17 + <li class="nav-item"><a href="/auth/login" class="nav-link">Login</a></li>
18 +
19 + </ul>
20 + </div>
21 + </div>
22 + </nav>
...\ 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="/book">Book</a></li>
15 + <li class="breadcrumb-item"><a href="/page/<%= page._id %>"><%= page.title %></a></li>
16 + <li class="breadcrumb-item active" aria-current="page">Edit Post</li>
17 + </ol>
18 + </nav>
19 +
20 + <form action="/page<%= page._id %>?_method=put" method="patch">
21 +
22 + <div class="form-group">
23 + <label for="title">Title</label>
24 + <input type="text" id="title" name="title" value="<%= page.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"><%= page.contents %></textarea>
30 + </div>
31 +
32 + <div>
33 + <a class="btn btn-primary" href="/page/<%= page._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 +<!-- views/posts/index.ejs -->
2 +
3 +<!DOCTYPE html>
4 +<html>
5 + <head>
6 + <%- include('../partials/head') %>
7 + </head>
8 + <body>
9 + <%- include('../partials/nav') %>
10 +
11 + <div class="container mb-3">
12 +
13 + <h2 class="mb-3">Board</h2>
14 +
15 + <table class="board-table table table-sm border-bottom">
16 +
17 + <thead class="thead-light">
18 + <tr>
19 + <th scope="col">Title</th>
20 + <th scope="col" class="date">Date</th>
21 + </tr>
22 + </thead>
23 +
24 + <tbody>
25 + <% if(page == null || page.length == 0){ %>
26 + <tr>
27 + <td colspan=2> There is no data to show :( </td>
28 + </tr>
29 + <% } %>
30 + <% page.forEach(function(page) { %>
31 + <tr>
32 + <td>
33 + <a href="/page/<%= page._id %>"><div class="ellipsis"><%= page.title %></div></a>
34 + </td>
35 + <td class="date">
36 + <span data-date="<%= page.createDate %>"></span> <!-- 1 -->
37 + </td>
38 + </tr>
39 + <% }) %>
40 + </tbody>
41 +
42 + </table>
43 +
44 + <div>
45 + <a class="btn btn-primary" href="/page/new">New</a>
46 + </div>
47 +
48 + </div>
49 + </body>
50 +</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="/page">Board</a></li>
15 + <li class="breadcrumb-item active" aria-current="page">New Post</li>
16 + </ol>
17 + </nav>
18 +
19 + <form action="/page" 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="/page">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 +
2 +<!DOCTYPE html>
3 +<html>
4 + <head>
5 + <%- include('../partials/head') %>
6 + </head>
7 + <body>
8 +
9 + <%- include('../partials/nav') %>
10 +
11 + <div class="container mb-3">
12 +
13 + <nav aria-label="breadcrumb">
14 + <ol class="breadcrumb p-1 pl-2 pr-2">
15 + <li class="breadcrumb-item"><a href="/">Home</a></li>
16 + <li class="breadcrumb-item"><a href="/page">Page</a></li>
17 + <li class="breadcrumb-item active" aria-current="page"><%= page.title %></li>
18 + </ol>
19 + </nav>
20 +
21 + <div class="card">
22 + <h5 class="card-header p-2"><%= page.title %></h5>
23 + <div class="row"> <!-- 1 -->
24 +
25 + <div class="col-md-7 col-lg-8 col-xl-9 order-sm-2 order-md-1"> <!-- 1 -->
26 + <div class="post-body p-2"><%= page.contents %></div>
27 + </div>
28 +
29 + <div class="col-md-5 col-lg-4 col-xl-3 order-sm-1 order-md-2"> <!-- 1 -->
30 + <div class="post-info card m-2 p-2">
31 + <div><span>Created</span> : <span data-date-time="<%= page.createDate %>"></span></div> <!-- 2 -->
32 + <% if(page.updateDate) { %>
33 + <div><span>Updated</span> : <span data-date-time="<%= page.updateDate %>"></span></div> <!-- 2 -->
34 + <% } %>
35 + </div>
36 + </div>
37 +
38 + </div>
39 + </div>
40 +
41 + <div class="mt-3">
42 + <a class="btn btn-primary" href="/page">Back</a>
43 + <a class="btn btn-primary" href="/page/<%= page._id %>/edit">Edit</a>
44 + <form action="/page/<%= page._id %>/_method=delete" method="post" class="d-inline">
45 + <a class="btn btn-primary" href="#" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a>
46 + </form>
47 + </div>
48 +
49 + </div>
50 + </body>
51 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +<!DOCTYPE html>
3 +<html>
4 + <head>
5 + <%- include('../partials/head') %>
6 + </head>
7 + <body>
8 + <%- include('../partials/nav') %>
9 +
10 + <div class="container mb-3">
11 +
12 + <h3 class="mb-3">Edit User</h3>
13 +
14 + <form action="/auth/users/<%= user.username %>?_method=put" method="post">
15 +
16 + <div class="form-group row">
17 + <label for="currentPassword" class="col-sm-3 col-form-label">Current Password*</label>
18 + <div class="col-sm-9 col-sm-offset-3">
19 + <input type="password" id="currentPassword" name="currentPassword" value="" class="form-control">
20 + </div>
21 + </div>
22 +
23 + <hr></hr>
24 +
25 + <div class="form-group row">
26 + <label for="username" class="col-sm-3 col-form-label">Username*</label>
27 + <div class="col-sm-9">
28 + <input type="text" id="username" name="username" value="<%= user.nickname %>" class="form-control">
29 + </div>
30 + </div>
31 +
32 + <div class="form-group row">
33 + <label for="name" class="col-sm-3 col-form-label">Name*</label>
34 + <div class="col-sm-9">
35 + <input type="text" id="name" name="name" value="<%= user.email %>" class="form-control">
36 + </div>
37 + </div>
38 +
39 + <div class="form-group row">
40 + <label for="email" class="col-sm-3 col-form-label">Email</label>
41 + <div class="col-sm-9">
42 + <input type="text" id="email" name="email" value="<%= user.email %>" class="form-control">
43 + </div>
44 + </div>
45 +
46 + <div class="form-group row">
47 + <label for="newPassword" class="col-sm-3 col-form-label">New Password</label>
48 + <div class="col-sm-9 col-sm-offset-3">
49 + <input type="password" id="newPassword" name="newPassword" value="" class="form-control">
50 + </div>
51 + </div>
52 +
53 + <div class="form-group row">
54 + <label for="passwordConfirmation" class="col-sm-3 col-form-label">Password Confirmation</label>
55 + <div class="col-sm-9 col-sm-offset-3">
56 + <input type="password" id="passwordConfirmation" name="passwordConfirmation" value="" class="form-control">
57 + </div>
58 + </div>
59 +
60 + <p>
61 + <small>*Required</small>
62 + </p>
63 +
64 + <div class="buttons">
65 + <a class="btn btn-primary" href="/auth/users/<%= user.username %>">Back</a>
66 + <button type="submit" class="btn btn-primary">Submit</button>
67 + </div>
68 +
69 + </form>
70 +
71 + </div>
72 + </body>
73 +</html>
...\ No newline at end of file ...\ No newline at end of file
File mode changed
1 +
2 +
3 +<!DOCTYPE html>
4 +<html>
5 + <head>
6 + <%- include('../partials/head') %>
7 + </head>
8 + <body>
9 + <%- include('../partials/nav') %>
10 +
11 + <div class="container mb-3">
12 +
13 + <h3 class="contentBoxTop mb-3">New User</h3>
14 +
15 + <form action="/auth/signin" method="post">
16 +
17 + <div class="form-group row">
18 + <label for="nickname" class="col-sm-3 col-form-label">Username*</label>
19 + <div class="col-sm-9">
20 + <input type="text" id="nickname" name="nickname" value="" class="form-control">
21 + </div>
22 + </div>
23 + <div class="form-group row">
24 + <label for="email" class="col-sm-3 col-form-label">Email</label>
25 + <div class="col-sm-9">
26 + <input type="text" id="email" name="email" value="" class="form-control">
27 + </div>
28 + </div>
29 + <div class="form-group row">
30 + <label for="password" class="col-sm-3 col-form-label">Password*</label>
31 + <div class="col-sm-9">
32 + <input type="password" id="password" name="password" value="" class="form-control">
33 + </div>
34 + </div>
35 + <div class="form-group row">
36 + <label for="passwordConfirmation" class="col-sm-3 col-form-label">Password Confirmation*</label>
37 + <div class="col-sm-9 col-sm-offset-3">
38 + <input type="password" id="passwordConfirmation" name="passwordConfirmation" value="" class="form-control">
39 + </div>
40 + </div>
41 + <p>
42 + <small>*Required</small>
43 + </p>
44 +
45 + <div class="form-group">
46 + <button type="submit" class="btn btn-primary">Submit</button>
47 + </div>
48 + </form>
49 +
50 + </div>
51 + </body>
52 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +
3 +<!DOCTYPE html>
4 +<html>
5 + <head>
6 + <%- include('../partials/head') %>
7 + </head>
8 + <body>
9 + <%- include('../partials/nav') %>
10 +
11 + <div class="container mb-3">
12 +
13 + <h3 class="contentBoxTop">usefname</h3>
14 +
15 + <form class="user-form" action="/users" method="post">
16 + <fieldset disabled>
17 +
18 + <div class="form-group row">
19 + <label for="email" class="col-sm-3 col-form-label">Email</label>
20 + <div class="col-sm-9">
21 + <input class="form-control" type="text" id="email" name="email" value="<%= 9 %>">
22 + </div>
23 + </div>
24 + </fieldset>
25 + </form>
26 +
27 + <div>
28 + <a class="btn btn-primary" href="/auth/users">Back</a>
29 + <a class="btn btn-primary" href="/auth/users/<%= user.email %>/edit">Edit</a>
30 + <form action="/users/<%= user.email %>?_method=delete" method="post" class="d-inline">
31 + <a class="btn btn-primary" href="javascript:void(0)" onclick="confirm('Do you want to delete this?')?this.parentElement.submit():null;">Delete</a>
32 + </form>
33 + </div>
34 +
35 + </div>
36 + </body>
37 +</html>
...\ No newline at end of file ...\ No newline at end of file