송용우

Merge branch 'feature/rest_api' into develop

SERVER_PORT=4000
MONGO_URL=mongodb://localhost:27017/jaksimsamil
\ No newline at end of file
MONGO_URL=mongodb://localhost:27017/jaksimsamil
JWT_SECERET=fcbyHhPCxQYLLh98b97hdyAC6tGctHQ7rQDissQx
\ No newline at end of file
......
{
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11
},
"rules": {
}
"parser": "babel-eslint",
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11
},
"rules": {}
}
......
......@@ -21,7 +21,7 @@
| group | description | method | URL | Detail | Auth |
| ------- | ------------------------ | ------ | -------------------------- | -------- | --------- |
| user | 유저 등록 | POST | api/user | 바로가기 | JWT Token |
| user | 유저 삭제 | DEELTE | api/user:id | 바로가기 | JWT Token |
| user | 유저 삭제 | DELETE | api/user:id | 바로가기 | JWT Token |
| user | 특정 유저 조회 | GET | api/user:id | 바로가기 | None |
| user | 전체 유저 조회 | GET | api/user | 바로가기 | JWT Token |
| friend | 유저 친구 등록 | POST | api/friend | 바로가기 | JWT Token |
......@@ -32,3 +32,5 @@
| notify | 슬랙 메시지 전송 요청 | POST | api/notify/slack | 바로가기 | Jwt Token |
| auth | 로그인 | POST | api/auth/login | 바로가기 | None |
| auth | 로그아웃 | GET | api/auth/logout | 바로가기 | JWT Token |
| auth | 회원가입 | POST | api/auth/register | 바로가기 | None |
| auth | 로그인 확인 | GET | api/auth/check | 바로가기 | None |
......
......@@ -2,15 +2,18 @@ const express = require("express");
const morgan = require("morgan");
const mongoose = require("mongoose");
const app = express();
const bodyParser = require("body-parser");
const api = require("./src/api");
const jwtMiddleware = require("./src/lib/jwtMiddleware");
require("dotenv").config();
const { SERVER_PORT, MONGO_URL } = process.env;
app.use(
morgan("[:date[iso]] :method :status :url :response-time(ms) :user-agent")
);
app.use(morgan("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/api", require("./api"));
app.use(bodyParser());
app.use(jwtMiddleware);
app.use("/api", api);
mongoose
.connect(MONGO_URL, { useNewUrlParser: true, useFindAndModify: false })
.then(() => {
......
......@@ -4,15 +4,20 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"bcrypt": "^4.0.1",
"body-parser": "^1.19.0",
"dotenv": "^8.2.0",
"eslint-config-prettier": "^6.11.0",
"express": "^4.17.1",
"fs": "^0.0.1-security",
"joi": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.9.17",
"morgan": "^1.10.0",
"path": "^0.12.7"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint": "^7.1.0",
"nodemon": "^2.0.4"
},
......
const Joi = require("joi");
const User = require("../../models/user");
/*
POST /api/auth/register
{
username: 'userid'
password: 'userpassword'
}
*/
exports.register = async (ctx) => {
const schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(20).required(),
password: Joi.string().required(),
});
const result = Joi.validate(ctx.request.body, schema);
if (result.error) {
ctx.status = 400;
ctx.body = result.error;
return;
}
const { username, password } = ctx.request.body;
try {
const isNameExist = await User.findByUsername(username);
if (isNameExist) {
ctx.status = 409;
return;
}
const user = new User({
username,
});
await user.setPassword(password);
await user.save();
ctx.body = user.serialize();
const token = user.generateToekn();
ctx.cookies.set("acces_token", token, {
//3일동안 유효
maxAge: 1000 * 60 * 60 * 24 * 3,
httpOnly: true,
});
} catch (e) {
ctx.throw(500, e);
}
};
/*
POST /api/auth/login
{
username: 'userid'
password: 'userpassword'
}
*/
exports.login = async (ctx) => {
const { username, password } = ctx.request.body;
if (!username || !password) {
ctx.status = 401;
return;
}
try {
const user = await User.findByUsername(username);
if (!user) {
ctx.status = 401;
return;
}
const isPasswordValid = await user.checkPassword(password);
if (!isPasswordValid) {
ctx.status = 401;
return;
}
ctx.body = user.serialize();
const token = user.generateToken();
ctx.cookies.set("acces_token", token, {
//7일동안 유효
maxAge: 1000 * 60 * 60 * 24 * 7,
httpOnly: true,
});
} catch (e) {
ctx.throw(500, e);
}
};
/*
GET api/auth/check
*/
exports.check = async (ctx) => {
const { user } = ctx.state;
if (!user) {
ctx.status = 401;
return;
}
ctx.body = user;
};
/*
POST /api/auth/logout
*/
exports.logout = async (ctx) => {
ctx.cookies.set("access_token");
ctx.status = 204;
};
const express = require("express");
const router = express.Router();
router.post("/login");
router.get("/logout");
router.post("/register");
module.exports = router;
const express = require("express");
const router = express.Router();
router.post("/");
router.delete("/:id");
router.get("/:id");
router.get("");
module.exports = router;
const express = require("express");
const app = express();
const auth = require("./auth");
const friend = require("./friend");
const notify = require("./profile");
const user = require("./user");
const profile = require("./profile");
app.use("/auth", auth);
app.use("/friend", friend);
app.use("/notify", notify);
app.use("/user", user);
app.use("/profile", profile);
module.exports = app;
......
const express = require("express");
const router = express.Router();
router.post("/slack");
module.exports = router;
const express = require("express");
const router = express.Router();
router.post("/solved:id");
router.get("/solvednum:id");
router.get("recommendps:id");
module.exports = router;
const express = require("express");
const router = express.Router();
router.post("/");
router.delete("/:id");
router.get("/:id");
router.get("");
module.exports = router;
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const jwtMiddleware = async (ctx, next) => {
const token = ctx.cookies.get("access_token");
if (!token) {
//토큰이 없을 때
return next();
}
try {
const decoded = jwt.verify(token, process.env.JWT_TOKEN);
ctx.state.user = {
_id: decoded._id,
username: decoded.username,
};
//토큰의 남은 유효 기간이 2일 이하라면 재발급
if (decoded.exp - Date.now() / 1000 < 60 * 60 * 24 * 2) {
const user = await User.findById(decoded._id);
const token = user.generateToken();
ctx.cookies.set("access_token", token, {
maxAge: 1000 * 60 * 60 * 24 * 7,
httpOnly: true,
});
}
return next();
} catch (e) {
return next();
}
};
module.exports = jwtMiddleware;
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: String,
hashedPassword: String,
});
UserSchema.methods.setPassword = async function (password) {
const hash = await bcrypt.hash(password, 10);
this.hashedPassword = hash;
};
UserSchema.methodss.checkPassword = async function (password) {
const result = await bcrypt.compare(password, this.hashedPassword);
return result;
};
UserSchema.statics.findByUsername = function (username) {
return this.findOne({ username });
};
UserSchema.methods.serialize = function () {
const data = this.toJSON();
delete data.hashedPassword;
return data;
};
UserSchema.methods.generateToken = function () {
const token = jwt.sign(
{
_id: this.id,
username: this.username,
},
process.env.JWT_SECRET,
{
expiresIn: "7d",
}
);
return token;
};
const User = mongoose.model("User", UserSchema);
module.exports = User;
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.