이승윤

feat: 초기 레이아웃 세팅

1 +node_modules
2 +package-lock.json
...\ No newline at end of file ...\ No newline at end of file
1 -나만의 요리 레시피 모음 만들기
2 -이 프로젝트는 youtube를 사용하여 해당 유튜브 채널의 정보 및 동영상을 나의 리스트로 볼 수 있도록 하기위해 만들어질 예정입니다.
3 -
1 +var express = require('express');
2 +var app = express();
3 +var port = 3000;
4 +
5 +var mongoose = require('mongoose');
6 +mongoose.Promise = global.Promise;
7 +//auto-increment를 위한 패키지
8 +var path = require('path');
9 +var logger = require('morgan');
10 +var bodyParser = require('body-parser');
11 +var cookieParser = require('cookie-parser');
12 +var flash = require('connect-flash');
13 +//passport 로그인 관련
14 +var passport = require('passport');
15 +var session = require('express-session');
16 +
17 +var db = mongoose.connection;
18 +db.on('error', console.error);
19 +db.once('open', function () {
20 + console.log('mongo db Connection');
21 +});
22 +var connect = mongoose.connect('mongodb://127.0.0.1:27017/cookBook', {
23 + useMongoClient: true,
24 +});
25 +
26 +//admin module get
27 +var admin = require('./routes/admin');
28 +var accounts = require('./routes/accounts');
29 +var auth = require('./routes/auth');
30 +var home = require('./routes/home.js');
31 +var chat = require('./routes/chat');
32 +var connectMongo = require('connect-mongo');
33 +var MongoStore = connectMongo(session);
34 +
35 +app.set('views', path.join(__dirname, 'views'));
36 +app.set('view engine', 'ejs');
37 +app.use(logger('dev'));
38 +app.use(bodyParser.json());
39 +app.use(bodyParser.urlencoded({ extended: false }));
40 +app.use(cookieParser());
41 +app.use('/uploads', express.static('uploads'));
42 +
43 +var sessionMiddleWare = session({
44 + secret: 'fastcampus',
45 + resave: false,
46 + saveUninitialized: true,
47 + cookie: {
48 + maxAge: 2000 * 60 * 60, //지속시간 2시간
49 + },
50 + store: new MongoStore({
51 + mongooseConnection: mongoose.connection,
52 + ttl: 14 * 24 * 60 * 60,
53 + }),
54 +});
55 +app.use(sessionMiddleWare);
56 +
57 +//passport 적용
58 +app.use(passport.initialize());
59 +app.use(passport.session());
60 +
61 +//플래시 메시지 관련
62 +app.use(flash());
63 +
64 +app.use(function (req, res, next) {
65 + app.locals.isLogin = req.isAuthenticated();
66 + //app.locals.urlparameter = req.url; //현재 url 정보를 보내고 싶으면 이와같이 셋팅
67 + //app.locals.userData = req.user; //사용 정보를 보내고 싶으면 이와같이 셋팅
68 + next();
69 +});
70 +
71 +//routes add
72 +app.use('/', home);
73 +app.use('/admin', admin);
74 +app.use('/accounts', accounts);
75 +app.use('/auth', auth);
76 +app.use('/chat', chat);
77 +
78 +var server = app.listen(port, function () {
79 + console.log('Express listening on port', port);
80 +});
81 +
82 +var listen = require('socket.io');
83 +var io = listen(server);
84 +//socket io passport 접근하기 위한 미들웨어 적용
85 +io.use(function (socket, next) {
86 + sessionMiddleWare(socket.request, socket.request.res, next);
87 +});
88 +require('./libs/socketConnection')(io);
1 +module.exports = function(req, res, next) {
2 + if (!req.isAuthenticated()){
3 + res.redirect('/accounts/login');
4 + }else{
5 + return next();
6 + }
7 +};
...\ No newline at end of file ...\ No newline at end of file
1 +var crypto = require('crypto');
2 +var mysalt = "fastcampus";
3 +module.exports = function(password){
4 + return crypto.createHash('sha512').update( password + mysalt).digest('base64');
5 +};
...\ No newline at end of file ...\ No newline at end of file
1 +module.exports = function(){
2 + Array.prototype.removeByValue = function (search) {
3 + var index = this.indexOf(search);
4 + if (index !== -1) {
5 + this.splice(index, 1);
6 + }
7 + };
8 +};
...\ No newline at end of file ...\ No newline at end of file
1 +require('./removeByValue')();
2 +var userList = []; //사용자 리스트를 저장할곳
3 +module.exports = function(io) {
4 + io.on('connection', function(socket){
5 +
6 + var session = socket.request.session.passport;
7 + var user = (typeof session !== 'undefined') ? ( session.user ) : "";
8 +
9 + // userList 필드에 사용자 명이 존재 하지 않으면 삽입
10 + if(userList.indexOf(user.displayname) === -1){
11 + userList.push(user.displayname);
12 + }
13 + io.emit('join', userList);
14 +
15 + //사용자 명과 메시지를 같이 반환한다.
16 + socket.on('client message', function(data){
17 + io.emit('server message', { message : data.message , displayname : user.displayname });
18 + });
19 +
20 + socket.on('disconnect', function(){
21 + userList.removeByValue(user.displayname);
22 + io.emit('leave', userList);
23 + });
24 +
25 + });
26 +};
...\ No newline at end of file ...\ No newline at end of file
1 -<!DOCTYPE html>
2 -<html>
3 -
4 -<head>
5 - <title>Hello OSS!</title>
6 -</head>
7 -
8 -<body>
9 - <header>
10 - <center>
11 - <h1>나의 요리비급서 from YouTube</h1>
12 - </center>
13 - <hr align="center" width=50%>
14 - </hr>
15 - </header>
16 -</body>
17 -
18 -</html>
...\ No newline at end of file ...\ No newline at end of file
1 +var mongoose = require('mongoose');
2 +var Schema = mongoose.Schema;
3 +
4 +//생성될 필드명을 정한다.
5 +var ProductsSchema = new Schema({
6 + name: {
7 + //제품명
8 + type: String,
9 + required: [true, '제목은 입력해주세요'],
10 + },
11 + thumbnail: String, //이미지 파일명
12 + price: Number, //가격
13 + description: String, //설명
14 + created_at: {
15 + //작성일
16 + type: Date,
17 + default: Date.now(),
18 + },
19 + username: String, //작성자추가
20 +});
21 +
22 +ProductsSchema.virtual('getDate').get(function () {
23 + var date = new Date(this.created_at);
24 + return {
25 + year: date.getFullYear(),
26 + month: date.getMonth() + 1,
27 + day: date.getDate(),
28 + };
29 +});
30 +
31 +// 1씩 증가하는 primary Key를 만든다
32 +// model : 생성할 document 이름
33 +// field : primary key , startAt : 1부터 시작
34 +
35 +module.exports = mongoose.model('products', ProductsSchema);
1 +var mongoose = require('mongoose');
2 +var Schema = mongoose.Schema;
3 +var UserSchema = new Schema({
4 + username: {
5 + type: String,
6 + required: [true, '아이디는 필수입니다.'],
7 + },
8 + password: {
9 + type: String,
10 + required: [true, '패스워드는 필수입니다.'],
11 + },
12 + displayname: String,
13 + created_at: {
14 + type: Date,
15 + default: Date.now(),
16 + },
17 +});
18 +
19 +module.exports = mongoose.model('user', UserSchema);
1 +var mongoose = require('mongoose');
2 +var Schema = mongoose.Schema;
3 +var CommentsSchema = new Schema({
4 + content: String,
5 + created_at: {
6 + type: Date,
7 + default: Date.now(),
8 + },
9 + product_id: Number,
10 +});
11 +
12 +module.exports = mongoose.model('comments', CommentsSchema);
1 +{
2 + "name": "node",
3 + "version": "1.0.0",
4 + "description": "my-cookbook-project",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "echo \"Error: no test specified\" && exit 1",
8 + "start": "nodemon ./app.js"
9 + },
10 + "dependencies": {
11 + "body-parser": "^1.18.2",
12 + "connect-flash": "^0.1.1",
13 + "connect-mongo": "^2.0.0",
14 + "cookie-parser": "^1.4.3",
15 + "csurf": "^1.9.0",
16 + "ejs": "^2.5.7",
17 + "express": "^4.15.4",
18 + "express-session": "^1.15.5",
19 + "mongodb": "^2.2.36",
20 + "mongoose": "^4.13.21",
21 + "morgan": "^1.8.2",
22 + "multer": "^1.3.0",
23 + "passport": "^0.4.0",
24 + "passport-facebook": "^2.1.1",
25 + "passport-local": "^1.0.0",
26 + "path": "^0.12.7",
27 + "socket.io": "^2.0.3"
28 + }
29 +}
1 +var express = require('express');
2 +var router = express.Router();
3 +var UserModel = require('../models/UserModel');
4 +var passwordHash = require('../libs/passwordHash');
5 +var passport = require('passport');
6 +var LocalStrategy = require('passport-local').Strategy;
7 +
8 +passport.serializeUser(function (user, done) {
9 + console.log('serializeUser');
10 + done(null, user);
11 +});
12 +
13 +passport.deserializeUser(function (user, done) {
14 + var result = user;
15 + result.password = "";
16 + console.log('deserializeUser');
17 + done(null, result);
18 +});
19 +
20 +passport.use(new LocalStrategy({
21 + usernameField: 'username',
22 + passwordField : 'password',
23 + passReqToCallback : true
24 + },
25 + function (req, username, password, done) {
26 + UserModel.findOne({ username : username , password : passwordHash(password) }, function (err,user) {
27 + if (!user){
28 + return done(null, false, { message: '아이디 또는 비밀번호 오류 입니다.' });
29 + }else{
30 + return done(null, user );
31 + }
32 + });
33 + }
34 +));
35 +
36 +router.get('/', function(req, res){
37 + res.send('account app');
38 +});
39 +
40 +router.get('/join', function(req, res){
41 + res.render('accounts/join');
42 +});
43 +
44 +router.post('/join', function(req, res){
45 + var User = new UserModel({
46 + username : req.body.username,
47 + password : passwordHash(req.body.password),
48 + displayname : req.body.displayname
49 + });
50 + User.save(function(err){
51 + res.send('<script>alert("회원가입 성공");location.href="/accounts/login";</script>');
52 + });
53 +});
54 +
55 +router.get('/login', function(req, res){
56 + res.render('accounts/login', { flashMessage : req.flash().error });
57 +});
58 +
59 +router.post('/login' ,
60 +passport.authenticate('local', {
61 + failureRedirect: '/accounts/login',
62 + failureFlash: true
63 +}),
64 +function(req, res){
65 + res.send('<script>alert("로그인 성공");location.href="/";</script>');
66 +}
67 +);
68 +
69 +router.get('/success', function(req, res){
70 + res.send(req.user);
71 +});
72 +
73 +
74 +router.get('/logout', function(req, res){
75 + req.logout();
76 + res.redirect('/accounts/login');
77 +});
78 +
79 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +var express = require('express');
2 +var router = express.Router();
3 +var ProductsModel = require('../models/ProductsModel');
4 +var CommentsModel = require('../models/VideoModels');
5 +var csrf = require('csurf');
6 +var csrfProtection = csrf({ cookie: true });
7 +var loginRequired = require('../libs/loginRequired');
8 +
9 +var path = require('path');
10 +var uploadDir = path.join(__dirname, '../uploads'); // 루트의 uploads위치에 저장한다.
11 +var fs = require('fs');
12 +
13 +//multer 셋팅
14 +var multer = require('multer');
15 +var storage = multer.diskStorage({
16 + destination: function (req, file, callback) {
17 + //이미지가 저장되는 도착지 지정
18 + callback(null, uploadDir);
19 + },
20 + filename: function (req, file, callback) {
21 + // products-날짜.jpg(png) 저장
22 + callback(
23 + null,
24 + 'products-' + Date.now() + '.' + file.mimetype.split('/')[1]
25 + );
26 + },
27 +});
28 +
29 +var upload = multer({ storage: storage });
30 +
31 +router.get('/', function (req, res) {
32 + res.send('admin main page');
33 +});
34 +
35 +router.get('/products', function (req, res) {
36 + ProductsModel.find(function (err, products) {
37 + res.render(
38 + 'admin/products',
39 + { products: products }
40 + //ProductModel의 products를 받아서
41 + //admin/products로 response를 보낸다.
42 + );
43 + });
44 +});
45 +
46 +router.get(
47 + '/products/write',
48 + loginRequired,
49 + csrfProtection,
50 + function (req, res) {
51 + //edit에서도 같은 form을 사용하므로 빈 변수( product )를 넣어서 에러를 피해준다
52 + res.render('admin/form', { product: '', csrfToken: req.csrfToken() });
53 + }
54 +);
55 +
56 +router.post(
57 + '/products/write',
58 + upload.single('thumbnail'),
59 + loginRequired,
60 + csrfProtection,
61 + function (req, res) {
62 + var product = new ProductsModel({
63 + name: req.body.name,
64 + thumbnail: req.file ? req.file.filename : '',
65 + price: req.body.price,
66 + description: req.body.description,
67 + username: req.user.username,
68 + });
69 + //이 아래는 수정되지 않았음
70 + var validationError = product.validateSync();
71 + if (validationError) {
72 + res.send(validationError);
73 + } else {
74 + product.save(function (err) {
75 + res.redirect('/admin/products');
76 + });
77 + }
78 + //이 위는 수정되지 않았음
79 + }
80 +);
81 +
82 +router.get('/products/detail/:id', function (req, res) {
83 + //url 에서 변수 값을 받아올떈 req.params.id 로 받아온다
84 + ProductsModel.findOne({ id: req.params.id }, function (err, product) {
85 + //제품정보를 받고 그안에서 댓글을 받아온다.
86 + CommentsModel.find({ product_id: req.params.id }, function (err, comments) {
87 + res.render('admin/productsDetail', {
88 + product: product,
89 + comments: comments,
90 + });
91 + });
92 + });
93 +});
94 +
95 +router.get(
96 + '/products/edit/:id',
97 + loginRequired,
98 + csrfProtection,
99 + function (req, res) {
100 + //기존에 폼에 value안에 값을 셋팅하기 위해 만든다.
101 + ProductsModel.findOne({ id: req.params.id }, function (err, product) {
102 + res.render('admin/form', {
103 + product: product,
104 + csrfToken: req.csrfToken(),
105 + });
106 + });
107 + }
108 +);
109 +
110 +router.post(
111 + '/products/edit/:id',
112 + loginRequired,
113 + upload.single('thumbnail'),
114 + csrfProtection,
115 + function (req, res) {
116 + //그전에 지정되 있는 파일명을 받아온다
117 + ProductsModel.findOne({ id: req.params.id }, function (err, product) {
118 + //아래의 코드만 추가되면 된다.
119 + if (req.file && product.thumbnail) {
120 + //요청중에 파일이 존재 할시 이전이미지 지운다.
121 + fs.unlinkSync(uploadDir + '/' + product.thumbnail);
122 + }
123 + //위의 코드만 추가되면 된다.
124 + //넣을 변수 값을 셋팅한다
125 + var query = {
126 + name: req.body.name,
127 + thumbnail: req.file ? req.file.filename : product.thumbnail,
128 + price: req.body.price,
129 + description: req.body.description,
130 + };
131 + ProductsModel.update(
132 + { id: req.params.id },
133 + { $set: query },
134 + function (err) {
135 + res.redirect('/admin/products/detail/' + req.params.id);
136 + }
137 + );
138 + });
139 + }
140 +);
141 +
142 +router.get('/products/delete/:id', function (req, res) {
143 + ProductsModel.remove({ id: req.params.id }, function (err) {
144 + res.redirect('/admin/products');
145 + });
146 +});
147 +
148 +router.post('/products/ajax_comment/insert', function (req, res) {
149 + var comment = new CommentsModel({
150 + content: req.body.content,
151 + product_id: parseInt(req.body.product_id),
152 + });
153 + comment.save(function (err, comment) {
154 + res.json({
155 + id: comment.id,
156 + content: comment.content,
157 + message: 'success',
158 + });
159 + });
160 +});
161 +
162 +router.post('/products/ajax_comment/delete', function (req, res) {
163 + CommentsModel.remove({ id: req.body.comment_id }, function (err) {
164 + res.json({ message: 'success' });
165 + });
166 +});
167 +
168 +module.exports = router;
1 +var express = require('express');
2 +var router = express.Router();
3 +var UserModel = require('../models/UserModel');
4 +var passport = require('passport');
5 +var FacebookStrategy = require('passport-facebook').Strategy;
6 +
7 +passport.serializeUser(function (user, done) {
8 + done(null, user);
9 +});
10 +
11 +passport.deserializeUser(function (user, done) {
12 + done(null, user);
13 +});
14 +
15 +passport.use(new FacebookStrategy({
16 + // https://developers.facebook.com에서 appId 및 scretID 발급
17 + clientID: "863352300499259", //입력하세요
18 + clientSecret: "36867723fbdd49dac987f9a061e2206a", //입력하세요.
19 + callbackURL: "http://localhost:3000/auth/facebook/callback",
20 + profileFields: ['id', 'displayName', 'photos', 'email'] //받고 싶은 필드 나열
21 + },
22 + function(accessToken, refreshToken, profile, done) {
23 + //아래 하나씩 찍어보면서 데이터를 참고해주세요.
24 + //console.log(profile);
25 + //console.log(profile.displayName);
26 + //console.log(profile.emails[0].value);
27 + //console.log(profile._raw);
28 + //console.log(profile._json);
29 + UserModel.findOne({ username : "fb_" + profile.id }, function(err, user){
30 + if(!user){ //없으면 회원가입 후 로그인 성공페이지 이동
31 + var regData = { //DB에 등록 및 세션에 등록될 데이터
32 + username : "fb_" + profile.id,
33 + password : "facebook_login",
34 + displayname : profile.displayName
35 + };
36 + var User = new UserModel(regData);
37 + User.save(function(err){ //DB저장
38 + done(null,regData); //세션 등록
39 + });
40 + }else{ //있으면 DB에서 가져와서 세션등록
41 + done(null,user);
42 + }
43 +
44 + });
45 + }
46 +));
47 +
48 +// http://localhost:3000/auth/facebook 접근시 facebook으로 넘길 url 작성해줌
49 +router.get('/facebook', passport.authenticate('facebook', { scope: 'email'}) );
50 +
51 +
52 +//인증후 페이스북에서 이 주소로 리턴해줌. 상단에 적은 callbackURL과 일치
53 +router.get('/facebook/callback',
54 + passport.authenticate('facebook',
55 + {
56 + successRedirect: '/',
57 + failureRedirect: '/auth/facebook/fail'
58 + }
59 + )
60 +);
61 +//로그인 성공시 이동할 주소
62 +router.get('/facebook/success', function(req,res){
63 + res.send(req.user);
64 +});
65 +
66 +router.get('/facebook/fail', function(req,res){
67 + res.send('facebook login fail');
68 +});
69 +
70 +
71 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +var express = require('express');
2 +var router = express.Router();
3 +
4 +router.get('/', function(req,res){
5 + if(!req.isAuthenticated()){
6 + res.send('<script>alert("로그인이 필요한 서비스입니다.");location.href="/accounts/login";</script>');
7 + }else{
8 + res.render('chat/index');
9 + }
10 +});
11 +
12 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +var express = require('express');
2 +var router = express.Router();
3 +var ProductsModel = require('../models/ProductsModel');
4 +
5 +/* GET home page. */
6 +router.get('/', function(req,res){
7 + ProductsModel.find( function(err,products){ //첫번째 인자는 err, 두번째는 받을 변수명
8 + res.render( 'home' ,
9 + { products : products } // DB에서 받은 products를 products변수명으로 내보냄
10 + );
11 + });
12 +});
13 +
14 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +<% include ../includes/header.ejs %>
2 +<div class="row">
3 + <div class="col-md-4 col-md-offset-4">
4 + <div class="login-panel panel panel-default">
5 + <div class="panel-heading">
6 + <h3 class="panel-title">회원가입</h3>
7 + </div>
8 + <div class="panel-body">
9 + <form role="form" action="" id="join_form" method="post">
10 + <fieldset>
11 + <div class="form-group">
12 + <input class="form-control" placeholder="ID" name="username" type="text" autofocus="" required="">
13 + </div>
14 + <div class="form-group">
15 + <input class="form-control" placeholder="Password" name="password" type="password" value="" required="">
16 + </div>
17 + <div class="form-group">
18 + <input class="form-control" placeholder="Password 확인" name="password2" type="password" value="" required="">
19 + </div>
20 + <div class="form-group">
21 + <input class="form-control" placeholder="이름" name="displayname" type="text" value="" required="">
22 + </div>
23 + <!-- Change this to a button or input when using this as a form -->
24 + <input type="submit" class="btn btn-lg btn-success btn-block" value="가입하기">
25 + <div style="margin-top: 10px">
26 + <a href="/auth/facebook" class="btn btn-lg btn-primary btn-block">
27 + <i class="fa fa-facebook" aria-hidden="true"></i> 페이스북 회원가입
28 + </a>
29 + </div>
30 + </fieldset>
31 + </form>
32 + </div>
33 + </div>
34 + </div>
35 +</div>
36 +<script type="text/javascript">
37 +(function(){
38 + $(document).ready(function() {
39 + $('#join_form').submit(function(){
40 + var $usernameInput = $('#join_form input[name=username]');
41 + var $passwordInput = $('#join_form input[name=password]');
42 + var $passwordInput2 = $('#join_form input[name=password2]');
43 + var $displayname = $('#join_form input[name=displayname]');
44 + if(!$usernameInput.val()){
45 + alert("아이디를 입력해주세요.");
46 + $usernameInput.focus();
47 + return false;
48 + }
49 + if(!$passwordInput.val()){
50 + alert("패스워드를 입력해주세요.");
51 + $passwordInput.focus();
52 + return false;
53 + }
54 + if(!$passwordInput2.val()){
55 + alert("확인 패스워드를 입력해주세요.");
56 + $passwordInput2.focus();
57 + return false;
58 + }
59 + if(!$displayname.val()){
60 + alert("이름을 입력해주세요.");
61 + $displayname.focus();
62 + return false;
63 + }
64 + if($passwordInput.val() !== $passwordInput2.val()){
65 + alert("패스워드와 확인용패스워드를 똑같이 입력해주세요.");
66 + return false;
67 + }
68 + return true;
69 + });
70 + });
71 +})();
72 +</script>
73 +<% include ../includes/footer.ejs %>
...\ No newline at end of file ...\ No newline at end of file
1 +<% include ../includes/header.ejs %>
2 +<div class="row">
3 + <div class="col-md-4 col-md-offset-4">
4 + <%if(typeof flashMessage !=='undefined') {%>
5 + <div class="alert alert-danger" role="alert"><%=flashMessage%></div>
6 + <%}%>
7 + <div class="login-panel panel panel-default">
8 + <div class="panel-heading">
9 + <h3 class="panel-title">로그인</h3>
10 + </div>
11 + <div class="panel-body">
12 + <form role="form" action="" id="login_form" method="post">
13 + <fieldset>
14 + <div class="form-group">
15 + <input class="form-control" placeholder="ID" name="username" type="text" autofocus="" required="">
16 + </div>
17 + <div class="form-group">
18 + <input class="form-control" placeholder="Password" name="password" type="password" value="" required="">
19 + </div>
20 + <!-- Change this to a button or input when using this as a form -->
21 + <input type="submit" class="btn btn-lg btn-success btn-block" value="로그인">
22 + <div style="margin-top: 10px">
23 + <a href="/auth/facebook" class="btn btn-lg btn-primary btn-block">
24 + <i class="fa fa-facebook" aria-hidden="true"></i> 페이스북 로그인
25 + </a>
26 + </div>
27 + </fieldset>
28 + </form>
29 + </div>
30 + </div>
31 + </div>
32 +</div>
33 +<script type="text/javascript">
34 +(function(){
35 + $(document).ready(function() {
36 + $('#login_form').submit(function(){
37 + var $usernameInput = $('#login_form input[name=username]');
38 + var $passwordInput = $('#login_form input[name=password]');
39 + if(!$usernameInput.val()){
40 + alert("아이디를 입력해주세요.");
41 + $usernameInput.focus();
42 + return false;
43 + }
44 + if(!$passwordInput.val()){
45 + alert("패스워드를 입력해주세요.");
46 + $passwordInput.focus();
47 + return false;
48 + }
49 + return true;
50 + });
51 + });
52 +})();
53 +</script>
54 +<% include ../includes/footer.ejs %>
...\ No newline at end of file ...\ No newline at end of file
1 +<% include ../includes/header.ejs %>
2 + <form action="" method="post" enctype="multipart/form-data">
3 + <input type="hidden" name="_csrf" value="<%=csrfToken%>" />
4 + <table class="table table-bordered">
5 + <tr>
6 + <th>제품명</th>
7 + <td><input type="text" name="name" class="form-control" value="<%=product.name%>"/></td>
8 + </tr>
9 + <tr>
10 + <th>섬네일</th>
11 + <td>
12 + <input type="file" name="thumbnail" />
13 + <% if(product.thumbnail){ %>
14 + <a href="/uploads/<%=product.thumbnail%>" target="_blank">업로드 이미지 보기</a>
15 + <% } %>
16 + </td>
17 + </tr>
18 + <tr>
19 + <th>가격</th>
20 + <td><input type="text" name="price" class="form-control" value="<%=product.price%>"/></td>
21 + </tr>
22 + <tr>
23 + <th>설명</th>
24 + <td><input type="text" name="description" class="form-control" value="<%=product.description%>"/></td>
25 + </tr>
26 + </table>
27 + <button class="btn btn-primary">작성하기</button>
28 + </form>
29 +<% include ../includes/footer.ejs %>
...\ No newline at end of file ...\ No newline at end of file
1 +<% include ../includes/header.ejs %>
2 + <table class="table table-bordered table-hover">
3 + <tr>
4 + <th>제목</th>
5 + <th>작성일</th>
6 + <th>삭제</th>
7 + </tr>
8 + <%products.forEach(function(product){%>
9 + <tr>
10 + <td>
11 + <a href="/admin/products/detail/<%=product.id%>"><%=product.name%></a>
12 + </td>
13 + <td>
14 + <%=product.getDate.year%> -
15 + <%=product.getDate.month%> -
16 + <%=product.getDate.day%>
17 + </td>
18 + <td>
19 + <a href="/admin/products/delete/<%=product.id%>" class="btn btn-danger" onclick="return confirm('삭제하시겠습니까?')">삭제</a>
20 + </td>
21 + </tr>
22 + <% }); %>
23 + </table>
24 +
25 + <a href="/admin/products/write" class="btn btn-default">작성하기</a>
26 +
27 +<% include ../includes/footer.ejs %>
...\ No newline at end of file ...\ No newline at end of file
1 +<% include ../includes/header.ejs %>
2 + <div class="panel panel-default">
3 + <div class="panel-heading">
4 + <%=product.name%>
5 + </div>
6 + <div class="panel-body">
7 + <div style="padding-bottom: 10px">
8 + 작성일 :
9 + <%=product.getDate.year%> -
10 + <%=product.getDate.month%> -
11 + <%=product.getDate.day%>
12 + </div>
13 + <% if(product.thumbnail){%>
14 + <p>
15 + <img src="/uploads/<%=product.thumbnail%>" style="max-width: 100%"/>
16 + </p>
17 + <% } %>
18 + <%=product.description%>
19 + <!-- 댓글영역 -->
20 + <div>
21 + 댓글작성하기
22 + <form id="commentForm" action="" method="post">
23 + <input type="hidden" name="product_id" value="<%=product.id%>" />
24 + <textarea class="form-control" name="content"></textarea>
25 + <button class="btn btn-primary" style="margin-top: 10px">댓글작성</button>
26 + </form>
27 + </div>
28 + <!-- 댓글영역 -->
29 + <hr />
30 + <div id="comment_area">
31 + <% comments.forEach(function(comment){ %>
32 + <div>
33 + <%=comment.content%>
34 + ( <a class='comment_delete' comment_id='<%=comment.id%>'>삭제</a> )
35 + </div>
36 + <% }); %>
37 + </div>
38 + </div>
39 + </div>
40 +
41 + <a href="/admin/products" class="btn btn-default">목록으로</a>
42 + <a href="/admin/products/edit/<%=product.id%>" class="btn btn-primary">수정</a>
43 +<% include ../includes/footer.ejs %>
44 +<script>
45 +(function(){
46 + $(document).ready(function() {
47 + $('#commentForm').submit(function(){
48 + var $contentVal = $(this).children('textarea[name=content]').val();
49 + if($contentVal){
50 + $.ajax({
51 + url: '/admin/products/ajax_comment/insert',
52 + type: 'POST',
53 + data: $(this).serialize(),
54 + })
55 + .done(function(args) {
56 + if(args.message==="success"){
57 + $('#comment_area').append(
58 + '<div>' + args.content +
59 + " ( <a class='comment_delete' comment_id='"+ args.id +"'>삭제</a> ) </div>"
60 + );
61 + $('#commentForm textarea[name=content]').val("");
62 + }
63 + })
64 + .fail(function(args) {
65 + console.log(args);
66 + });
67 + }else{
68 + alert('댓글 내용을 입력해주세요.')
69 + }
70 + return false;
71 + });
72 + });
73 +})();
74 +</script>
75 +<script>
76 +$(document).on('click' , '.comment_delete' , function(){
77 + if(confirm('삭제하시겠습니까?')){ //확인창 예 눌렀을 시만 진행
78 + var $self = $(this);
79 + $.ajax({
80 + url: '/admin/products/ajax_comment/delete',
81 + type: 'POST',
82 + data: { comment_id : $self.attr('comment_id') },
83 + })
84 + .done(function() {
85 + $self.parent().remove();
86 + alert("삭제가 완료되었습니다.");
87 + })
88 + .fail(function(args) {
89 + console.log(args);
90 + });
91 + }
92 +});
93 +</script>
...\ No newline at end of file ...\ No newline at end of file
1 +<% include ../includes/header.ejs %>
2 +<div class="row">
3 + <div class="col-sm-10">
4 + <div class="panel panel-default" id="chatWrap">
5 + <div class="panel-heading">대화내용</div>
6 + <div class="panel-body">
7 + <ul id="chatBody"></ul>
8 + </div>
9 + </div>
10 + </div>
11 + <div class="col-sm-2">
12 + <div class="panel panel-default" id="userWrap">
13 + <div class="panel-heading">User</div>
14 + <div class="panel-body">
15 + <ul id="userList"></ul>
16 + </div>
17 + </div>
18 + </div>
19 +</div>
20 +
21 +<div>
22 + <form action="" method="post" id="sendForm">
23 +
24 + <div class="input-group">
25 + <input type="hidden" name="socketId">
26 + <input type="text" name="message" class="form-control" placeholder="대화내용을 입력해주세요.">
27 + <span class="input-group-btn">
28 + <button class="btn btn-primary">작성하기</button>
29 + </span>
30 + </div><!-- /input-group -->
31 +
32 + </form>
33 +
34 +</div>
35 +
36 +<style type="text/css">
37 +.panel-default ul { padding-left:0px; }
38 +.panel-default ul li { list-style:none; padding-left:0px;}
39 +.panel-default .panel-body {min-height:350px; max-height:350px; overflow-y:scroll; }
40 +#chatWrap ul li strong::after { content: " : "; }
41 +@media (max-width: 768px) {
42 + #userWrap { display:none; }
43 + #chatWrap .panel-body { min-height:250px; }
44 +}
45 +</style>
46 +<script src="/socket.io/socket.io.js"></script>
47 +<script>
48 +(function(){
49 + var socket = io();
50 +
51 + function updateUserList(userList){
52 + $('#userList').html("");
53 + for(var key in userList){
54 + $('#userList').append('<li>' + userList[key] + '</li>');
55 + }
56 + }
57 +
58 + socket.on('join', function(data){
59 + updateUserList(data);
60 + });
61 + socket.on('server message', function(data){
62 + $('#chatBody').append('<li><strong>'+ data.displayname +'</strong>' + data.message + '</li>');
63 + });
64 +
65 + socket.on('leave', function(data){
66 + updateUserList(data);
67 + });
68 +
69 + $(document).ready(function() {
70 + $('#sendForm').submit(function(){
71 + var $massage = $('#sendForm input[name=message]');
72 + socket.emit('client message', { message : $massage.val()});
73 + $massage.val('');
74 + return false;
75 + });
76 + });
77 +})();
78 +</script>
79 +<% include ../includes/footer.ejs %>
80 +
1 +<% include ./includes/header.ejs %>
2 + <div class="container" id="masonry_container">
3 + <% products.forEach(function(product){ %>
4 + <div class="masonry-grid">
5 + <%if(product.thumbnail){%>
6 + <img src="/uploads/<%=product.thumbnail%>">
7 + <%}%>
8 + <p>
9 + <%=product.title%><br />
10 + by <%=product.username%>(
11 + <%=product.getDate.year%>.
12 + <%=product.getDate.month%>.
13 + <%=product.getDate.day%>
14 + )
15 + </p>
16 + </div>
17 + <%});%>
18 + </div>
19 +<style type="text/css">
20 +.masonry-grid img { max-width: 260px; }
21 +</style>
22 +<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
23 +<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/4.1.1/imagesloaded.pkgd.min.js"></script>
24 +<script type="text/javascript">
25 + var $masonry_container = $('#masonry_container');
26 + $masonry_container.imagesLoaded(function(){
27 + $masonry_container.masonry({
28 + itemSelector : '.masonry-grid',
29 + columnWidth : 270
30 + });
31 + });
32 +</script>
33 +<% include ./includes/footer.ejs %>
...\ No newline at end of file ...\ No newline at end of file
1 +</div>
2 +</body>
3 +<script
4 + src="https://code.jquery.com/jquery-3.2.1.min.js"
5 + integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
6 + crossorigin="anonymous"></script>
7 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +<!DOCTYPE html>
2 +<html lang="en">
3 +<head>
4 + <meta charset="UTF-8">
5 + <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 + <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 + <title>Node.js 예제</title>
8 + <script
9 + src="https://code.jquery.com/jquery-3.2.1.min.js"
10 + integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
11 + crossorigin="anonymous"></script>
12 + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
13 + <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
14 + <!--bootstrap js 추가 -->
15 + <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
16 +</head>
17 +<body>
18 + <div class="container" style="padding-top: 10px;">
19 + <nav class="navbar navbar-inverse">
20 + <div class="container-fluid">
21 + <div class="navbar-header">
22 + <!-- 오른쪽 메뉴바 -->
23 + <button type="button" class="collapsed navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-9" aria-expanded="false">
24 + <span class="sr-only">Toggle navigation</span>
25 + <span class="icon-bar"></span>
26 + <span class="icon-bar"></span>
27 + <span class="icon-bar"></span>
28 + </button>
29 + <a href="/" class="navbar-brand">Nodejs</a>
30 + </div>
31 + <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-9">
32 + <ul class="nav navbar-nav">
33 + <li class="active">
34 + <a href="/">Home</a>
35 + </li>
36 + <li><a href="/admin/products">ADMIN</a></li>
37 + <li><a href="/chat">CHAT</a></li>
38 + <% if(isLogin){%>
39 + <li><a href="/accounts/logout" onclick="return confirm('로그아웃 하시겠습니까?')">LOGOUT</a></li>
40 + <%}else{%>
41 + <li><a href="/accounts/join">JOIN</a></li>
42 + <li><a href="/accounts/login">LOGIN</a></li>
43 + <%}%>
44 + </ul>
45 + </div>
46 + </div>
47 + </nav>