이정민
1 +COOKIE_SECRET=auction
...\ No newline at end of file ...\ No newline at end of file
1 -
2 -# Created by https://www.gitignore.io/api/node
3 -
4 -### Node ###
5 -# Logs
6 -logs
7 -*.log
8 -npm-debug.log*
9 -yarn-debug.log*
10 -yarn-error.log*
11 -
12 -# Runtime data
13 -pids
14 -*.pid
15 -*.seed
16 -*.pid.lock
17 -
18 -# Directory for instrumented libs generated by jscoverage/JSCover
19 -lib-cov
20 -
21 -# Coverage directory used by tools like istanbul
22 -coverage
23 -
24 -# nyc test coverage
25 -.nyc_output
26 -
27 -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
28 -.grunt
29 -
30 -# Bower dependency directory (https://bower.io/)
31 -bower_components
32 -
33 -# node-waf configuration
34 -.lock-wscript
35 -
36 -# Compiled binary addons (https://nodejs.org/api/addons.html)
37 -build/Release
38 -
39 -# Dependency directories
40 -node_modules/
41 -jspm_packages/
42 -keys/api_option.js
43 -keys/db_option.js
44 -# TypeScript v1 declaration files
45 -typings/
46 -
47 -# Optional npm cache directory
48 -.npm
49 -
50 -# Optional eslint cache
51 -.eslintcache
52 -
53 -# Optional REPL history
54 -.node_repl_history
55 -
56 -# Output of 'npm pack'
57 -*.tgz
58 -
59 -# Yarn Integrity file
60 -.yarn-integrity
61 -
62 -# dotenv environment variables file
63 -.env
64 -
65 -# parcel-bundler cache (https://parceljs.org/)
66 -.cache
67 -
68 -# next.js build output
69 -.next
70 -
71 -# nuxt.js build output
72 -.nuxt
73 -
74 -# vuepress build output
75 -.vuepress/dist
76 -
77 -# Serverless directories
78 -.serverless
79 -
80 -
81 -# End of https://www.gitignore.io/api/node
...\ No newline at end of file ...\ No newline at end of file
1 +const express = require('express');
2 +const passport = require('passport');
3 +const bcrypt = require('bcrypt');
4 +const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
5 +const { User } = require('../models');
6 +
7 +const router = express.Router();
8 +
9 +router.post('/join', isNotLoggedIn, async (req, res, next) => {
10 + const { email, nick, password, money } = req.body;
11 + try {
12 + const exUser = await User.find({ where: { email } });
13 + if (exUser) {
14 + req.flash('joinError', '이미 가입된 이메일입니다.');
15 + return res.redirect('/join');
16 + }
17 + const hash = await bcrypt.hash(password, 12);
18 + await User.create({
19 + email,
20 + nick,
21 + password: hash,
22 + money,
23 + });
24 + return res.redirect('/');
25 + } catch (error) {
26 + console.error(error);
27 + return next(error);
28 + }
29 +});
30 +
31 +router.post('/login', isNotLoggedIn, (req, res, next) => {
32 + passport.authenticate('local', (authError, user, info) => {
33 + if (authError) {
34 + console.error(authError);
35 + return next(authError);
36 + }
37 + if (!user) {
38 + req.flash('loginError', info.message);
39 + return res.redirect('/');
40 + }
41 + return req.login(user, (loginError) => {
42 + if (loginError) {
43 + console.error(loginError);
44 + return next(loginError);
45 + }
46 + return res.redirect('/');
47 + });
48 + })(req, res, next);
49 +});
50 +
51 +router.get('/logout', isLoggedIn, (req, res) => {
52 + req.logout();
53 + req.session.destroy();
54 + res.redirect('/');
55 +});
56 +
57 +module.exports = router;
1 +const express = require('express');
2 +const multer = require('multer');
3 +const path = require('path');
4 +const fs = require('fs');
5 +const schedule = require('node-schedule');
6 +
7 +const { Good, Auction, User, sequelize } = require('../models');
8 +const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
9 +
10 +const router = express.Router();
11 +
12 +router.use((req, res, next) => {
13 + res.locals.user = req.user;
14 + next();
15 +});
16 +
17 +router.get('/', async (req, res, next) => {
18 + try {
19 + const goods = await Good.findAll({ where: { soldId: null } });
20 + res.render('main', {
21 + title: 'NodeAuction',
22 + goods,
23 + loginError: req.flash('loginError'),
24 + });
25 + } catch (error) {
26 + console.error(error);
27 + next(error);
28 + }
29 +});
30 +
31 +router.get('/join', isNotLoggedIn, (req, res) => {
32 + res.render('join', {
33 + title: '회원가입 - NodeAuction',
34 + joinError: req.flash('joinError'),
35 + });
36 +});
37 +
38 +router.get('/good', isLoggedIn, (req, res) => {
39 + res.render('good', { title: '상품 등록 - NodeAuction' });
40 +});
41 +
42 +fs.readdir('uploads', (error) => {
43 + if (error) {
44 + console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
45 + fs.mkdirSync('uploads');
46 + }
47 +});
48 +const upload = multer({
49 + storage: multer.diskStorage({
50 + destination(req, file, cb) {
51 + cb(null, 'uploads/');
52 + },
53 + filename(req, file, cb) {
54 + const ext = path.extname(file.originalname);
55 + cb(null, path.basename(file.originalname, ext) + new Date().valueOf() + ext);
56 + },
57 + }),
58 + limits: { fileSize: 5 * 1024 * 1024 },
59 +});
60 +router.post('/good', isLoggedIn, upload.single('img'), async (req, res, next) => {
61 + try {
62 + const { name, price } = req.body;
63 + const good = await Good.create({
64 + ownerId: req.user.id,
65 + name,
66 + img: req.file.filename,
67 + price,
68 + });
69 + const end = new Date();
70 + end.setDate(end.getDate() + 1); // 하루 뒤
71 + schedule.scheduleJob(end, async () => {
72 + const success = await Auction.find({
73 + where: { goodId: good.id },
74 + order: [['bid', 'DESC']],
75 + });
76 + await Good.update({ soldId: success.userId }, { where: { id: good.id } });
77 + await User.update({
78 + money: sequelize.literal(`money - ${success.bid}`),
79 + }, {
80 + where: { id: success.userId },
81 + });
82 + });
83 + res.redirect('/');
84 + } catch (error) {
85 + console.error(error);
86 + next(error);
87 + }
88 +});
89 +
90 +router.get('/good/:id', isLoggedIn, async (req, res, next) => {
91 + try {
92 + const [good, auction] = await Promise.all([
93 + Good.find({
94 + where: { id: req.params.id },
95 + include: {
96 + model: User,
97 + as: 'owner',
98 + },
99 + }),
100 + Auction.findAll({
101 + where: { goodId: req.params.id },
102 + include: { model: User },
103 + order: [['bid', 'ASC']],
104 + }),
105 + ]);
106 + res.render('auction', {
107 + title: `${good.name} - NodeAuction`,
108 + good,
109 + auction,
110 + auctionError: req.flash('auctionError'),
111 + });
112 + } catch (error) {
113 + console.error(error);
114 + next(error);
115 + }
116 +});
117 +
118 +router.post('/good/:id/bid', isLoggedIn, async (req, res, next) => {
119 + try {
120 + const { bid, msg } = req.body;
121 + const good = await Good.find({
122 + where: { id: req.params.id },
123 + include: { model: Auction },
124 + order: [[{ model: Auction }, 'bid', 'DESC']],
125 + });
126 + if (good.price > bid) { // 시작 가격보다 낮게 입찰하면
127 + return res.status(403).send('시작 가격보다 높게 입찰해야 합니다.');
128 + }
129 + // 경매 종료 시간이 지났으면
130 + if (new Date(good.createdAt).valueOf() + (24 * 60 * 60 * 1000) < new Date()) {
131 + return res.status(403).send('경매가 이미 종료되었습니다');
132 + }
133 + // 직전 입찰가와 현재 입찰가 비교
134 + if (good.auctions[0] && good.auctions[0].bid >= bid) {
135 + return res.status(403).send('이전 입찰가보다 높아야 합니다');
136 + }
137 + const result = await Auction.create({
138 + bid,
139 + msg,
140 + userId: req.user.id,
141 + goodId: req.params.id,
142 + });
143 + req.app.get('io').to(req.params.id).emit('bid', {
144 + bid: result.bid,
145 + msg: result.msg,
146 + nick: req.user.nick,
147 + });
148 + return res.send('ok');
149 + } catch (error) {
150 + console.error(error);
151 + return next(error);
152 + }
153 +});
154 +
155 +router.get('/list', isLoggedIn, async (req, res, next) => {
156 + try {
157 + const goods = await Good.findAll({
158 + where: { soldId: req.user.id },
159 + include: { model: Auction },
160 + order: [[{ model: Auction }, 'bid', 'DESC']],
161 + });
162 + res.render('list', { title: '낙찰 목록 - NodeAuction', goods });
163 + } catch (error) {
164 + console.error(error);
165 + next(error);
166 + }
167 +});
168 +
169 +module.exports = router;
1 +exports.isLoggedIn = (req, res, next) => {
2 + if (req.isAuthenticated()) {
3 + next();
4 + } else {
5 + req.flash('loginError', '로그인이 필요합니다');
6 + res.redirect('/');
7 + }
8 +};
9 +
10 +exports.isNotLoggedIn = (req, res, next) => {
11 + if (!req.isAuthenticated()) {
12 + next();
13 + } else {
14 + res.redirect('/');
15 + }
16 +};
1 +const SocketIO = require('socket.io');
2 +
3 +module.exports = (server, app) => {
4 + const io = SocketIO(server, { path: '/socket.io' });
5 +
6 + app.set('io', io);
7 +
8 + io.on('connection', (socket) => {
9 + const req = socket.request;
10 + const { headers: { referer } } = req;
11 + const roomId = referer.split('/')[referer.split('/').length - 1];
12 + socket.join(roomId);
13 + socket.on('disconnect', () => {
14 + socket.leave(roomId);
15 + });
16 + });
17 +};
1 +const SSE = require('sse');
2 +
3 +module.exports = (server) => {
4 + const sse = new SSE(server);
5 + sse.on('connection', (client) => {
6 + setInterval(() => {
7 + client.send(Date.now().toString());
8 + }, 1000);
9 + });
10 +};