Showing
41 changed files
with
1937 additions
and
121 deletions
... | @@ -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 | ... | ... |
firebase-debug.log
0 → 100644
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 | ... | ... |
... | @@ -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 | ... | ... |
src/public/css/master.css
0 → 100644
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 | +} |
src/public/js/script.js
0 → 100644
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 |
src/routes/book.js
0 → 100644
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 |
src/routes/page.js
0 → 100644
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; |
src/routes/user.js
0 → 100644
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 |
src/util.js
0 → 100644
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 |
src/views/books/edit.ejs
0 → 100644
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 |
src/views/books/index.ejs
0 → 100644
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 |
src/views/books/new.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <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 |
src/views/books/show.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <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 |
src/views/home/about.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <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 |
src/views/home/login.ejs
0 → 100644
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 |
src/views/home/welcome.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <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 |
src/views/partials/head.ejs
0 → 100644
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 |
src/views/partials/nav.ejs
0 → 100644
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 |
src/views/posts/edit.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <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 |
src/views/posts/index.ejs
0 → 100644
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 |
src/views/posts/new.ejs
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <%- include('../partials/head') %> | ||
5 | + </head> | ||
6 | + <body> | ||
7 | + <%- include('../partials/nav') %> | ||
8 | + | ||
9 | + <div class="container mb-3"> | ||
10 | + | ||
11 | + <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 |
src/views/posts/show.ejs
0 → 100644
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 |
src/views/users/edit.ejs
0 → 100644
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 |
src/views/users/index.ejs
0 → 100644
File mode changed
src/views/users/new.ejs
0 → 100644
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 |
src/views/users/show.ejs
0 → 100644
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 |
-
Please register or login to post a comment