Showing
16 changed files
with
271 additions
and
18 deletions
... | @@ -11,6 +11,8 @@ import flash from "express-flash"; | ... | @@ -11,6 +11,8 @@ import flash from "express-flash"; |
11 | import MongoStore from "connect-mongo"; | 11 | import MongoStore from "connect-mongo"; |
12 | import { localsMiddleware } from "./middlewares"; | 12 | import { localsMiddleware } from "./middlewares"; |
13 | import routes from "./routes"; | 13 | import routes from "./routes"; |
14 | +import globalRouter from "./routers/globalRouter"; | ||
15 | +import fileRouter from "./routers/fileRouter"; | ||
14 | 16 | ||
15 | dotenv.config(); | 17 | dotenv.config(); |
16 | const app = express(); | 18 | const app = express(); |
... | @@ -35,9 +37,8 @@ app.use( | ... | @@ -35,9 +37,8 @@ app.use( |
35 | }) | 37 | }) |
36 | ); | 38 | ); |
37 | app.use(flash()); | 39 | app.use(flash()); |
38 | -//app.use(passport.initialize()); | ||
39 | -//app.use(passport.session()); | ||
40 | - | ||
41 | app.use(localsMiddleware); | 40 | app.use(localsMiddleware); |
41 | +app.use(routes.home, globalRouter); | ||
42 | +app.use(routes.files, fileRouter); | ||
42 | 43 | ||
43 | export default app; // 파일을 불러올때 app object를 준다는 의미. | 44 | export default app; // 파일을 불러올때 app object를 준다는 의미. | ... | ... |
Project/controllers/homeController.js
0 → 100644
1 | +/* eslint-disable no-console */ | ||
2 | +import File from "../models/File"; | ||
3 | + | ||
4 | +export const home = async (req, res) => { | ||
5 | + try { | ||
6 | + const files = await File.find({}).sort({ _id: -1 }); // 모든 비디오를 가져온다. | ||
7 | + res.render("home", { pageTitle: "Home", files }); // render DB에 저장된 video의 내용을 보여준다 | ||
8 | + } catch (error) { | ||
9 | + console.log(error); | ||
10 | + res.render("home", { pageTitle: "Home", files: [] }); | ||
11 | + } | ||
12 | +}; | ||
13 | + | ||
14 | +export const search = async (req, res) => { | ||
15 | + const { | ||
16 | + query: { term: searchingBy }, | ||
17 | + } = req; // == const searchingBy = req.query.term; | ||
18 | + let files = []; | ||
19 | + try { | ||
20 | + files = await File.find({ | ||
21 | + title: { $regex: searchingBy, $options: "i" }, // i를 옵션으로 추가하면 insensitive.. 대소문자 구분 안함. | ||
22 | + }); | ||
23 | + } catch (error) { | ||
24 | + console.log(error); | ||
25 | + } | ||
26 | + res.render("search", { pageTitle: "Search", searchingBy, files }); | ||
27 | +}; | ||
28 | + | ||
29 | + | ||
30 | +// upload 또한 upload를 준비하기 위한 get 페이지와 실제 데이터를 보내는 post 페이지가 필요하다. | ||
31 | +export const getUpload = (req, res) => | ||
32 | + res.render("upload", { pageTitle: "Upload" }); | ||
33 | +export const postUpload = async (req, res) => { | ||
34 | + // const {} 를 통해 body를 받아와 요청하는 정보들을 확인한다. | ||
35 | + // 이는 pug와 db.js를 확인해야하는 듯 하다. | ||
36 | + const { | ||
37 | + body: { title, description }, | ||
38 | + file: { path }, // path로 할때는 로컬의 경로. S3는 location | ||
39 | + } = req; // file에 path라는 요소가 있다. | ||
40 | + | ||
41 | + const newVideo = await Video.create({ | ||
42 | + fileUrl: path, | ||
43 | + title, | ||
44 | + description, | ||
45 | + creator: req.user.id, | ||
46 | + // 여기있는 fileUrl, title, description은 videoDB의 속성이다. | ||
47 | + }); | ||
48 | + // console.log(newVideo); | ||
49 | + req.user.videos.push(newVideo.id); // user DB의 video atribute에 추가 | ||
50 | + req.user.save(); | ||
51 | + res.redirect(routes.videoDetail(newVideo.id)); // id는 DB의 id | ||
52 | + // id는 mongoDB가 랜덤하게 만들어준다. | ||
53 | +}; | ||
54 | + | ||
55 | +export const fileDetail = async (req, res) => { | ||
56 | + // console.log(req.params); params에 id가 있다는걸 알게 됨 | ||
57 | + const { | ||
58 | + params: { id }, | ||
59 | + } = req; | ||
60 | + try { | ||
61 | + const video = await Video.findById(id) | ||
62 | + .populate("creator") | ||
63 | + .populate("comments"); | ||
64 | + res.render("videoDetail", { pageTitle: video.title, video }); | ||
65 | + } catch (error) { | ||
66 | + res.redirect(routes.home); | ||
67 | + } | ||
68 | +}; | ||
69 | + | ||
70 | + | ||
71 | +export const deleteFile = async (req, res) => { | ||
72 | + const { | ||
73 | + params: { id }, | ||
74 | + } = req; | ||
75 | + try { | ||
76 | + const video = await Video.findById(id); | ||
77 | + // video를 받아서 render로 통해 템플릿으로 던져준다, | ||
78 | + if (String(video.creator) !== req.user.id) { | ||
79 | + throw Error(); | ||
80 | + } else { | ||
81 | + await Video.findOneAndRemove({ _id: id }); | ||
82 | + } | ||
83 | + } catch (error) { | ||
84 | + console.log(error); | ||
85 | + } | ||
86 | + // 삭제를 실패하던 성공하던 home으로 redirect한다. | ||
87 | + res.redirect(routes.home); | ||
88 | +}; | ||
89 | + |
1 | import dotenv from "dotenv"; | 1 | import dotenv from "dotenv"; |
2 | import app from "./app"; // app.js에서 export default app했기 때문에 불러올 수 있다. | 2 | import app from "./app"; // app.js에서 export default app했기 때문에 불러올 수 있다. |
3 | import "./db"; | 3 | import "./db"; |
4 | +import "./models/File"; | ||
4 | 5 | ||
5 | dotenv.config(); | 6 | dotenv.config(); |
6 | const PORT = process.env.PORT || 80; | 7 | const PORT = process.env.PORT || 80; | ... | ... |
... | @@ -15,22 +15,8 @@ const s3 = new aws.S3({ | ... | @@ -15,22 +15,8 @@ const s3 = new aws.S3({ |
15 | export const localsMiddleware = (req, res, next) => { | 15 | export const localsMiddleware = (req, res, next) => { |
16 | res.locals.siteName = "my Storage"; | 16 | res.locals.siteName = "my Storage"; |
17 | res.locals.routes = routes; | 17 | res.locals.routes = routes; |
18 | - res.locals.loggedUser = req.user || null; | 18 | + // res.locals.loggedUser = req.user || null; |
19 | // console.log(req); | 19 | // console.log(req); |
20 | next(); | 20 | next(); |
21 | }; | 21 | }; |
22 | 22 | ||
23 | -export const onlyPublic = (req, res, next) => { | ||
24 | - if (req.user) { | ||
25 | - res.redirect(routes.home); | ||
26 | - } else { | ||
27 | - next(); | ||
28 | - } | ||
29 | -}; | ||
30 | -export const onlyPrivate = (req, res, next) => { | ||
31 | - if (req.user) { | ||
32 | - next(); | ||
33 | - } else { | ||
34 | - res.redirect(routes.home); | ||
35 | - } | ||
36 | -}; | ... | ... |
Project/models/File.js
0 → 100644
1 | +// DB 모델을 작성한다. | ||
2 | +// File 자체를 DB에 저장하진 않을 것이다. 즉, byte를 저장하는 것이 아니라 file의 link를 저장한다. | ||
3 | +import mongoose from "mongoose"; | ||
4 | + | ||
5 | +const FileSchema = new mongoose.Schema({ | ||
6 | + fileUrl: { | ||
7 | + type: String, | ||
8 | + required: "File URL is required", // url이 없으면 오류메시지 출력 | ||
9 | + }, | ||
10 | + title: { | ||
11 | + type: String, | ||
12 | + required: "Title is required", | ||
13 | + }, | ||
14 | + createdAt: { | ||
15 | + type: Date, | ||
16 | + default: Date.now, // 현재 날짜를 반환하는 function | ||
17 | + }, | ||
18 | +}); | ||
19 | +// 이제 이 스키마를 이용하여 model을 만들어준다. | ||
20 | +// 모델의 이름은 "Video" | ||
21 | +const model = mongoose.model("File", FileSchema); | ||
22 | +export default model; | ||
23 | +// 모델이 만들어짐을 알리기 위해 init.js에 import해준다. |
Project/routers/fileRouter.js
0 → 100644
1 | +import express from "express"; | ||
2 | +import routes from "../routes"; | ||
3 | +import { | ||
4 | + getUpload, | ||
5 | + postUpload, | ||
6 | + fileDetail, | ||
7 | + deleteFile, | ||
8 | +} from "../controllers/homeController"; | ||
9 | +const fileRouter = express.Router(); | ||
10 | + | ||
11 | +// Upload | ||
12 | +fileRouter.get(routes.upload, getUpload); | ||
13 | +fileRouter.post(routes.upload, postUpload); | ||
14 | + | ||
15 | +// File Detail | ||
16 | +fileRouter.get(routes.fileDetail(), fileDetail); | ||
17 | + | ||
18 | +// File Delete | ||
19 | +fileRouter.get(routes.deleteFile(), deleteFile); | ||
20 | + | ||
21 | +export default fileRouter; |
Project/routers/globalRouter.js
0 → 100644
1 | +import express from "express"; | ||
2 | +import routes from "../routes"; | ||
3 | +import {home, search} from "../controllers/homeController"; | ||
4 | + | ||
5 | +const globalRouter = express.Router(); | ||
6 | + | ||
7 | +globalRouter.get(routes.home, home); | ||
8 | +globalRouter.get(routes.search, search); | ||
9 | + | ||
10 | +export default globalRouter; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +// Global | ||
2 | +const HOME = "/"; | ||
3 | +const SEARCH = "/search"; | ||
4 | + | ||
5 | +// Files | ||
6 | +const FILES = "/files"; | ||
7 | +const UPLOAD = "/upload"; | ||
8 | +const FILE_DETAIL = "/:id"; | ||
9 | +const DELETE_FILE = "/:id/delete"; | ||
10 | + | ||
11 | + | ||
12 | + | ||
13 | +const routes = { | ||
14 | + home: HOME, | ||
15 | + search: SEARCH, | ||
16 | + files: FILES, | ||
17 | + upload: UPLOAD, | ||
18 | + fileDetail: (id) => { | ||
19 | + if (id) { | ||
20 | + return `/files/${id}`; | ||
21 | + } else { | ||
22 | + return FILE_DETAIL; | ||
23 | + } | ||
24 | + }, | ||
25 | + deleteFile: (id) => { | ||
26 | + if (id) { | ||
27 | + return `/files/${id}/delete`; | ||
28 | + } else { | ||
29 | + return DELETE_FILE; | ||
30 | + } | ||
31 | + }, | ||
32 | +}; | ||
33 | +// template에서 직접 접근이 필요한 경우 함수로 바꿔준다. | ||
34 | +export default routes; | ... | ... |
Project/views/home.pug
0 → 100644
1 | +extends layouts/main | ||
2 | +include mixins/fileBlock | ||
3 | + | ||
4 | +block content | ||
5 | + a(href=`/files${routes.upload}`) Upload | ||
6 | + .home-files | ||
7 | + each item in files | ||
8 | + +fileBlock({ | ||
9 | + id: item.id, | ||
10 | + title : item.title, | ||
11 | + fileFile:item.fileUrl | ||
12 | + }) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/layouts/main.pug
0 → 100644
1 | +//공통되는 코드가 필요한 경우div | ||
2 | +include ../mixins/message | ||
3 | +doctype html | ||
4 | +html | ||
5 | + head | ||
6 | + <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> | ||
7 | + title #{pageTitle} | #{siteName} | ||
8 | + link(rel="stylesheet", href="/static/style.css") | ||
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 | + }) | ||
25 | + include ../partials/header | ||
26 | + main | ||
27 | + block content | ||
28 | + include ../partials/footer | ||
29 | + script(src="/static/main.js") |
Project/views/mixins/fileBlock.pug
0 → 100644
Project/views/mixins/message.pug
0 → 100644
Project/views/partials/footer.pug
0 → 100644
Project/views/partials/header.pug
0 → 100644
1 | +header.header | ||
2 | + .header__wrapper | ||
3 | + .header__column | ||
4 | + a(href=routes.home) | ||
5 | + i.far.fa-file-alt | ||
6 | + .header__column | ||
7 | + form(action=routes.search, method="get") | ||
8 | + input(type="text", placeholder="Search by term...", name="term") | ||
9 | + | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/search.pug
0 → 100644
1 | +extends layouts/main | ||
2 | +include mixins/videoBlock | ||
3 | + | ||
4 | +block content | ||
5 | + .search__header | ||
6 | + h3 Searching for: #{searchingBy} | ||
7 | + .search__videos | ||
8 | + if videos.length === 0 | ||
9 | + h5 No Videos Found | ||
10 | + each item in videos | ||
11 | + +videoBlock({ | ||
12 | + title : item.title, | ||
13 | + views: item.views, | ||
14 | + videoFile:item.videoFile, | ||
15 | + id: item.id | ||
16 | + }) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
Project/views/upload.pug
0 → 100644
1 | +extends layouts/main | ||
2 | + | ||
3 | +block content | ||
4 | + .form-container | ||
5 | + form(action=`/files${routes.upload}`, method="post", enctype="multipart/form-data") | ||
6 | + div.fileUpload | ||
7 | + label(for="file") File | ||
8 | + input(type="file", id="file", name="file", required=true, accept = "file/*") | ||
9 | + input(type="text", placeholder="Title", name="title", required=true) | ||
10 | + input(type="submit", value="Upload File") | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment