diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3998b66
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+mysql/*
+node_modules
+logs
+yarn.lock
+package-lock.json
\ No newline at end of file
diff --git a/README.md b/README.md
index bbf0a58..d347ddb 100644
--- a/README.md
+++ b/README.md
@@ -1,88 +1,30 @@
# nginx,nodejs,mysql 도커
-
-
## 추가점
-- redis 추가 (공부차원에서 넣어봄 신경쓰지 말것)
-- redis 시험 페이지 index.js에 적용
-- redis session 적용가능
+- redis 추가 (cluster mode)
+- redis session 적용가능 (session을 적용하지 않으면 passport 불가능)
- sequelize, seqeulize-auto, express 구동확인
-- 기존의 도커는 호스팅을 중점으로 파일을 만들었다
-- 지금 세팅은 개발에 집중하게 docker-compse.yml 23번째 줄에 command: tail -f /dev/null을 삽입
+- adminjs로 어드민 페이지 구축
+- socket.io 통신확인
## 필독
-- command: tail -f /dev/null 도커 프로세스가 끝나도 종료되지 않게 세팅
-- vscode나 ssh로 연결해서 node 컨테이너로 들어간후 yarn dev || npm run dev를 쳐서 실행할것
+- node 들어간후 yarn strat || npm run start를 쳐서 실행할것
+- test으 경우 알아서 test 파일을 찾기 때문에 yarn test|| npm test로 확인 가능
- 치고나면 log가 나오지 않을것인데 그것은 pm2 monit쳐서 확인 가능하다
-
+- jest를 활용해 모듈이 제대로 작동하는지 서버를 실행전에 확인할수 있다.
+> test 폴더의 user.test.js와 testuser.js를 반드시 확인할것
## docker
- docker-compose up -d build를 통해 백그라운드와 빌드를 동시 가능
- docker-compose에서 command를 통해 시작할때 서버와 mysql nginx가 실행되게 하였다.
## node.js - express
-- Login : passport 사용 (로그인 사용할때 필수적으로 사용되는 모듈)
+- passport: 사용 (로그인 사용할때 필수적으로 사용되는 모듈)
passport.js
-
-```js
-패스포트 예시
-./passport/index.js
-const passport = require('passport');
-const local = require('./local');
-const { User } = require('../models');
-
-module.exports = () => {
- passport.serializeUser((user, done) => {
- done(null, user.id);
- });
-
- passport.deserializeUser(async (id, done) => {
- try {
- const user = await User.findOne({ where: { id }});
- done(null, user); // req.user
- } catch (error) {
- console.error(error);
- done(error);
- }
- });
-
- local();
-};
-
-./passport/local.js
-const passport = require('passport');
-const { Strategy: LocalStrategy } = require('passport-local');
-const bcrypt = require('bcrypt');
-const { User } = require('../models');
-
-module.exports = () => {
- passport.use(new LocalStrategy({
- usernameField: 'email',
- passwordField: 'password',
- }, async (email, password, done) => {
- try {
- const user = await User.findOne({
- where: { email }
- });
- if (!user) {
- return done(null, false, { reason: '존재하지 않는 이메일입니다!' });
- }
- const result = await bcrypt.compare(password, user.password);
- if (result) {
- return done(null, user);
- }
- return done(null, false, { reason: '비밀번호가 틀렸습니다.' });
- } catch (error) {
- console.error(error);
- return done(error);
- }
- }));
-};
-
-```
- helmet : http header의 보안관련 모듈
- morgan : express 내에서 로그 기록 남김
-- express -view=ejs 폴더명 (express를 써서 간단한 필요 모듈을 설치하자)
-
+- winston : moragn의 추가 로그 API
+- jest : 테스트 API
+- Adminjs : 어드민 페이지 구축
## nginx
- nginx -g daemon off 명령어로 현재 백그라운드에서 실행
@@ -140,45 +82,9 @@ yarn sequelize-auto -o "./models" -d DB이름 -h mysql -u root -p 3306 -x root -
- http://localhost:80/
- mysql : ip: localhost , port 3306
-## 사용 라이브러리
-
-- 칼만 필터
-
-> 칼만 필터는 과거의 정보와 새로운 측정값을 사용하여 측정값에 포함된 잡음을 제거시켜 값을 최적화 하는 필터
- kalmanjs
-
-- 사용법 예시
-```js
-var KalmanFilter = require('kalmanjs')
-
-var kf = new KalmanFilter();
-console.log(kf.filter(3));
-console.log(kf.filter(2));
-console.log(kf.filter(1));
-
-```
-
-```js
-//Generate a simple static dataset
-var dataConstant = Array.apply(null, {length: dataSetSize}).map(function() {
- return 4;
-});
-//Add noise to data
-var noisyDataConstant = dataConstant.map(function(v) {
- return v + randn(0, 3);
-});
-
-//Apply kalman filter
-var kalmanFilter = new KalmanFilter({R: 0.01, Q: 3});
-
-var dataConstantKalman = noisyDataConstant.map(function(v) {
- return kalmanFilter.filter(v);
-});
-```
-
## Admin
> sequelize - express를 사용해서 만드는게 적정
-Admin 페이지
+
## JWT
>서버에 부담을 적게 주기위해 사용
@@ -187,69 +93,17 @@ var dataConstantKalman = noisyDataConstant.map(function(v) {
- 설치 : npm isntall jsonwebtoken rand-token
- 우리는 passport와 JWT를 같이 써야한다
-```js
-//passport/index.js
-const passport = require("passport");
-const passportJWT = require("passport-jwt");
-const bcrypt = require("bcrypt");
-
-const JWTStrategy = passportJWT.Strategy;
-const { ExtractJwt } = passportJWT;
-const LocalStrategy = require("passport-local").Strategy;
-```
-
-- ID,PWD에 대한 전략
-```js
-const LocalStrategyOption = {
- username: "UserId",
- userpwd: "PWD",
-};
-async function idpwVerify(id, pwd, done) {
- let user;
- try {
- user = await userDAO.find(id);
-
- if (!user) return done(null, false);
- const CorrectPassword = await bcrypt.compare(pwd, user.userpwd);
- if (!CorrectPassword) return done(null, false);
- } catch (e) {
- done(e);
- }
- return done(null, user);
-}
-```
-- 토큰 전략 생성
-```js
-const jwtStrategyOption = {
- jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
- secretOrKey: global.config.secret,
-};
-
-async function jwtVerifty(payload, done) {
- let user;
- try {
- user = await userDAO.find(payload.uid);
- if (!user) return done(null, false);
- } catch (e) {
- return done(e);
- }
- return done(null, user);
-}
-```
-## 날씨 정보
-- 기상청 RSS 사용
-- http://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=2714076000
## 해야할일
-- [x] 백엔드 문서 설계화
-- [x] rest-API 통신 문서 설계화
-- [x] 개발환경 구축
-- [x] 로그인 구현
-- [x] 받아오는 자료에 대한 CRUD
-- [x] 앱과의 통신
-- [ ] 로드 밸런싱
-- [ ] AWS나 학교 서버에 배포
+- [] 백엔드 문서 설계화
+- [] rest-API 통신 문서 설계화
+- [] 개발환경 구축
+- [] 로그인 구현
+- [] 받아오는 자료에 대한 CRUD
+- [] Socket 채팅
+- [] 앱과의 통신
+- [] 로드 밸런싱
+- [] 서버에 배포
-# Server
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..636acf7
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,63 @@
+version: '3'
+
+services:
+ nginx:
+ build:
+ context: ./nginx
+ container_name: nginx
+ entrypoint: dockerize -wait tcp://node:3000 -timeout 5m
+ command: [ 'nginx', '-g', 'daemon off;' ]
+ networks:
+ - node_nginx
+ depends_on:
+ - node
+ ports:
+ - 80:80
+ restart: "unless-stopped"
+ node:
+ build:
+ context: ./node
+ container_name: node
+ volumes:
+ - ./node/:/usr/src/app/
+ - /usr/src/app/node_modules/
+ entrypoint: dockerize -wait tcp://mysql:3306 -timeout 5m
+ command: tail -f /dev/null
+ # command: ["pm2-runtime", "app.js","--watch"]
+ networks:
+ - node_nginx
+ - node_mysql
+ - node_redis
+ depends_on:
+ - mysql
+ - redis
+ restart: "unless-stopped"
+ mysql:
+ image: mysql:5.7
+ command: --innodb-use-native-aio=0
+ container_name: mysql
+ restart: always
+ volumes:
+ - ./mysql:/var/lib/mysql
+ environment:
+ - MYSQL_DATABASE=nodedb
+ - MYSQL_ROOT_PASSWORD=root
+
+ ports:
+ - 3306:3306
+ networks:
+ - node_mysql
+ redis:
+ image: redis:alpine
+ container_name: redis
+ ports:
+ - 3307:3307
+ networks:
+ - node_redis
+networks:
+ node_nginx:
+ driver: bridge
+ node_mysql:
+ driver: bridge
+ node_redis:
+ driver: bridge
diff --git a/nginx/Dockerfile b/nginx/Dockerfile
new file mode 100644
index 0000000..edd61ab
--- /dev/null
+++ b/nginx/Dockerfile
@@ -0,0 +1,11 @@
+FROM nginx:1.19.7-alpine
+
+RUN apk add --no-cache openssl
+
+ENV DOCKERIZE_VERSION v0.6.1
+RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
+ && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
+ && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz
+
+RUN rm /etc/nginx/conf.d/default.conf
+COPY nginx.conf /etc/nginx/conf.d/
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
new file mode 100644
index 0000000..b6b2fa9
--- /dev/null
+++ b/nginx/nginx.conf
@@ -0,0 +1,11 @@
+server {
+ listen 80;
+ location / {
+ proxy_pass http://node:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_cache_bypass $http_upgrade;
+ }
+}
diff --git a/node/.adminjs/.entry.js b/node/.adminjs/.entry.js
new file mode 100644
index 0000000..c9edeb9
--- /dev/null
+++ b/node/.adminjs/.entry.js
@@ -0,0 +1 @@
+AdminJS.UserComponents = {}
diff --git a/node/.adminjs/bundle.js b/node/.adminjs/bundle.js
new file mode 100644
index 0000000..59a3d30
--- /dev/null
+++ b/node/.adminjs/bundle.js
@@ -0,0 +1,7 @@
+(function () {
+ 'use strict';
+
+ AdminJS.UserComponents = {};
+
+}());
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlcyI6WyIuZW50cnkuanMiXSwic291cmNlc0NvbnRlbnQiOlsiQWRtaW5KUy5Vc2VyQ29tcG9uZW50cyA9IHt9XG4iXSwibmFtZXMiOlsiQWRtaW5KUyIsIlVzZXJDb21wb25lbnRzIl0sIm1hcHBpbmdzIjoiOzs7Q0FBQUEsT0FBTyxDQUFDQyxjQUFSLEdBQXlCLEVBQXpCOzs7Ozs7In0=
diff --git a/node/.config/config.json b/node/.config/config.json
new file mode 100644
index 0000000..1fa42c9
--- /dev/null
+++ b/node/.config/config.json
@@ -0,0 +1,23 @@
+{
+ "development": {
+ "username": "root",
+ "password": "root",
+ "database": "mydb",
+ "host": "mysql",
+ "dialect": "mysql"
+ },
+ "test": {
+ "username": "root",
+ "password": "root",
+ "database": "mydb",
+ "host": "mysql",
+ "dialect": "mysql"
+ },
+ "production": {
+ "username": "root",
+ "password": "root",
+ "database": "mydb",
+ "host": "mysql",
+ "dialect": "mysql"
+ }
+}
diff --git a/node/.config/morgan.js b/node/.config/morgan.js
new file mode 100644
index 0000000..f8f6bae
--- /dev/null
+++ b/node/.config/morgan.js
@@ -0,0 +1,22 @@
+//custom morgan middleware
+const morgan = require('morgan')
+const Logger = require('./winston')
+
+
+const format = () => {
+ const result = 'combined' === 'production' ? 'combined' : 'common'
+ return result
+}
+
+const stream = { write: (message) => Logger.http(message) }
+
+const skip = (_, res) => {
+ if (process.env.NODE_ENV === 'production') {
+ return res.statusCode < 400
+ }
+ return false
+}
+
+const morganMiddleware = morgan(format(), { stream, skip })
+
+module.exports = morganMiddleware
\ No newline at end of file
diff --git a/node/.config/winston.js b/node/.config/winston.js
new file mode 100644
index 0000000..999d386
--- /dev/null
+++ b/node/.config/winston.js
@@ -0,0 +1,83 @@
+const winston = require('winston')
+const WinstonDaily = require('winston-daily-rotate-file')
+const path = require('path')
+
+const { combine, timestamp, printf, colorize } = winston.format
+
+const logDir = '../logs'
+
+const levels = {
+ error: 0,
+ warn: 1,
+ info: 2,
+ http: 3,
+ debug: 4
+}
+
+const colors = {
+ error: 'red',
+ warn: 'yellow',
+ info: 'green',
+ http: 'magenta',
+ debug: 'blue'
+}
+winston.addColors(colors)
+
+const level = () => {
+ const env = process.env.NODE_ENV || 'development'
+ const isDevelopment = env === 'development'
+ return isDevelopment ? 'debug' : 'http'
+}
+
+// Log Format
+const logFormat = combine(
+ timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
+ printf((info) => {
+ if (info.stack) {
+ return `${info.timestamp} ${info.level}: ${info.message} \n Error Stack: ${info.stack}`
+ }
+ return `${info.timestamp} ${info.level}: ${info.message}`
+ })
+)
+
+// 콘솔에 찍힐 때는 색깔을 구변해서 로깅해주자.
+const consoleOpts = {
+ handleExceptions: true,
+ level: process.env.NODE_ENV === 'production' ? 'error' : 'debug',
+ format: combine(
+ colorize({ all: true }),
+ timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' })
+ )
+}
+
+const transports = [
+ // 콘솔로그찍을 때만 색넣자.
+ new winston.transports.Console(consoleOpts),
+ // error 레벨 로그를 저장할 파일 설정
+ new WinstonDaily({
+ level: 'error',
+ datePattern: 'YYYY-MM-DD',
+ dirname: path.join(__dirname, logDir, '/error'),
+ filename: '%DATE%.error.log',
+ maxFiles: 30,
+ zippedArchive: true
+ }),
+ // 모든 레벨 로그를 저장할 파일 설정
+ new WinstonDaily({
+ level: 'debug',
+ datePattern: 'YYYY-MM-DD',
+ dirname: path.join(__dirname, logDir, '/all'),
+ filename: '%DATE%.all.log',
+ maxFiles: 7,
+ zippedArchive: true
+ })
+]
+
+const Logger = winston.createLogger({
+ level: level(),
+ levels,
+ format: logFormat,
+ transports
+})
+
+module.exports = Logger
\ No newline at end of file
diff --git a/node/Dockerfile b/node/Dockerfile
new file mode 100644
index 0000000..d9519be
--- /dev/null
+++ b/node/Dockerfile
@@ -0,0 +1,25 @@
+FROM node:15
+
+WORKDIR /usr/src/app
+
+LABEL "purpose"="webdev"
+
+RUN apt-get update && apt-get install -y wget
+
+ENV DOCKERIZE_VERSION v0.6.1
+RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
+ && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
+ && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
+
+COPY . .
+RUN apt-get install -y yarn
+RUN npm install -y -g sequelize-auto
+RUN npm install -y -g pm2
+ENV PM2_PUBLIC_KEY ghuzel5nkndxo2m
+ENV PM2_SECRET_KEY uzc2e7x174an0ld
+
+RUN npm install
+
+# EXPOSE 3000
+
+CMD tail -f /dev/null
\ No newline at end of file
diff --git a/node/app.js b/node/app.js
new file mode 100644
index 0000000..da8be31
--- /dev/null
+++ b/node/app.js
@@ -0,0 +1,86 @@
+const createError = require('http-errors');
+const helmet = require('helmet');
+const passport = require('passport');
+const express = require('express');
+const cookieParser = require('cookie-parser');
+const morganMiddleware = require('./.config/morgan');
+const session = require('express-session');
+const RedisStore = require('connect-redis')(session)
+const logger = require('./.config/winston');
+const redis = require('redis');
+const webSocket = require("./socket/socket");
+const AdminJS = require ('adminjs');
+const { buildAuthenticatedRouter } = require('@adminjs/express');
+const AdminJSSequelize = require('@adminjs/sequelize');
+const { sequelize }= require('./models');
+const indexRouter = require('./routes/index');
+const usersRouter = require('./routes/users');
+
+const app = express();
+
+sequelize.sync({ force: false }).then(() => {
+ console.log('데이터베이스 연결 성공');
+ }).catch((err) => {
+ console.error(err);
+ });
+
+// // redis 세팅
+const redisClient = redis.createClient({host: "redis", logErrors: true,})
+
+
+// // admin 사이트 제작
+AdminJS.registerAdapter(AdminJSSequelize)
+ const admin = new AdminJS({
+ databases: [sequelize],
+ })
+admin.watch()
+
+const router = buildAuthenticatedRouter(admin,{
+ authenticate: async (email, password) => {
+ if ("admin" === password && "admin" === email) {
+ return {email:"admin",password:"admin"}
+ }
+ return null
+ },
+ cookieName: 'adminBro',
+ cookiePassword: 'adminBro'
+})
+app.use(admin.options.rootPath, router)
+app.use(helmet());
+app.use(morganMiddleware)
+app.use(express.json());
+app.use(express.urlencoded({ extended: false }));
+app.use(cookieParser());
+
+app.use('/', indexRouter);
+app.use('/users',usersRouter);
+
+app.use(session({
+ secret: "secret",
+ saveUninitialized: true,
+ resave: false,
+ store: new RedisStore({
+ client : redisClient
+ })
+}))
+app.use(passport.initialize())
+app.use(passport.session())
+
+// catch 404 and forward to error handler
+app.use(function(req, res, next) {
+ next(createError(404));
+});
+
+app.use(function(err, req, res, next) {
+ res.locals.message = err.message;
+ res.locals.error = req.app.get('env') === 'development' ? err : {};
+ res.status(err.status || 500);
+});
+
+const server = app.listen(3000, () => {
+ logger.info(`Server Start Listening on port ${3000}`)
+});
+
+webSocket(server,app)
+
+module.exports = app;
diff --git a/node/ecosystem.config.js b/node/ecosystem.config.js
new file mode 100644
index 0000000..8261246
--- /dev/null
+++ b/node/ecosystem.config.js
@@ -0,0 +1,34 @@
+module.exports = {
+ apps : [{
+ name : 'SWDevelop', // 프로젝트 이름
+ script: 'app.js', // 실행되는 파일
+ instances: 4, // 클러스터 모드 사용 시 생성할 인스턴스 수
+ exec_mode: 'cluster', // fork, cluster 모드 중 선택
+ merge_logs: true, // 클러스터 모드 사용 시 각 클러스터에서 생성되는 로그를 한 파일로 합쳐준다.
+ autorestart: true, // 프로세스 실패 시 자동으로 재시작할지 선택
+ watch:true,
+ env: {
+ // 개발 환경설정
+ NODE_ENV: 'development',
+ PROT:3000,
+ },
+ env_production: {
+ // 운영 환경설정 (--env production 옵션으로 지정할 수 있다.)
+ NODE_ENV: 'production',
+ PROT:80
+ }
+ }],
+
+ deploy : {
+ production : {
+ user : 'SSH_USERNAME',
+ host : 'SSH_HOSTMACHINE',
+ ref : 'origin/master',
+ repo : 'GIT_REPOSITORY',
+ path : 'DESTINATION_PATH',
+ 'pre-deploy-local': '',
+ 'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',
+ 'pre-setup': ''
+ }
+ }
+};
diff --git a/node/index-room.html b/node/index-room.html
new file mode 100644
index 0000000..99b3680
--- /dev/null
+++ b/node/index-room.html
@@ -0,0 +1,38 @@
+
+
+
+
+ socket io redis store
+
+
+
+
+
+socketio redis store...
+
+
+
+
+
\ No newline at end of file
diff --git a/node/models/chatRoomTB.js b/node/models/chatRoomTB.js
new file mode 100644
index 0000000..61fb6c5
--- /dev/null
+++ b/node/models/chatRoomTB.js
@@ -0,0 +1,25 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('chatRoomTB', {
+ chatRoomID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ }
+ }, {
+ sequelize,
+ tableName: 'chatRoomTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "chatRoomID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/cltTB.js b/node/models/cltTB.js
new file mode 100644
index 0000000..2f00903
--- /dev/null
+++ b/node/models/cltTB.js
@@ -0,0 +1,110 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('cltTB', {
+ cltID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ cltName: {
+ type: DataTypes.STRING(10),
+ allowNull: false
+ },
+ cltTel: {
+ type: DataTypes.CHAR(11),
+ allowNull: false
+ },
+ cltAddr: {
+ type: DataTypes.STRING(45),
+ allowNull: false
+ },
+ cltEmail: {
+ type: DataTypes.STRING(50),
+ allowNull: false
+ },
+ cltPW: {
+ type: DataTypes.STRING(128),
+ allowNull: false
+ },
+ cltLv: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 1
+ },
+ lifeVolTime: {
+ type: DataTypes.TIME,
+ allowNull: false,
+ defaultValue: "00:00:00"
+ },
+ facVolTime: {
+ type: DataTypes.TIME,
+ allowNull: false,
+ defaultValue: "00:00:00"
+ },
+ eduVolTime: {
+ type: DataTypes.TIME,
+ allowNull: false,
+ defaultValue: "00:00:00"
+ },
+ osVolTime: {
+ type: DataTypes.TIME,
+ allowNull: false,
+ defaultValue: "00:00:00"
+ },
+ onlVolTime: {
+ type: DataTypes.TIME,
+ allowNull: false,
+ defaultValue: "00:00:00"
+ },
+ totalVolTime: {
+ type: DataTypes.TIME,
+ allowNull: false,
+ defaultValue: "00:00:00"
+ },
+ lifeVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ facVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ eduVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ osVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ onlVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ totalVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ }
+ }, {
+ sequelize,
+ tableName: 'cltTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "cltID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/index.js b/node/models/index.js
new file mode 100644
index 0000000..0a296cb
--- /dev/null
+++ b/node/models/index.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || 'development';
+const config = require(__dirname + '/../.config/config.json')[env];
+const db = {};
+
+let sequelize;
+if (config.use_env_variable) {
+ sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+ sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+
+fs
+ .readdirSync(__dirname)
+ .filter(file => {
+ return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
+ })
+ .forEach(file => {
+ const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(modelName => {
+ if (db[modelName].associate) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db;
diff --git a/node/models/init-models.js b/node/models/init-models.js
new file mode 100644
index 0000000..f7d3326
--- /dev/null
+++ b/node/models/init-models.js
@@ -0,0 +1,64 @@
+var DataTypes = require("sequelize").DataTypes;
+var _chatRoomTB = require("./chatRoomTB");
+var _cltTB = require("./cltTB");
+var _joinChatTB = require("./joinChatTB");
+var _joinVolActvTB = require("./joinVolActvTB");
+var _msgTB = require("./msgTB");
+var _tokenGetStateTB = require("./tokenGetStateTB");
+var _tokenTB = require("./tokenTB");
+var _volActvCatTB = require("./volActvCatTB");
+var _volActvTB = require("./volActvTB");
+
+function initModels(sequelize) {
+ var chatRoomTB = _chatRoomTB(sequelize, DataTypes);
+ var cltTB = _cltTB(sequelize, DataTypes);
+ var joinChatTB = _joinChatTB(sequelize, DataTypes);
+ var joinVolActvTB = _joinVolActvTB(sequelize, DataTypes);
+ var msgTB = _msgTB(sequelize, DataTypes);
+ var tokenGetStateTB = _tokenGetStateTB(sequelize, DataTypes);
+ var tokenTB = _tokenTB(sequelize, DataTypes);
+ var volActvCatTB = _volActvCatTB(sequelize, DataTypes);
+ var volActvTB = _volActvTB(sequelize, DataTypes);
+
+ cltTB.belongsToMany(tokenTB, { as: 'tokenID_tokenTBs', through: tokenGetStateTB, foreignKey: "cltID", otherKey: "tokenID" });
+ cltTB.belongsToMany(volActvTB, { as: 'volActvID_volActvTBs', through: joinVolActvTB, foreignKey: "cltID", otherKey: "volActvID" });
+ tokenTB.belongsToMany(cltTB, { as: 'cltID_cltTB_tokengetstatetbs', through: tokenGetStateTB, foreignKey: "tokenID", otherKey: "cltID" });
+ volActvTB.belongsToMany(cltTB, { as: 'cltID_cltTBs', through: joinVolActvTB, foreignKey: "volActvID", otherKey: "cltID" });
+ joinChatTB.belongsTo(chatRoomTB, { as: "chatRoom", foreignKey: "chatRoomID"});
+ chatRoomTB.hasMany(joinChatTB, { as: "joinchattbs", foreignKey: "chatRoomID"});
+ msgTB.belongsTo(chatRoomTB, { as: "chatRoom", foreignKey: "chatRoomID"});
+ chatRoomTB.hasMany(msgTB, { as: "msgtbs", foreignKey: "chatRoomID"});
+ joinChatTB.belongsTo(cltTB, { as: "clt", foreignKey: "cltID"});
+ cltTB.hasMany(joinChatTB, { as: "joinchattbs", foreignKey: "cltID"});
+ joinVolActvTB.belongsTo(cltTB, { as: "clt", foreignKey: "cltID"});
+ cltTB.hasMany(joinVolActvTB, { as: "joinvolactvtbs", foreignKey: "cltID"});
+ msgTB.belongsTo(cltTB, { as: "clt", foreignKey: "cltID"});
+ cltTB.hasMany(msgTB, { as: "msgtbs", foreignKey: "cltID"});
+ tokenGetStateTB.belongsTo(cltTB, { as: "clt", foreignKey: "cltID"});
+ cltTB.hasMany(tokenGetStateTB, { as: "tokengetstatetbs", foreignKey: "cltID"});
+ volActvTB.belongsTo(cltTB, { as: "publ", foreignKey: "publID"});
+ cltTB.hasMany(volActvTB, { as: "volactvtbs", foreignKey: "publID"});
+ tokenGetStateTB.belongsTo(tokenTB, { as: "token", foreignKey: "tokenID"});
+ tokenTB.hasMany(tokenGetStateTB, { as: "tokengetstatetbs", foreignKey: "tokenID"});
+ tokenTB.belongsTo(volActvCatTB, { as: "volActvCat", foreignKey: "volActvCatID"});
+ volActvCatTB.hasMany(tokenTB, { as: "tokentbs", foreignKey: "volActvCatID"});
+ volActvTB.belongsTo(volActvCatTB, { as: "volActvCat", foreignKey: "volActvCatID"});
+ volActvCatTB.hasMany(volActvTB, { as: "volactvtbs", foreignKey: "volActvCatID"});
+ joinVolActvTB.belongsTo(volActvTB, { as: "volActv", foreignKey: "volActvID"});
+ volActvTB.hasMany(joinVolActvTB, { as: "joinvolactvtbs", foreignKey: "volActvID"});
+
+ return {
+ chatRoomTB,
+ cltTB,
+ joinChatTB,
+ joinVolActvTB,
+ msgTB,
+ tokenGetStateTB,
+ tokenTB,
+ volActvCatTB,
+ volActvTB,
+ };
+}
+module.exports = initModels;
+module.exports.initModels = initModels;
+module.exports.default = initModels;
diff --git a/node/models/joinChatTB.js b/node/models/joinChatTB.js
new file mode 100644
index 0000000..3ccf495
--- /dev/null
+++ b/node/models/joinChatTB.js
@@ -0,0 +1,55 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('joinChatTB', {
+ joinChatID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ cltID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'cltTB',
+ key: 'cltID'
+ }
+ },
+ chatRoomID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'chatRoomTB',
+ key: 'chatRoomID'
+ }
+ }
+ }, {
+ sequelize,
+ tableName: 'joinChatTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "joinChatID" },
+ ]
+ },
+ {
+ name: "fk_joinChatTB_cltTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "cltID" },
+ ]
+ },
+ {
+ name: "fk_joinChatTB_chatRoomTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "chatRoomID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/joinVolActvTB.js b/node/models/joinVolActvTB.js
new file mode 100644
index 0000000..2b3f9bc
--- /dev/null
+++ b/node/models/joinVolActvTB.js
@@ -0,0 +1,66 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('joinVolActvTB', {
+ cltID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true,
+ references: {
+ model: 'cltTB',
+ key: 'cltID'
+ }
+ },
+ volActvID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true,
+ references: {
+ model: 'volActvTB',
+ key: 'volActvID'
+ }
+ },
+ cltActvState: {
+ type: DataTypes.CHAR(1),
+ allowNull: false
+ },
+ cltBKM: {
+ type: DataTypes.CHAR(1),
+ allowNull: false,
+ defaultValue: "N"
+ },
+ cltReqTime: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: Sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
+ }
+ }, {
+ sequelize,
+ tableName: 'joinVolActvTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "cltID" },
+ { name: "volActvID" },
+ ]
+ },
+ {
+ name: "fk_joinVolActvTB_cltTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "cltID" },
+ ]
+ },
+ {
+ name: "fk_joinVolActvTB_volActvTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "volActvID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/msgTB.js b/node/models/msgTB.js
new file mode 100644
index 0000000..9ea214f
--- /dev/null
+++ b/node/models/msgTB.js
@@ -0,0 +1,64 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('msgTB', {
+ msgID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ chatRoomID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'chatRoomTB',
+ key: 'chatRoomID'
+ }
+ },
+ cltID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'cltTB',
+ key: 'cltID'
+ }
+ },
+ msgCont: {
+ type: DataTypes.STRING(100),
+ allowNull: false
+ },
+ msgTime: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: Sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
+ }
+ }, {
+ sequelize,
+ tableName: 'msgTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "msgID" },
+ ]
+ },
+ {
+ name: "fk_msgTB_cltTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "cltID" },
+ ]
+ },
+ {
+ name: "fk_msgTB_chatRoomTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "chatRoomID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/tokenGetStateTB.js b/node/models/tokenGetStateTB.js
new file mode 100644
index 0000000..9e937e2
--- /dev/null
+++ b/node/models/tokenGetStateTB.js
@@ -0,0 +1,57 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('tokenGetStateTB', {
+ tokenID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true,
+ references: {
+ model: 'tokenTB',
+ key: 'tokenID'
+ }
+ },
+ cltID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true,
+ references: {
+ model: 'cltTB',
+ key: 'cltID'
+ }
+ },
+ tokenActState: {
+ type: DataTypes.CHAR(1),
+ allowNull: false,
+ defaultValue: "N"
+ }
+ }, {
+ sequelize,
+ tableName: 'tokenGetStateTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "tokenID" },
+ { name: "cltID" },
+ ]
+ },
+ {
+ name: "fk_tokenGetStateTB_tokenTB_idx",
+ using: "BTREE",
+ fields: [
+ { name: "tokenID" },
+ ]
+ },
+ {
+ name: "fk_tokenGetStateTB_cltTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "cltID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/tokenTB.js b/node/models/tokenTB.js
new file mode 100644
index 0000000..4e9efb8
--- /dev/null
+++ b/node/models/tokenTB.js
@@ -0,0 +1,56 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('tokenTB', {
+ tokenID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ volActvCatID: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ references: {
+ model: 'volActvCatTB',
+ key: 'volActvCatID'
+ }
+ },
+ tokenName: {
+ type: DataTypes.STRING(20),
+ allowNull: false
+ },
+ tokenCont: {
+ type: DataTypes.STRING(50),
+ allowNull: false
+ },
+ tokenActVolCnt: {
+ type: DataTypes.INTEGER,
+ allowNull: true
+ },
+ tokenActVolTime: {
+ type: DataTypes.TIME,
+ allowNull: true
+ }
+ }, {
+ sequelize,
+ tableName: 'tokenTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "tokenID" },
+ ]
+ },
+ {
+ name: "fk_tokenTB_volActvCatTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "volActvCatID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/volActvCatTB.js b/node/models/volActvCatTB.js
new file mode 100644
index 0000000..9786f89
--- /dev/null
+++ b/node/models/volActvCatTB.js
@@ -0,0 +1,29 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('volActvCatTB', {
+ volActvCatID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ volActCatName: {
+ type: DataTypes.STRING(10),
+ allowNull: false
+ }
+ }, {
+ sequelize,
+ tableName: 'volActvCatTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "volActvCatID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/models/volActvTB.js b/node/models/volActvTB.js
new file mode 100644
index 0000000..97ae7af
--- /dev/null
+++ b/node/models/volActvTB.js
@@ -0,0 +1,97 @@
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define('volActvTB', {
+ volActvID: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ publID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'cltTB',
+ key: 'cltID'
+ }
+ },
+ volActvCatID: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'volActvCatTB',
+ key: 'volActvCatID'
+ }
+ },
+ volActvTitle: {
+ type: DataTypes.STRING(20),
+ allowNull: false
+ },
+ volActvCont: {
+ type: DataTypes.STRING(50),
+ allowNull: false
+ },
+ volActvPer: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ volActvProgTime: {
+ type: DataTypes.TIME,
+ allowNull: false
+ },
+ volActvOSD: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: Sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
+ },
+ volActvOED: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: Sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
+ },
+ volActvPT: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: Sequelize.Sequelize.literal('CURRENT_TIMESTAMP')
+ },
+ volActvState: {
+ type: DataTypes.CHAR(1),
+ allowNull: false,
+ defaultValue: "R"
+ },
+ volActvViews: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ }
+ }, {
+ sequelize,
+ tableName: 'volActvTB',
+ timestamps: false,
+ indexes: [
+ {
+ name: "PRIMARY",
+ unique: true,
+ using: "BTREE",
+ fields: [
+ { name: "volActvID" },
+ ]
+ },
+ {
+ name: "fk_volActTB_cltTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "publID" },
+ ]
+ },
+ {
+ name: "fk_volActvTB_volActvCatTB1_idx",
+ using: "BTREE",
+ fields: [
+ { name: "volActvCatID" },
+ ]
+ },
+ ]
+ });
+};
diff --git a/node/package.json b/node/package.json
new file mode 100644
index 0000000..9bffaef
--- /dev/null
+++ b/node/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "node2",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "api-docs": "swagger-cli bundle ./src/swagger/openapi.yaml --outfile build/swagger.yaml --type yaml",
+ "start": "pm2 start ecosystem.config.js --env development",
+ "test": "jest"
+ },
+ "dependencies": {
+ "@admin-bro/express": "^3.1.0",
+ "@admin-bro/sequelize": "^1.2.1",
+ "@adminjs/express": "^4.0.0",
+ "@adminjs/sequelize": "^2.0.0",
+ "adminjs": "^5.1.0",
+ "connect-redis": "^6.0.0",
+ "cookie-parser": "~1.4.4",
+ "cors": "^2.8.5",
+ "debug": "~2.6.9",
+ "dotenv": "^10.0.0",
+ "express": "^4.17.1",
+ "express-formidable": "^1.2.0",
+ "express-session": "^1.17.2",
+ "helmet": "^4.6.0",
+ "http-errors": "~1.6.3",
+ "install": "^0.13.0",
+ "jade": "~1.11.0",
+ "moment-timezone": "^0.5.33",
+ "morgan": "^1.9.1",
+ "mysql2": "^2.3.0",
+ "passport": "^0.4.1",
+ "pm2": "^5.1.0",
+ "redis": "^3.1.2",
+ "sequelize": "^6.6.5",
+ "sequelize-auto": "^0.8.4",
+ "socket.io": "^4.1.3",
+ "socket.io-redis": "^6.1.1",
+ "swagger-jsdoc": "^6.1.0",
+ "swagger-ui-express": "^4.1.6",
+ "tslib": "^2.3.0",
+ "winston": "^3.3.3",
+ "winston-daily-rotate-file": "^4.5.5"
+ },
+ "devDependencies": {
+ "jest": "^27.0.6",
+ "supertest": "^6.1.4"
+ }
+}
diff --git a/node/passport/index.js b/node/passport/index.js
new file mode 100644
index 0000000..32c678b
--- /dev/null
+++ b/node/passport/index.js
@@ -0,0 +1,5 @@
+const passport = require('passport');
+const { Strategy: LocalStrategy } = require('passport-local');
+const bcrypt = require('bcrypt');
+
+const User = require('../models/user'); // sequelize의 user 모델
\ No newline at end of file
diff --git a/node/passport/local.js b/node/passport/local.js
new file mode 100644
index 0000000..e69de29
diff --git a/node/routes/index.js b/node/routes/index.js
new file mode 100644
index 0000000..73818e9
--- /dev/null
+++ b/node/routes/index.js
@@ -0,0 +1,9 @@
+var express = require('express');
+var router = express.Router();
+
+/* GET home page. */
+router.get('/', function(req, res, next) {
+ res.send(`${aa}`);
+});
+
+module.exports = router;
diff --git a/node/routes/users.js b/node/routes/users.js
new file mode 100644
index 0000000..a48a625
--- /dev/null
+++ b/node/routes/users.js
@@ -0,0 +1,9 @@
+var express = require('express');
+var router = express.Router();
+
+/* GET users listing. */
+router.post('/', function(req, res, next) {
+ res.send({userId:0})
+});
+
+module.exports = router;
diff --git a/node/socket/socket.js b/node/socket/socket.js
new file mode 100644
index 0000000..cee677f
--- /dev/null
+++ b/node/socket/socket.js
@@ -0,0 +1,26 @@
+const SocketIO = require("socket.io");
+var redis = require('socket.io-redis');
+
+module.exports = (server) => {
+
+ // 이는 클라이언트가 /socket.io 경로 접근시 소켓 연결을 시작함을 의미
+ const io = SocketIO(server, { path: "/socket.io",cors: { origin: "*" }});
+ io.adapter(redis({ host:'redis'}));
+
+ // 연결시 connection 이벤트 발생한 후 콜백 실행
+ io.on("connection", (socket) => {
+
+ // ws에 req가 따로 있었다면 socket에서는 socket.request에 존재합니다
+ const req = socket.request;
+ const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
+ console.log(`✔ ${ip} 클라이언트 접속, socket.id : ${socket.id}, req.ip : ${req.ip}`);
+
+ // 연결 해제
+ socket.on("disconnect", () => {
+ console.log(`${ip} 클라이언트 접속 해제. socket.id : ${socket.id}`);
+ clearInterval(socket.interval);
+ });
+ // 에러
+ socket.on("error", (error) => {});
+ });
+};
\ No newline at end of file
diff --git a/node/test/app.test.js b/node/test/app.test.js
new file mode 100644
index 0000000..e8961fd
--- /dev/null
+++ b/node/test/app.test.js
@@ -0,0 +1,45 @@
+const request = require("supertest");
+const app = require("../app");
+
+describe("POST/users",()=>{
+ describe("given a username and password", ()=>{
+
+ test("should respond with a 200 status code", async()=>{
+ const response = await request(app).post("/users").send({
+ username:"username",
+ password: "passowrd"
+ })
+ expect(response.statusCode).toBe(200)
+ })
+ // header의 content-type
+ test("should specify json in the content type header", async () => {
+ const response = await request(app).post("/users").send({
+ username: "username",
+ password: "password"
+ })
+ expect(response.headers['content-type']).toEqual(expect.stringContaining("json"))
+ })
+ test("response has userId", async () => {
+ const response = await request(app).post("/users").send({
+ username: "username",
+ password: "password"
+ })
+ expect(response.body.userId).toBeDefined()
+ })
+ })
+
+ describe("when the username and password is missing", () => {
+ test("should respond with a status code of 400", async () => {
+ const bodyData = [
+ {username: "username"},
+ {password: "password"},
+ {}
+ ]
+ for (const body of bodyData) {
+ const response = await request(app).post("/users").send(body)
+ expect(response.statusCode).toBe(400)
+ }
+ })
+ })
+
+})
\ No newline at end of file
diff --git a/node/test/testuser.js b/node/test/testuser.js
new file mode 100644
index 0000000..d22020a
--- /dev/null
+++ b/node/test/testuser.js
@@ -0,0 +1,81 @@
+// 이문서는 user.test.js의 테스트용 파일이라 실행 되지 않습니다.
+const { User, Item } = require('../models');
+const createUser = async (req, res) => {
+ try {
+ const user = await User.create(req.body);
+ return res.status(201).json({
+ user,
+ });
+ } catch (error) {
+ return res.status(500).json({ error: error.message })
+ }
+}
+const getAllUsers = async (req, res) => {
+ try {
+ const users = await User.findAll({
+ include: [
+ {
+ model: Item
+ }
+ ]
+ });
+ return res.status(200).json({ users });
+ } catch (error) {
+ return res.status(500).send(error.message);
+ }
+}
+const getUserById = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const user = await User.findOne({
+ where: { id: id },
+ include: [
+ {
+ model: Item
+ }
+ ]
+ });
+ if (user) {
+ return res.status(200).json({ user });
+ }
+ return res.status(404).send('User with the specified ID does not exists');
+ } catch (error) {
+ return res.status(500).send(error.message);
+ }
+}
+const updateUser = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const [updated] = await User.update(req.body, {
+ where: { id: id }
+ });
+ if (updated) {
+ const updatedUser = await User.findOne({ where: { id: id } });
+ return res.status(200).json({ user: updatedUser });
+ }
+ throw new Error('User not found');
+ } catch (error) {
+ return res.status(500).send(error.message);
+ }
+};
+const deleteUser = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const deleted = await User.destroy({
+ where: { id: id }
+ });
+ if (deleted) {
+ return res.status(204).send("User deleted");
+ }
+ throw new Error("User not found");
+ } catch (error) {
+ return res.status(500).send(error.message);
+ }
+};
+module.exports = {
+ createUser,
+ getAllUsers,
+ getUserById,
+ updateUser,
+ deleteUser
+}
\ No newline at end of file
diff --git a/node/test/user.test.js b/node/test/user.test.js
new file mode 100644
index 0000000..50caea0
--- /dev/null
+++ b/node/test/user.test.js
@@ -0,0 +1,49 @@
+const request = require("supertest")
+const app = require("../app.js")
+// 이문서는 test 폴더에 있는 testuser.js를 참고해 만든것입니다.
+
+// describe 에는 테스트 할 내용을 작성한다.
+describe('User API 테스트', () => {
+ // 테스트할 기능을 적는다.
+ it('유저 등록 테스트', async () => {
+ const res = await request(app)
+ .post('/api/users')
+ .send({
+ firstName: 'Bob',
+ lastName: 'Doe',
+ email: 'bob@doe.com',
+ password: '12345678'
+ })
+ // 상태 코드가 201과 같을걸로 예상
+ expect(res.statusCode).toEqual(201)
+ // body안에 user라는 프로터티가 있을걸로 판단
+ expect(res.body).toHaveProperty('user')
+ }),
+ it('should show a user', async () => {
+ const res = await request(app).get('/api/users/3')
+ expect(res.statusCode).toEqual(200)
+ expect(res.body).toHaveProperty('user')
+ }),
+ it('should show all users', async () => {
+ const res = await request(app).get('/api/users')
+ expect(res.statusCode).toEqual(200)
+ expect(res.body).toHaveProperty('users')
+ }),
+ it('should update a user', async () => {
+ const res = await request(app)
+ .put('/api/users/3')
+ .send({
+ firstName: 'Bob',
+ lastName: 'Smith',
+ email: 'bob@doe.com',
+ password: 'abc123'
+ })
+ expect(res.statusCode).toEqual(200)
+ expect(res.body).toHaveProperty('user')
+ }),
+ it('should delete a user', async () => {
+ const res = await request(app)
+ .del('/api/users/3')
+ expect(res.statusCode).toEqual(204)
+ })
+})
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..47ba503
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "bcrypt": "^5.0.1"
+ }
+}