송용우

Merge branch 'feature/rest_api' into develop

1 SERVER_PORT=4000 1 SERVER_PORT=4000
2 MONGO_URL=mongodb://localhost:27017/jaksimsamil 2 MONGO_URL=mongodb://localhost:27017/jaksimsamil
3 +JWT_SECERET=fcbyHhPCxQYLLh98b97hdyAC6tGctHQ7rQDissQx
...\ No newline at end of file ...\ No newline at end of file
......
1 { 1 {
2 + "parser": "babel-eslint",
2 "env": { 3 "env": {
3 "commonjs": true, 4 "commonjs": true,
4 "es6": true, 5 "es6": true,
...@@ -12,6 +13,5 @@ ...@@ -12,6 +13,5 @@
12 "parserOptions": { 13 "parserOptions": {
13 "ecmaVersion": 11 14 "ecmaVersion": 11
14 }, 15 },
15 - "rules": { 16 + "rules": {}
16 - }
17 } 17 }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
21 | group | description | method | URL | Detail | Auth | 21 | group | description | method | URL | Detail | Auth |
22 | ------- | ------------------------ | ------ | -------------------------- | -------- | --------- | 22 | ------- | ------------------------ | ------ | -------------------------- | -------- | --------- |
23 | user | 유저 등록 | POST | api/user | 바로가기 | JWT Token | 23 | user | 유저 등록 | POST | api/user | 바로가기 | JWT Token |
24 -| user | 유저 삭제 | DEELTE | api/user:id | 바로가기 | JWT Token | 24 +| user | 유저 삭제 | DELETE | api/user:id | 바로가기 | JWT Token |
25 | user | 특정 유저 조회 | GET | api/user:id | 바로가기 | None | 25 | user | 특정 유저 조회 | GET | api/user:id | 바로가기 | None |
26 | user | 전체 유저 조회 | GET | api/user | 바로가기 | JWT Token | 26 | user | 전체 유저 조회 | GET | api/user | 바로가기 | JWT Token |
27 | friend | 유저 친구 등록 | POST | api/friend | 바로가기 | JWT Token | 27 | friend | 유저 친구 등록 | POST | api/friend | 바로가기 | JWT Token |
...@@ -32,3 +32,5 @@ ...@@ -32,3 +32,5 @@
32 | notify | 슬랙 메시지 전송 요청 | POST | api/notify/slack | 바로가기 | Jwt Token | 32 | notify | 슬랙 메시지 전송 요청 | POST | api/notify/slack | 바로가기 | Jwt Token |
33 | auth | 로그인 | POST | api/auth/login | 바로가기 | None | 33 | auth | 로그인 | POST | api/auth/login | 바로가기 | None |
34 | auth | 로그아웃 | GET | api/auth/logout | 바로가기 | JWT Token | 34 | auth | 로그아웃 | GET | api/auth/logout | 바로가기 | JWT Token |
35 +| auth | 회원가입 | POST | api/auth/register | 바로가기 | None |
36 +| auth | 로그인 확인 | GET | api/auth/check | 바로가기 | None |
......
...@@ -2,15 +2,18 @@ const express = require("express"); ...@@ -2,15 +2,18 @@ const express = require("express");
2 const morgan = require("morgan"); 2 const morgan = require("morgan");
3 const mongoose = require("mongoose"); 3 const mongoose = require("mongoose");
4 const app = express(); 4 const app = express();
5 +const bodyParser = require("body-parser");
6 +const api = require("./src/api");
7 +const jwtMiddleware = require("./src/lib/jwtMiddleware");
5 require("dotenv").config(); 8 require("dotenv").config();
6 const { SERVER_PORT, MONGO_URL } = process.env; 9 const { SERVER_PORT, MONGO_URL } = process.env;
7 -app.use( 10 +app.use(morgan("dev"));
8 - morgan("[:date[iso]] :method :status :url :response-time(ms) :user-agent")
9 -);
10 app.use(express.json()); 11 app.use(express.json());
11 app.use(express.urlencoded({ extended: false })); 12 app.use(express.urlencoded({ extended: false }));
12 -app.use("/api", require("./api")); 13 +app.use(bodyParser());
14 +app.use(jwtMiddleware);
13 15
16 +app.use("/api", api);
14 mongoose 17 mongoose
15 .connect(MONGO_URL, { useNewUrlParser: true, useFindAndModify: false }) 18 .connect(MONGO_URL, { useNewUrlParser: true, useFindAndModify: false })
16 .then(() => { 19 .then(() => {
......
...@@ -4,15 +4,20 @@ ...@@ -4,15 +4,20 @@
4 "main": "index.js", 4 "main": "index.js",
5 "license": "MIT", 5 "license": "MIT",
6 "dependencies": { 6 "dependencies": {
7 + "bcrypt": "^4.0.1",
8 + "body-parser": "^1.19.0",
7 "dotenv": "^8.2.0", 9 "dotenv": "^8.2.0",
8 "eslint-config-prettier": "^6.11.0", 10 "eslint-config-prettier": "^6.11.0",
9 "express": "^4.17.1", 11 "express": "^4.17.1",
10 "fs": "^0.0.1-security", 12 "fs": "^0.0.1-security",
13 + "joi": "^14.3.1",
14 + "jsonwebtoken": "^8.5.1",
11 "mongoose": "^5.9.17", 15 "mongoose": "^5.9.17",
12 "morgan": "^1.10.0", 16 "morgan": "^1.10.0",
13 "path": "^0.12.7" 17 "path": "^0.12.7"
14 }, 18 },
15 "devDependencies": { 19 "devDependencies": {
20 + "babel-eslint": "^10.1.0",
16 "eslint": "^7.1.0", 21 "eslint": "^7.1.0",
17 "nodemon": "^2.0.4" 22 "nodemon": "^2.0.4"
18 }, 23 },
......
1 +const Joi = require("joi");
2 +const User = require("../../models/user");
3 +/*
4 +POST /api/auth/register
5 +{
6 + username: 'userid'
7 + password: 'userpassword'
8 +}
9 +*/
10 +exports.register = async (ctx) => {
11 + const schema = Joi.object().keys({
12 + username: Joi.string().alphanum().min(3).max(20).required(),
13 + password: Joi.string().required(),
14 + });
15 + const result = Joi.validate(ctx.request.body, schema);
16 + if (result.error) {
17 + ctx.status = 400;
18 + ctx.body = result.error;
19 + return;
20 + }
21 +
22 + const { username, password } = ctx.request.body;
23 + try {
24 + const isNameExist = await User.findByUsername(username);
25 + if (isNameExist) {
26 + ctx.status = 409;
27 + return;
28 + }
29 + const user = new User({
30 + username,
31 + });
32 + await user.setPassword(password);
33 + await user.save();
34 + ctx.body = user.serialize();
35 +
36 + const token = user.generateToekn();
37 + ctx.cookies.set("acces_token", token, {
38 + //3일동안 유효
39 + maxAge: 1000 * 60 * 60 * 24 * 3,
40 + httpOnly: true,
41 + });
42 + } catch (e) {
43 + ctx.throw(500, e);
44 + }
45 +};
46 +/*
47 +POST /api/auth/login
48 +{
49 + username: 'userid'
50 + password: 'userpassword'
51 +}
52 + */
53 +exports.login = async (ctx) => {
54 + const { username, password } = ctx.request.body;
55 + if (!username || !password) {
56 + ctx.status = 401;
57 + return;
58 + }
59 + try {
60 + const user = await User.findByUsername(username);
61 + if (!user) {
62 + ctx.status = 401;
63 + return;
64 + }
65 + const isPasswordValid = await user.checkPassword(password);
66 + if (!isPasswordValid) {
67 + ctx.status = 401;
68 + return;
69 + }
70 + ctx.body = user.serialize();
71 + const token = user.generateToken();
72 + ctx.cookies.set("acces_token", token, {
73 + //7일동안 유효
74 + maxAge: 1000 * 60 * 60 * 24 * 7,
75 + httpOnly: true,
76 + });
77 + } catch (e) {
78 + ctx.throw(500, e);
79 + }
80 +};
81 +/*
82 +GET api/auth/check
83 +*/
84 +exports.check = async (ctx) => {
85 + const { user } = ctx.state;
86 + if (!user) {
87 + ctx.status = 401;
88 + return;
89 + }
90 + ctx.body = user;
91 +};
92 +/*
93 +POST /api/auth/logout
94 +*/
95 +exports.logout = async (ctx) => {
96 + ctx.cookies.set("access_token");
97 + ctx.status = 204;
98 +};
1 +const express = require("express");
2 +const router = express.Router();
3 +
4 +router.post("/login");
5 +router.get("/logout");
6 +router.post("/register");
7 +
8 +
9 +
10 +
11 +module.exports = router;
1 +const express = require("express");
2 +const router = express.Router();
3 +
4 +router.post("/");
5 +router.delete("/:id");
6 +router.get("/:id");
7 +router.get("");
8 +
9 +module.exports = router;
1 +const express = require("express");
2 +const app = express();
3 +
4 +const auth = require("./auth");
5 +const friend = require("./friend");
6 +const notify = require("./profile");
7 +const user = require("./user");
8 +const profile = require("./profile");
9 +
10 +app.use("/auth", auth);
11 +app.use("/friend", friend);
12 +app.use("/notify", notify);
13 +app.use("/user", user);
14 +app.use("/profile", profile);
15 +
16 +module.exports = app;
......
1 +const express = require("express");
2 +const router = express.Router();
3 +
4 +router.post("/slack");
5 +
6 +module.exports = router;
1 +const express = require("express");
2 +const router = express.Router();
3 +
4 +router.post("/solved:id");
5 +router.get("/solvednum:id");
6 +router.get("recommendps:id");
7 +
8 +module.exports = router;
1 +const express = require("express");
2 +const router = express.Router();
3 +
4 +router.post("/");
5 +router.delete("/:id");
6 +router.get("/:id");
7 +router.get("");
8 +
9 +module.exports = router;
1 +const jwt = require("jsonwebtoken");
2 +const User = require("../models/user");
3 +const jwtMiddleware = async (ctx, next) => {
4 + const token = ctx.cookies.get("access_token");
5 + if (!token) {
6 + //토큰이 없을 때
7 + return next();
8 + }
9 + try {
10 + const decoded = jwt.verify(token, process.env.JWT_TOKEN);
11 + ctx.state.user = {
12 + _id: decoded._id,
13 + username: decoded.username,
14 + };
15 + //토큰의 남은 유효 기간이 2일 이하라면 재발급
16 + if (decoded.exp - Date.now() / 1000 < 60 * 60 * 24 * 2) {
17 + const user = await User.findById(decoded._id);
18 + const token = user.generateToken();
19 + ctx.cookies.set("access_token", token, {
20 + maxAge: 1000 * 60 * 60 * 24 * 7,
21 + httpOnly: true,
22 + });
23 + }
24 + return next();
25 + } catch (e) {
26 + return next();
27 + }
28 +};
29 +
30 +module.exports = jwtMiddleware;
1 +const mongoose = require("mongoose");
2 +const bcrypt = require("bcrypt");
3 +const jwt = require("jsonwebtoken");
4 +const Schema = mongoose.Schema;
5 +
6 +const UserSchema = new Schema({
7 + username: String,
8 + hashedPassword: String,
9 +});
10 +
11 +UserSchema.methods.setPassword = async function (password) {
12 + const hash = await bcrypt.hash(password, 10);
13 + this.hashedPassword = hash;
14 +};
15 +UserSchema.methodss.checkPassword = async function (password) {
16 + const result = await bcrypt.compare(password, this.hashedPassword);
17 + return result;
18 +};
19 +UserSchema.statics.findByUsername = function (username) {
20 + return this.findOne({ username });
21 +};
22 +UserSchema.methods.serialize = function () {
23 + const data = this.toJSON();
24 + delete data.hashedPassword;
25 + return data;
26 +};
27 +UserSchema.methods.generateToken = function () {
28 + const token = jwt.sign(
29 + {
30 + _id: this.id,
31 + username: this.username,
32 + },
33 + process.env.JWT_SECRET,
34 + {
35 + expiresIn: "7d",
36 + }
37 + );
38 + return token;
39 +};
40 +const User = mongoose.model("User", UserSchema);
41 +module.exports = User;
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.