이정민
COOKIE_SECRET=auction
\ No newline at end of file
# Created by https://www.gitignore.io/api/node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
keys/api_option.js
keys/db_option.js
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# End of https://www.gitignore.io/api/node
\ No newline at end of file
const express = require('express');
const passport = require('passport');
const bcrypt = require('bcrypt');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const { User } = require('../models');
const router = express.Router();
router.post('/join', isNotLoggedIn, async (req, res, next) => {
const { email, nick, password, money } = req.body;
try {
const exUser = await User.find({ where: { email } });
if (exUser) {
req.flash('joinError', '이미 가입된 이메일입니다.');
return res.redirect('/join');
}
const hash = await bcrypt.hash(password, 12);
await User.create({
email,
nick,
password: hash,
money,
});
return res.redirect('/');
} catch (error) {
console.error(error);
return next(error);
}
});
router.post('/login', isNotLoggedIn, (req, res, next) => {
passport.authenticate('local', (authError, user, info) => {
if (authError) {
console.error(authError);
return next(authError);
}
if (!user) {
req.flash('loginError', info.message);
return res.redirect('/');
}
return req.login(user, (loginError) => {
if (loginError) {
console.error(loginError);
return next(loginError);
}
return res.redirect('/');
});
})(req, res, next);
});
router.get('/logout', isLoggedIn, (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
module.exports = router;
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const schedule = require('node-schedule');
const { Good, Auction, User, sequelize } = require('../models');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const router = express.Router();
router.use((req, res, next) => {
res.locals.user = req.user;
next();
});
router.get('/', async (req, res, next) => {
try {
const goods = await Good.findAll({ where: { soldId: null } });
res.render('main', {
title: 'NodeAuction',
goods,
loginError: req.flash('loginError'),
});
} catch (error) {
console.error(error);
next(error);
}
});
router.get('/join', isNotLoggedIn, (req, res) => {
res.render('join', {
title: '회원가입 - NodeAuction',
joinError: req.flash('joinError'),
});
});
router.get('/good', isLoggedIn, (req, res) => {
res.render('good', { title: '상품 등록 - NodeAuction' });
});
fs.readdir('uploads', (error) => {
if (error) {
console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
fs.mkdirSync('uploads');
}
});
const upload = multer({
storage: multer.diskStorage({
destination(req, file, cb) {
cb(null, 'uploads/');
},
filename(req, file, cb) {
const ext = path.extname(file.originalname);
cb(null, path.basename(file.originalname, ext) + new Date().valueOf() + ext);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
router.post('/good', isLoggedIn, upload.single('img'), async (req, res, next) => {
try {
const { name, price } = req.body;
const good = await Good.create({
ownerId: req.user.id,
name,
img: req.file.filename,
price,
});
const end = new Date();
end.setDate(end.getDate() + 1); // 하루 뒤
schedule.scheduleJob(end, async () => {
const success = await Auction.find({
where: { goodId: good.id },
order: [['bid', 'DESC']],
});
await Good.update({ soldId: success.userId }, { where: { id: good.id } });
await User.update({
money: sequelize.literal(`money - ${success.bid}`),
}, {
where: { id: success.userId },
});
});
res.redirect('/');
} catch (error) {
console.error(error);
next(error);
}
});
router.get('/good/:id', isLoggedIn, async (req, res, next) => {
try {
const [good, auction] = await Promise.all([
Good.find({
where: { id: req.params.id },
include: {
model: User,
as: 'owner',
},
}),
Auction.findAll({
where: { goodId: req.params.id },
include: { model: User },
order: [['bid', 'ASC']],
}),
]);
res.render('auction', {
title: `${good.name} - NodeAuction`,
good,
auction,
auctionError: req.flash('auctionError'),
});
} catch (error) {
console.error(error);
next(error);
}
});
router.post('/good/:id/bid', isLoggedIn, async (req, res, next) => {
try {
const { bid, msg } = req.body;
const good = await Good.find({
where: { id: req.params.id },
include: { model: Auction },
order: [[{ model: Auction }, 'bid', 'DESC']],
});
if (good.price > bid) { // 시작 가격보다 낮게 입찰하면
return res.status(403).send('시작 가격보다 높게 입찰해야 합니다.');
}
// 경매 종료 시간이 지났으면
if (new Date(good.createdAt).valueOf() + (24 * 60 * 60 * 1000) < new Date()) {
return res.status(403).send('경매가 이미 종료되었습니다');
}
// 직전 입찰가와 현재 입찰가 비교
if (good.auctions[0] && good.auctions[0].bid >= bid) {
return res.status(403).send('이전 입찰가보다 높아야 합니다');
}
const result = await Auction.create({
bid,
msg,
userId: req.user.id,
goodId: req.params.id,
});
req.app.get('io').to(req.params.id).emit('bid', {
bid: result.bid,
msg: result.msg,
nick: req.user.nick,
});
return res.send('ok');
} catch (error) {
console.error(error);
return next(error);
}
});
router.get('/list', isLoggedIn, async (req, res, next) => {
try {
const goods = await Good.findAll({
where: { soldId: req.user.id },
include: { model: Auction },
order: [[{ model: Auction }, 'bid', 'DESC']],
});
res.render('list', { title: '낙찰 목록 - NodeAuction', goods });
} catch (error) {
console.error(error);
next(error);
}
});
module.exports = router;
exports.isLoggedIn = (req, res, next) => {
if (req.isAuthenticated()) {
next();
} else {
req.flash('loginError', '로그인이 필요합니다');
res.redirect('/');
}
};
exports.isNotLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
next();
} else {
res.redirect('/');
}
};
const SocketIO = require('socket.io');
module.exports = (server, app) => {
const io = SocketIO(server, { path: '/socket.io' });
app.set('io', io);
io.on('connection', (socket) => {
const req = socket.request;
const { headers: { referer } } = req;
const roomId = referer.split('/')[referer.split('/').length - 1];
socket.join(roomId);
socket.on('disconnect', () => {
socket.leave(roomId);
});
});
};
const SSE = require('sse');
module.exports = (server) => {
const sse = new SSE(server);
sse.on('connection', (client) => {
setInterval(() => {
client.send(Date.now().toString());
}, 1000);
});
};