JaeHyeok Song

Merge branch 'lineDev' into master

This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
......@@ -3,25 +3,33 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.11.2",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.5.0",
"axios": "^0.21.0",
"immer": "^8.0.0",
"@material-ui/core": "^4.10.2",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"axios": "^0.19.2",
"eslint": "^7.14.0",
"immer": "^7.0.5",
"include-media": "^1.4.9",
"moment": "^2.27.0",
"moment": "^2.29.1",
"open-color": "^1.7.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"react-scripts": "4.0.1",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
"redux-devtools-extension": "^2.13.8",
"redux-saga": "^1.1.3",
"styled-components": "^5.1.1"
"styled-components": "^5.2.1"
},
"scripts": {
"start": "react-scripts start",
......@@ -30,7 +38,10 @@
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
"extends": "react-app",
"rules": {
"jsx-a11y/anchor-is-valid": "off"
}
},
"browserslist": {
"production": [
......@@ -44,5 +55,5 @@
"last 1 safari version"
]
},
"proxy": "http://localhost:4000"
"proxy": "http://localhost:4000/"
}
......
......@@ -25,6 +25,10 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<script src ="https://developers.kakao.com/sdk/js/kakao.min.js"></script>
<script type = "text/javascript">
Kakao.init("9454fd9d91effc97b064c201f04586d2");
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
......@@ -3,7 +3,7 @@ import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import palette from '../../lib/styles/palette';
import AuthForm from '../auth/AuthForm';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
......
import React from 'react';
const LineQR = () => {
return (
<div>
<img
src="https://qr-official.line.me/sid/L/132nudgz.png"
width="130"
/>
</div>
);
};
export default LineQR
......@@ -8,6 +8,7 @@ import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import CircularProgress from '@material-ui/core/CircularProgress';
import styled from 'styled-components';
import LineQR from './LineQR'
const useStyles = makeStyles((theme) => ({
root: {
......@@ -80,6 +81,14 @@ const SettingForm = ({
/>
</Paper>
</Grid>
<Grid container item xs={6}>
<Paper className={classes.paper} elevation={3}>
<h1>QR코드를 찍어주세요</h1>
<LineQR></LineQR>
</Paper>
</Grid>
</Grid>
</div>
);
......
......@@ -75,7 +75,7 @@ const SettingContainer = ({ history }) => {
}
}, [dispatch, user, history]);
useEffect(() => {
if (loading['profile/SYNC_BJID'] == true) {
if (loading['profile/SYNC_BJID']) {
setLoading(true);
} else {
setLoading(false);
......
const Koa = require("koa");
const Router = require("koa-router");
const bodyParser = require("koa-bodyparser");
const mongoose = require("mongoose");
const fs = require("fs");
const path = require('path');
const HTTPS = require('https');
const morgan = require("koa-morgan");
const jwtMiddleware = require("./src/lib/jwtMiddleware");
const api = require("./src/api");
const cors = require('@koa/cors'); // for fix CORS Error
require("dotenv").config();
const app = new Koa();
......@@ -15,10 +22,12 @@ const accessLogStream = fs.createWriteStream(__dirname + "/access.log", {
flags: "a",
});
require("dotenv").config();
app.use(cors());
app.use(bodyParser());
app.use(jwtMiddleware);
app.use(morgan("combined", { stream: accessLogStream }));
const { SERVER_PORT, MONGO_URL } = process.env;
const { SERVER_PORT, MONGO_URL, SSLPORT, DOMAIN } = process.env;
router.use("/api", api.routes());
app.use(router.routes()).use(router.allowedMethods());
......@@ -28,6 +37,7 @@ mongoose
useNewUrlParser: true,
useFindAndModify: false,
useUnifiedTopology: true,
useCreateIndex : true
})
.then(() => {
console.log("Connected to MongoDB");
......@@ -39,3 +49,19 @@ mongoose
app.listen(SERVER_PORT, () => {
console.log("Server is running on port", process.env.SERVER_PORT);
});
//for Line Channel
try {
const option = {
ca: fs.readFileSync('/etc/letsencrypt/live/' + DOMAIN +'/fullchain.pem'),
key: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + DOMAIN +'/privkey.pem'), 'utf8').toString(),
cert: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + DOMAIN +'/cert.pem'), 'utf8').toString(),
};
HTTPS.createServer(option, app.callback()).listen(SSLPORT, () => {
console.log(`[HTTPS] Server is started on port ${SSLPORT}`);
});
} catch (error) {
console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.');
console.log(error);
}
......
......@@ -146,6 +146,14 @@
"to-fast-properties": "^2.0.0"
}
},
"@koa/cors": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.1.0.tgz",
"integrity": "sha512-7ulRC1da/rBa6kj6P4g2aJfnET3z8Uf3SWu60cjbtxTA5g8lxRdX/Bd2P92EagGwwAhANeNw8T8if99rJliR6Q==",
"requires": {
"vary": "^1.1.2"
}
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
......@@ -826,6 +834,15 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"cron-parser": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.15.0.tgz",
......
......@@ -4,11 +4,13 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"@koa/cors": "^3.1.0",
"axios": "^0.19.2",
"bcrypt": "^4.0.1",
"body-parser": "^1.19.0",
"cheerio": "^1.0.0-rc.3",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"eslint-config-prettier": "^6.11.0",
"fs": "^0.0.1-security",
......
......@@ -14,7 +14,7 @@ exports.register = async (ctx) => {
password: Joi.string().required(),
});
const result = Joi.validate(ctx.request.body, schema);
const result = schema.validate(ctx.request.body);
if (result.error) {
ctx.status = 400;
ctx.body = result.error;
......
const Router = require("koa-router");
const auth = new Router();
const authCtrl = require("./auth.ctrl");
auth.post("/login", authCtrl.login);
auth.post("/logout", authCtrl.logout);
auth.post("/register", authCtrl.register);
......
......@@ -6,11 +6,13 @@ const friend = require("./friend");
const notify = require("./notify");
const user = require("./user");
const profile = require("./profile");
const line = require("./line")
api.use("/auth", auth.routes());
api.use("/friend", friend.routes());
api.use("/notify", notify.routes());
api.use("/user", user.routes());
api.use("/profile", profile.routes());
api.use("/line", line.routes());
module.exports = api;
module.exports = api;
\ No newline at end of file
......
const Router = require("koa-router");
const line = new Router();
const lineCtrl = require("./line.ctrl");
line.post("/hook", lineCtrl.linestart);
module.exports = line;
const request = require('request');
const TARGET_URL = 'https://api.line.me/v2/bot/message/reply'
require("dotenv").config();
const TOKEN = process.env.LINE_CHANNEL_KEY;
const Profile = require("../../models/profile");
const problem_set = require("../../data/problem_set");
const compareBJ = require("../../util/compareBJ");
/*
username : default = ""
*/
let username = "";
/*
POST api/line/hook
{
for line reply : recommend and set
}
*/
exports.linestart = async(ctx) => {
var eventObj = ctx.request.body.events[0];
// request log
console.log('======================', new Date() ,'======================');
console.log('[request]', ctx.request.body);
console.log('[request source] ', eventObj.source);
console.log('[request message]', eventObj.message);
console.log('[username]', username);
//first . set User
if(username == "") {
username = eventObj.message.text;
const result = await isExist(username);
if (!result) {
username = "";
request.post(
{
url: TARGET_URL,
headers: {
'Authorization': `Bearer ${TOKEN}`
},
json: {
"replyToken":eventObj.replyToken,
"messages":[
{
"type":"text",
"text": "작심삼일 사이트의 ID값이 올바르지 않습니다. 다시 입력해주세요."
}
]
}
},(body) => {
console.log(body)
});
} else {
request.post({
url: TARGET_URL,
headers: {
'Authorization': `Bearer ${TOKEN}`
},
json: {
"replyToken":eventObj.replyToken,
"messages":[
{
"type":"text",
"text": eventObj.message.text + " : 정상 등록 되었습니다."
}
]
}
},(body) => {
console.log(body)
});
}
};
//second. can reset username
if(eventObj.message.text == "reset ID") {
username = "";
request.post(
{
url: TARGET_URL,
headers: {
'Authorization': `Bearer ${TOKEN}`
},
json: {
"replyToken": eventObj.replyToken,
"messages":[
{
"type":"text",
"text": "작심삼일 사이트의 ID가 초기화 되었습니다."
}
]
}
},(body) => {
console.log(body)
});
}
//main : recommend Problem
if(eventObj.message.text == "문제") {
console.log("문제를 추천합니다.");
let recommendData = await lineRecommend(username);
recommendBJ(eventObj.replyToken, recommendData);
console.log(recommendData);
}
}
//for recommend message
function recommendBJ(replyToken, recommendData) {
var recommendProblem = "오늘의 추천문제는 " + recommendData.problem_title + "입니다.";
var problemURL = "https://www.boj.kr/" + recommendData.problem_number;
request.post(
{
url: TARGET_URL,
headers: {
'Authorization': `Bearer ${TOKEN}`
},
json: {
"replyToken":replyToken,
"messages":[
{
"type":"text",
"text": recommendProblem
},
{
"type":"text",
"text": problemURL
}
]
}
},(body) => {
console.log(body)
});
}
async function lineRecommend (username) {
const profile = await Profile.findByUsername(username);
if (!profile) {
console.log("401");
return;
}
const unsolved_data = await compareBJ.compareBJ(
profile.getBJdata(),
problem_set.problem_set
);
const recommendData = await compareBJ.randomItem(unsolved_data);
if (!recommendData) {
console.log("402");
return;
}
return recommendData;
};
async function isExist(username) {
const profile = await Profile.findByUsername(username);
if (!profile) {
return false;
}
return true;
}
const Router = require("koa-router");
const profile = new Router();
const profileCtrl = require("./profile.ctrl");
profile.post("/solved:id");
profile.get("/solvednum:id");
profile.post("/recommend", profileCtrl.recommend);
profile.patch("/syncBJ", profileCtrl.syncBJ);
profile.post("/setprofile", profileCtrl.setProfile);
profile.post("/getprofile", profileCtrl.getProfile);
module.exports = profile;
......
......@@ -51,7 +51,7 @@ exports.setProfile = async (ctx) => {
})
.unknown();
console.log(ctx.request.body);
const result = Joi.validate(ctx.request.body, schema);
const result = schema.validate(ctx.request.body);
if (result.error) {
ctx.status = 400;
ctx.body = result.error;
......@@ -118,14 +118,16 @@ POST /api/proflie/recommend
*/
exports.recommend = async (ctx) => {
const { username } = ctx.request.body;
if (!username) {
console.log('no given username');
ctx.status = 401;
return;
}
try {
console.log('finding username:', username);
const profile = await Profile.findByUsername(username);
if (!profile) {
console.log('no profile');
ctx.status = 401;
return;
}
......@@ -137,5 +139,7 @@ exports.recommend = async (ctx) => {
//데이터가 비었을 떄 예외처리 필요
} catch (e) {
ctx.throw(500, e);
} finally {
console.log('successfully sent user recommendation')
}
};
......