Flare-k

[Add] Express Flash

...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 📺 [KhuTube](http://www.khutube.tk:4000/) 11 📺 [KhuTube](http://www.khutube.tk:4000/)
12 12
13 위 주소는 실제 EC2 서버에 배포한 주소입니다. (불필요한 업로드는 삼가 주시기 바랍니다.) 13 위 주소는 실제 EC2 서버에 배포한 주소입니다. (불필요한 업로드는 삼가 주시기 바랍니다.)
14 +현재는 사용하지 않는 주소입니다.(2020.08.29 기준)
14 15
15 This is the address that deployed to the actual EC2 server. (Please refrain from uploading unnecessary files.) 16 This is the address that deployed to the actual EC2 server. (Please refrain from uploading unnecessary files.)
16 17
...@@ -108,4 +109,4 @@ I just recommend run both at the same time. ...@@ -108,4 +109,4 @@ I just recommend run both at the same time.
108 ## Contact 109 ## Contact
109 110
110 이용하시다가 궁금한 점이 있으시면 이쪽으로 연락 부탁드립니다. 111 이용하시다가 궁금한 점이 있으시면 이쪽으로 연락 부탁드립니다.
111 -강연욱 - rokkyw@khu.ac.kr 112 +강연욱 - rokkyw@khu.ac.kr / rokkyw@naver.com
......
...@@ -8,6 +8,7 @@ import bodyParser from "body-parser"; ...@@ -8,6 +8,7 @@ import bodyParser from "body-parser";
8 import passport from "passport"; 8 import passport from "passport";
9 import mongoose from "mongoose"; 9 import mongoose from "mongoose";
10 import session from "express-session"; 10 import session from "express-session";
11 +import flash from "express-flash";
11 import MongoStore from "connect-mongo"; 12 import MongoStore from "connect-mongo";
12 import { localsMiddleware } from "./middlewares"; 13 import { localsMiddleware } from "./middlewares";
13 import routes from "./routes"; 14 import routes from "./routes";
...@@ -39,7 +40,7 @@ app.use( ...@@ -39,7 +40,7 @@ app.use(
39 store: new CokieStore({ mongooseConnection: mongoose.connection }), 40 store: new CokieStore({ mongooseConnection: mongoose.connection }),
40 }) 41 })
41 ); 42 );
42 - 43 +app.use(flash());
43 app.use(passport.initialize()); 44 app.use(passport.initialize());
44 app.use(passport.session()); 45 app.use(passport.session());
45 46
......
1 +@keyframes flashAnimation {
2 + 0% {
3 + transform: translateY(-70px);
4 + }
5 + 5% {
6 + transform: translateY(0px);
7 + }
8 + 95% {
9 + transform: translateY(0px);
10 + }
11 + 100% {
12 + transform: translateY(-70px);
13 + }
14 +}
15 +
16 +.flash-message__container {
17 + position: fixed;
18 + top: 0;
19 + left: 0;
20 + width: 100%;
21 + padding: 20px;
22 + text-align: center;
23 + animation: flashAnimation 5s ease-in-out forwards;
24 + &.error {
25 + background-color: #e74c3c;
26 + }
27 + &.success {
28 + background-color: #2ecc71;
29 + }
30 + &.info {
31 + background-color: #f1c40f;
32 + }
33 + .flash-message__text {
34 + color: white;
35 + font-size: 14px;
36 + }
37 +}
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
10 @import "partials/videoBlock.scss"; 10 @import "partials/videoBlock.scss";
11 @import "partials/videoPlayer.scss"; 11 @import "partials/videoPlayer.scss";
12 @import "partials/videoRecorder.scss"; 12 @import "partials/videoRecorder.scss";
13 +@import "partials/messages.scss";
13 14
14 @import "pages/home.scss"; 15 @import "pages/home.scss";
15 @import "pages/videoDetail.scss"; 16 @import "pages/videoDetail.scss";
......
...@@ -11,6 +11,7 @@ export const postJoin = async (req, res, next) => { ...@@ -11,6 +11,7 @@ export const postJoin = async (req, res, next) => {
11 body: { name, email, password, password2 }, 11 body: { name, email, password, password2 },
12 } = req; 12 } = req;
13 if (password !== password2) { 13 if (password !== password2) {
14 + req.flash("error", "Passwords don't match");
14 res.status(400); 15 res.status(400);
15 res.render("join", { pageTitle: "Join" }); 16 res.render("join", { pageTitle: "Join" });
16 } else { 17 } else {
...@@ -37,10 +38,15 @@ export const getLogin = (req, res) => ...@@ -37,10 +38,15 @@ export const getLogin = (req, res) =>
37 export const postLogin = passport.authenticate("local", { 38 export const postLogin = passport.authenticate("local", {
38 failureRedirect: routes.login, 39 failureRedirect: routes.login,
39 successRedirect: routes.home, 40 successRedirect: routes.home,
41 + successFlash: "Welcome",
42 + failureFlash: "Can't log in. Check email and/or password",
40 }); 43 });
41 44
42 // 깃허브 로그인 45 // 깃허브 로그인
43 -export const githubLogin = passport.authenticate("github"); 46 +export const githubLogin = passport.authenticate("github", {
47 + successFlash: "Welcome",
48 + failureFlash: "Can't log in. Check email and/or password",
49 +});
44 // call back 받아오는 함수 50 // call back 받아오는 함수
45 export const githubLoginCallback = async ( 51 export const githubLoginCallback = async (
46 accessToken, 52 accessToken,
...@@ -78,7 +84,10 @@ export const postGithubLogin = (req, res) => { ...@@ -78,7 +84,10 @@ export const postGithubLogin = (req, res) => {
78 res.redirect(routes.home); 84 res.redirect(routes.home);
79 }; 85 };
80 86
81 -export const facebookLogin = passport.authenticate("facebook"); 87 +export const facebookLogin = passport.authenticate("facebook", {
88 + successFlash: "Welcome",
89 + failureFlash: "Can't log in. Check email and/or password",
90 +});
82 91
83 // http://www.passportjs.org/packages/passport-facebook/ 참고하였습니다. 92 // http://www.passportjs.org/packages/passport-facebook/ 참고하였습니다.
84 export const facebookLoginCallback = async (_, __, profile, cb) => { 93 export const facebookLoginCallback = async (_, __, profile, cb) => {
...@@ -92,7 +101,7 @@ export const facebookLoginCallback = async (_, __, profile, cb) => { ...@@ -92,7 +101,7 @@ export const facebookLoginCallback = async (_, __, profile, cb) => {
92 user.avatarUrl = `https://graph.facebook.com/${id}/picture?type=large`; 101 user.avatarUrl = `https://graph.facebook.com/${id}/picture?type=large`;
93 user.save(); 102 user.save();
94 return cb(null, user); 103 return cb(null, user);
95 - } 104 + } // graph API 참조.
96 const newUser = await User.create({ 105 const newUser = await User.create({
97 email, 106 email,
98 name, 107 name,
...@@ -114,10 +123,11 @@ export const postFacebookLogin = (req, res) => { ...@@ -114,10 +123,11 @@ export const postFacebookLogin = (req, res) => {
114 // 즉, 초반에 만들어둔 logout.pug는 삭제해도 좋다. 123 // 즉, 초반에 만들어둔 logout.pug는 삭제해도 좋다.
115 export const logout = (req, res) => { 124 export const logout = (req, res) => {
116 // res.render("logout", { pageTitle: "Logout" }); 125 // res.render("logout", { pageTitle: "Logout" });
126 + req.flash("info", "Logged out, see you later");
117 req.logout(); 127 req.logout();
118 res.redirect(routes.home); 128 res.redirect(routes.home);
119 }; 129 };
120 - 130 +// 미들웨어로 부터 로그인한 user를 받는다.
121 export const getMe = async (req, res) => { 131 export const getMe = async (req, res) => {
122 try { 132 try {
123 const user = await User.findById(req.user.id).populate("videos"); 133 const user = await User.findById(req.user.id).populate("videos");
...@@ -137,6 +147,7 @@ export const userDetail = async (req, res) => { ...@@ -137,6 +147,7 @@ export const userDetail = async (req, res) => {
137 const user = await User.findById(id).populate("videos"); 147 const user = await User.findById(id).populate("videos");
138 res.render("userDetail", { pageTitle: "User Detail", user }); 148 res.render("userDetail", { pageTitle: "User Detail", user });
139 } catch (error) { 149 } catch (error) {
150 + req.flash("error", "User not found");
140 res.redirect(routes.home); 151 res.redirect(routes.home);
141 } 152 }
142 }; 153 };
...@@ -155,8 +166,10 @@ export const postEditProfile = async (req, res) => { ...@@ -155,8 +166,10 @@ export const postEditProfile = async (req, res) => {
155 email, 166 email,
156 avatarUrl: file ? file.location : req.user.avatarUrl, // S3 적용때메 file.path -> file.location 변경(06/24) 167 avatarUrl: file ? file.location : req.user.avatarUrl, // S3 적용때메 file.path -> file.location 변경(06/24)
157 }); 168 });
169 + req.flash("success", "Profile updated");
158 res.redirect(routes.me); 170 res.redirect(routes.me);
159 } catch (error) { 171 } catch (error) {
172 + req.flash("error", "Can't update profile");
160 res.redirect(`/users${routes.editProfile}`); 173 res.redirect(`/users${routes.editProfile}`);
161 } 174 }
162 }; 175 };
...@@ -170,6 +183,7 @@ export const postChangePassword = async (req, res) => { ...@@ -170,6 +183,7 @@ export const postChangePassword = async (req, res) => {
170 } = req; 183 } = req;
171 try { 184 try {
172 if (newPassword !== newPassword1) { 185 if (newPassword !== newPassword1) {
186 + req.flash("error", "Passwords don't match");
173 res.status(400); 187 res.status(400);
174 res.redirect(`/users${routes.changePassword}`); 188 res.redirect(`/users${routes.changePassword}`);
175 return; 189 return;
...@@ -177,6 +191,7 @@ export const postChangePassword = async (req, res) => { ...@@ -177,6 +191,7 @@ export const postChangePassword = async (req, res) => {
177 await req.user.changePassword(oldPassword, newPassword); 191 await req.user.changePassword(oldPassword, newPassword);
178 res.redirect(routes.me); 192 res.redirect(routes.me);
179 } catch (error) { 193 } catch (error) {
194 + req.flash("error", "Can't change password");
180 res.status(400); 195 res.status(400);
181 res.redirect(`/users${routes.changePassword}`); 196 res.redirect(`/users${routes.changePassword}`);
182 } 197 }
......
...@@ -40,11 +40,11 @@ export const postUpload = async (req, res) => { ...@@ -40,11 +40,11 @@ export const postUpload = async (req, res) => {
40 // 이는 pug와 db.js를 확인해야하는 듯 하다. 40 // 이는 pug와 db.js를 확인해야하는 듯 하다.
41 const { 41 const {
42 body: { title, description }, 42 body: { title, description },
43 - file: { location }, // path로 할때는 로컬의 경로. S3는 location 43 + file: { path }, // path로 할때는 로컬의 경로. S3는 location
44 } = req; // file에 path라는 요소가 있다. 44 } = req; // file에 path라는 요소가 있다.
45 45
46 const newVideo = await Video.create({ 46 const newVideo = await Video.create({
47 - fileUrl: location, 47 + fileUrl: path,
48 title, 48 title,
49 description, 49 description,
50 creator: req.user.id, 50 creator: req.user.id,
...@@ -66,6 +66,7 @@ export const videoDetail = async (req, res) => { ...@@ -66,6 +66,7 @@ export const videoDetail = async (req, res) => {
66 const video = await Video.findById(id) 66 const video = await Video.findById(id)
67 .populate("creator") 67 .populate("creator")
68 .populate("comments"); 68 .populate("comments");
69 + console.log(video);
69 res.render("videoDetail", { pageTitle: video.title, video }); 70 res.render("videoDetail", { pageTitle: video.title, video });
70 } catch (error) { 71 } catch (error) {
71 res.redirect(routes.home); 72 res.redirect(routes.home);
......
...@@ -4,7 +4,7 @@ import dotenv from "dotenv"; ...@@ -4,7 +4,7 @@ import dotenv from "dotenv";
4 4
5 dotenv.config(); 5 dotenv.config();
6 6
7 -mongoose.connect(process.env.MONGO_URL_PROD, { 7 +mongoose.connect(process.env.MONGO_URL, {
8 useNewUrlParser: true, 8 useNewUrlParser: true,
9 useFindAndModify: false, 9 useFindAndModify: false,
10 }); 10 });
......
...@@ -12,15 +12,17 @@ const s3 = new aws.S3({ ...@@ -12,15 +12,17 @@ const s3 = new aws.S3({
12 region: "ap-northeast-2", 12 region: "ap-northeast-2",
13 }); 13 });
14 14
15 -// const multerVideo = multer({ dest: "uploads/videos/" }); 15 +const multerVideo = multer({ dest: "uploads/videos/" });
16 +/*
16 const multerVideo = multer({ 17 const multerVideo = multer({
17 storage: multerS3({ 18 storage: multerS3({
18 s3, 19 s3,
19 acl: "public-read", 20 acl: "public-read",
20 bucket: "khutube/video", 21 bucket: "khutube/video",
21 }), 22 }),
22 -}); 23 +});*/
23 -// const multerAvatar = multer({ dest: "uploads/avatars/" }); 24 +const multerAvatar = multer({ dest: "uploads/avatars/" });
25 +/*
24 const multerAvatar = multer({ 26 const multerAvatar = multer({
25 storage: multerS3({ 27 storage: multerS3({
26 s3, 28 s3,
...@@ -28,11 +30,12 @@ const multerAvatar = multer({ ...@@ -28,11 +30,12 @@ const multerAvatar = multer({
28 bucket: "khutube/avatars", 30 bucket: "khutube/avatars",
29 }), 31 }),
30 }); 32 });
31 - 33 +*/
32 export const localsMiddleware = (req, res, next) => { 34 export const localsMiddleware = (req, res, next) => {
33 res.locals.siteName = "my Youtube"; 35 res.locals.siteName = "my Youtube";
34 res.locals.routes = routes; 36 res.locals.routes = routes;
35 res.locals.loggedUser = req.user || null; 37 res.locals.loggedUser = req.user || null;
38 + // console.log(req);
36 next(); 39 next();
37 }; 40 };
38 41
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
31 "css-loader": "^3.5.3", 31 "css-loader": "^3.5.3",
32 "dotenv": "^8.2.0", 32 "dotenv": "^8.2.0",
33 "express": "^4.17.1", 33 "express": "^4.17.1",
34 + "express-flash": "0.0.2",
34 "express-session": "^1.17.1", 35 "express-session": "^1.17.1",
35 "extract-text-webpack-plugin": "^4.0.0-beta.0", 36 "extract-text-webpack-plugin": "^4.0.0-beta.0",
36 "get-blob-duration": "^1.1.1", 37 "get-blob-duration": "^1.1.1",
......
...@@ -501,6 +501,91 @@ input[type="submit"] { ...@@ -501,6 +501,91 @@ input[type="submit"] {
501 width: 100%; 501 width: 100%;
502 margin-bottom: 20px; } 502 margin-bottom: 20px; }
503 503
504 +@-webkit-keyframes flashAnimation {
505 + 0% {
506 + -webkit-transform: translateY(-70px);
507 + transform: translateY(-70px); }
508 + 5% {
509 + -webkit-transform: translateY(0px);
510 + transform: translateY(0px); }
511 + 95% {
512 + -webkit-transform: translateY(0px);
513 + transform: translateY(0px); }
514 + 100% {
515 + -webkit-transform: translateY(-70px);
516 + transform: translateY(-70px); } }
517 +
518 +@-moz-keyframes flashAnimation {
519 + 0% {
520 + -moz-transform: translateY(-70px);
521 + transform: translateY(-70px); }
522 + 5% {
523 + -moz-transform: translateY(0px);
524 + transform: translateY(0px); }
525 + 95% {
526 + -moz-transform: translateY(0px);
527 + transform: translateY(0px); }
528 + 100% {
529 + -moz-transform: translateY(-70px);
530 + transform: translateY(-70px); } }
531 +
532 +@-o-keyframes flashAnimation {
533 + 0% {
534 + -o-transform: translateY(-70px);
535 + transform: translateY(-70px); }
536 + 5% {
537 + -o-transform: translateY(0px);
538 + transform: translateY(0px); }
539 + 95% {
540 + -o-transform: translateY(0px);
541 + transform: translateY(0px); }
542 + 100% {
543 + -o-transform: translateY(-70px);
544 + transform: translateY(-70px); } }
545 +
546 +@keyframes flashAnimation {
547 + 0% {
548 + -webkit-transform: translateY(-70px);
549 + -moz-transform: translateY(-70px);
550 + -o-transform: translateY(-70px);
551 + transform: translateY(-70px); }
552 + 5% {
553 + -webkit-transform: translateY(0px);
554 + -moz-transform: translateY(0px);
555 + -o-transform: translateY(0px);
556 + transform: translateY(0px); }
557 + 95% {
558 + -webkit-transform: translateY(0px);
559 + -moz-transform: translateY(0px);
560 + -o-transform: translateY(0px);
561 + transform: translateY(0px); }
562 + 100% {
563 + -webkit-transform: translateY(-70px);
564 + -moz-transform: translateY(-70px);
565 + -o-transform: translateY(-70px);
566 + transform: translateY(-70px); } }
567 +
568 +.flash-message__container {
569 + position: fixed;
570 + top: 0;
571 + left: 0;
572 + width: 100%;
573 + padding: 20px;
574 + text-align: center;
575 + -webkit-animation: flashAnimation 5s ease-in-out forwards;
576 + -moz-animation: flashAnimation 5s ease-in-out forwards;
577 + -o-animation: flashAnimation 5s ease-in-out forwards;
578 + animation: flashAnimation 5s ease-in-out forwards; }
579 + .flash-message__container.error {
580 + background-color: #e74c3c; }
581 + .flash-message__container.success {
582 + background-color: #2ecc71; }
583 + .flash-message__container.info {
584 + background-color: #f1c40f; }
585 + .flash-message__container .flash-message__text {
586 + color: white;
587 + font-size: 14px; }
588 +
504 .home-videos { 589 .home-videos {
505 display: grid; 590 display: grid;
506 grid-template-columns: repeat(6, minmax(150px, 1fr)); 591 grid-template-columns: repeat(6, minmax(150px, 1fr));
......
1 //공통되는 코드가 필요한 경우div 1 //공통되는 코드가 필요한 경우div
2 - 2 +include ../mixins/message
3 doctype html 3 doctype html
4 html 4 html
5 head 5 head
...@@ -7,6 +7,21 @@ html ...@@ -7,6 +7,21 @@ html
7 title #{pageTitle} | #{siteName} 7 title #{pageTitle} | #{siteName}
8 link(rel="stylesheet", href="/static/style.css") 8 link(rel="stylesheet", href="/static/style.css")
9 body 9 body
10 + if messages.error
11 + +message({
12 + type:'error',
13 + text:messages.error
14 + })
15 + else if messages.info
16 + +message({
17 + type:'info',
18 + text:messages.info
19 + })
20 + else if messages.success
21 + +message({
22 + type:'success',
23 + text:messages.success
24 + })
10 include ../partials/header 25 include ../partials/header
11 main 26 main
12 block content 27 block content
......
1 +mixin message(message={})
2 + .flash-message__container(class=message.type)
3 + span.flash-message__text=message.text
1 mixin videoPlayer(video={}) 1 mixin videoPlayer(video={})
2 .videoPlayer#jsVideoPlayer 2 .videoPlayer#jsVideoPlayer
3 - video(src=video.src) 3 + video(src=`/${video.src}`)
4 .videoPlayer__controls 4 .videoPlayer__controls
5 .videoPlayer__column 5 .videoPlayer__column
6 span#jsVolumeBtn 6 span#jsVolumeBtn
......