Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
semi: false,
singleQuote: true,
trailingComma: 'all',
}
362 changes: 360 additions & 2 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,31 @@
"@ckeditor/ckeditor5-react": "^6.1.0",
"@types/bcrypt": "^5.0.1",
"@types/date-fns": "^2.6.0",
"@types/formidable": "^3.4.5",
"@types/multer": "^1.4.10",
"@types/react-datepicker": "^4.19.3",
"axios": "^1.6.1",
"bcrypt": "^5.1.1",
"date-fns": "^2.30.0",
"dotenv": "^16.3.1",
"formidable": "^3.5.1",
"gsap": "^3.12.2",
"jsonwebtoken": "^9.0.2",
"moment": "^2.29.4",
"multer": "^1.4.5-lts.1",
"mysql": "^2.18.1",
"mysql2": "^3.6.3",
"next": "14.0.1",
"next-auth": "^4.24.4",
"next-connect": "^1.0.0",
"next-themes": "^0.2.1",
"prettier": "^3.1.0",
"react": "^18",
"react-calendar": "^4.6.1",
"react-datepicker": "^4.21.0",
"react-dom": "^18",
"react-modal": "^3.16.1"
"react-modal": "^3.16.1",
"recoil": "^0.7.7"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.4",
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/imgs/1699932377524_q28ucKw2qv7zceIO.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/imgs/1699932690871_6ziy3nprERU56YTy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/imgs/스크린샷 2023-09-11 100559.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/imgs/스크린샷 2023-09-15 085650.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions src/app/api/advice/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import axios from 'axios';
import { NextResponse } from 'next/server';
import { verifyJwt } from '@/app/lib/jwt';

interface reqBody {
text: string;
}
type resBody = {
result: string;
}

export const POST = async(req: Request) => {
const body: reqBody = await req.json();
const accessToken = req.headers.get('Authorization')?.split('mlru ')[1] as string;

// 헤더에 토큰이 없거나, 토큰 복호화 실패하면 리턴.
if(!accessToken || !verifyJwt(accessToken)) {
return new Response(JSON.stringify({"result":"No Authorization"}))
}
try {
const res = await axios.post(`${process.env.MODEL_URL}/advice/`,{
text: body.text
});
const result = res.data
return NextResponse.json(result.result)
} catch (err) {
console.log(err)
return NextResponse.json(err)
}

};
Comment on lines +1 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The JWT token extraction from the Authorization header is not correctly splitting the token. It seems to be looking for the string 'mlru ' which might be a typo or a specific token prefix. Verify that this is the intended prefix and not the standard 'Bearer '.
  • The error handling in the catch block is directly returning the error object to the client, which could potentially leak sensitive information. It's better to return a generic error message to the client and log the detailed error server-side.
  • The response for an invalid or missing token is a plain Response object, not a NextResponse object. For consistency, consider using NextResponse for all responses.
  • The POST function should explicitly set the response content type to application/json when returning JSON data.
  • The POST function is not setting an appropriate HTTP status code when the JWT token is missing or invalid. It should return a 401 Unauthorized status code in such cases.
  • The POST function is not setting an appropriate HTTP status code for errors caught in the catch block. It should return a 500 Internal Server Error status code for unexpected errors.
-    const accessToken = req.headers.get('Authorization')?.split('mlru ')[1] as string;
+    const accessToken = req.headers.get('Authorization')?.split('Bearer ')[1] as string;
-        return new Response(JSON.stringify({"result":"No Authorization"}))
+        return new NextResponse(JSON.stringify({"result":"No Authorization"}), { status: 401, headers: { 'Content-Type': 'application/json' } })
-        return NextResponse.json(err)
+        // Log the error for server-side inspection
+        console.error(err);
+        // Return a generic error message to the client
+        return new NextResponse(JSON.stringify({"result":"An error occurred"}), { status: 500, headers: { 'Content-Type': 'application/json' } })

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
import axios from 'axios';
import { NextResponse } from 'next/server';
import { verifyJwt } from '@/app/lib/jwt';
interface reqBody {
text: string;
}
type resBody = {
result: string;
}
export const POST = async(req: Request) => {
const body: reqBody = await req.json();
const accessToken = req.headers.get('Authorization')?.split('mlru ')[1] as string;
// 헤더에 토큰이 없거나, 토큰 복호화 실패하면 리턴.
if(!accessToken || !verifyJwt(accessToken)) {
return new Response(JSON.stringify({"result":"No Authorization"}))
}
try {
const res = await axios.post(`${process.env.MODEL_URL}/advice/`,{
text: body.text
});
const result = res.data
return NextResponse.json(result.result)
} catch (err) {
console.log(err)
return NextResponse.json(err)
}
};
import axios from 'axios';
import { NextResponse } from 'next/server';
import { verifyJwt } from '@/app/lib/jwt';
interface reqBody {
text: string;
}
type resBody = {
result: string;
}
export const POST = async(req: Request) => {
const body: reqBody = await req.json();
const accessToken = req.headers.get('Authorization')?.split('Bearer ')[1] as string;
// 헤더에 토큰이 없거나, 토큰 복호화 실패하면 리턴.
if(!accessToken || !verifyJwt(accessToken)) {
return new NextResponse(JSON.stringify({"result":"No Authorization"}), { status: 401, headers: { 'Content-Type': 'application/json' } })
}
try {
const res = await axios.post(`${process.env.MODEL_URL}/advice/`,{
text: body.text
});
const result = res.data
return NextResponse.json(result.result)
} catch (err) {
// Log the error for server-side inspection
console.error(err);
// Return a generic error message to the client
return new NextResponse(JSON.stringify({"result":"An error occurred"}), { status: 500, headers: { 'Content-Type': 'application/json' } })
}
};

3 changes: 2 additions & 1 deletion src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const handler = NextAuth({
if(result.user_id) {
return result;
}
throw new Error(result);
throw new Error(result.result);
}
}),
KakaoProvider({
Expand All @@ -53,6 +53,7 @@ const handler = NextAuth({
account.refresh_token = result.refreshToken
return true;
} else {
// 로컬 로그인 처리 쪽인데 어떻게 할까.
return true;
}
},
Expand Down
174 changes: 174 additions & 0 deletions src/app/api/diary/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { NextRequest, NextResponse } from "next/server";
import axios from 'axios';
import queryPromise from "@/app/lib/db";
export const api = {
bodyParse: false
};

// POST 작성
// PATCH 수정
// DELETE 삭제

export async function POST( req: NextRequest, res: NextResponse ) {
const data = await req.formData();
const accessToken = req.headers.get('Authorization')?.split('mlru ')[1] as string;
const title = data.get('title') as string;
const content = data.get('content') as string;
const weather = data.get('weather') as string;
const id = data.get('id') as string;
const name = data.get('name') as string;
const imgTit = data.get('imgTit') as string;


const predictEmo = await axios.post(
`${process.env.BASE_URL}/api/emotion`,
{text: content},
{
headers: {
'Authorization': `mlru ${accessToken}`
}
}
);
console.log(predictEmo.data); // 감정 숫자.

const maxEmotion = Object.entries(predictEmo.data).reduce((max: any, [key, value]: any) => {
return value > max[1] ? [key, value] : max;
}, ['', -Infinity]);

const predictSumm = await axios.post(
`${process.env.BASE_URL}/api/summary`,
{text: content},
{
headers: {
'Authorization': `mlru ${accessToken}`
}
}
);

console.log(predictSumm.data); // 내용 요약.

const predictAdvice = await axios.post(
`${process.env.BASE_URL}/api/advice`,
{text:predictSumm.data},
{
headers: {
'Authorization': `mlru ${accessToken}`
}
}
);
console.log(predictAdvice.data); // 조언

const weatherQuery: { [key: string]: string } = {
"맑음": "sunny",
"흐림":"cloudy",
"비":"rainy",
"바람":"windy",
"눈":"snowy"
};
const emotionQuery: { [key: string]: string } = {
"중립": "normal",
"슬픔": "sadness",
"분노": "angry",
"놀람": "amazing",
"행복": "happiness",
"불안": "unhappiness"
};
const query = 'weather is ' + weatherQuery[weather] + `, feel ${emotionQuery[maxEmotion[0]]} in the picture`;
let imgSrc = [];
const predictImg = await axios.post(
`${process.env.BASE_URL}/api/img`,
{text: query},
{
headers: {
'Authorization': `mlru ${accessToken}`
}
}
);
imgSrc.push(predictImg.data.result);
const img = data.get('img') as File;
if(img) {
const fb = new FormData();
fb.append('image', img);
const result = await axios.post(
'https://api.imgur.com/3/upload',
fb,
{
headers: {
'Authorization': `Client-ID ${process.env.IMGUR_KEY}`,
'Accept': 'application/json'
}
}
);
imgSrc.push(result.data.data.link);
}

try {
let sql = 'INSERT INTO tb_diary VALUES(?,?,?,?,?,?,?,?,?,?,?,?)';
let values = [
null,
id,
name,
title,
content,
predictEmo.data,
weather,
'pretendard',
null,
predictAdvice.data,
new Date(),
new Date()
];
const result = await queryPromise(sql, values);
if(img) {
sql = 'INSERT INTO tb_image VALUES(?,?,?,?,?,?)';
values = [null, result.insertId, imgTit, imgSrc, 'user', new Date()];
const done = await queryPromise(sql, values);
}
return NextResponse.json({result:'done'});
} catch (err) {
console.log(err);
return NextResponse.json({result:'error'})
Comment on lines +12 to +130
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The POST function is handling multiple responsibilities: parsing form data, making API calls, and database operations. Consider refactoring to separate concerns for better maintainability and testability.
  • The use of console.log for debugging is found in the code. It's better to replace these with a proper logging mechanism that can be toggled for production and development environments.
  • The error handling in the try-catch block is very generic. It would be beneficial to log specific error messages and potentially return different HTTP status codes based on the error type.
  • The authorization header is being manually split and the token is extracted. This could be refactored into a middleware or helper function to avoid code duplication and potential errors.
  • The imgSrc array is being used to collect image URLs, but there is no check to ensure that the URLs are valid or that the image upload was successful before inserting into the database.
  • The database query strings are hardcoded within the function. It's a good practice to abstract these into a repository layer or use an ORM for better security and maintainability.
  • The weatherQuery and emotionQuery objects are used to map values, but there is no check to ensure that the provided weather and emotion values are valid keys in these objects, which could lead to undefined behavior.

}
};

export const PUT = async(req: Request) => {
const data = await req.formData();
const images: any[] = [];
data.forEach((v, k) => {
images.push(v);
})
const imgs: any[] = [];

const uploadImages = async () => {
for (const v of images) {
const fb = new FormData();
fb.append('image', v);

try {
const result = await axios.post(
'https://api.imgur.com/3/upload',
fb,
{
headers: {
'Authorization': `Client-ID ${process.env.IMGUR_KEY}`,
'Accept': 'application/json'
}
}
);
imgs.push(result.data.data.link);
} catch (error) {
console.log(error);
return 0;
}
}
return imgs;
};

const result = await uploadImages();
if(result === 0) {
return NextResponse.json({result:'error'});
}
console.log(result);

return NextResponse.json({result:imgs})
}
Comment on lines +134 to +174
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The PUT function is designed to upload multiple images to Imgur, but there is no check to ensure that the images are valid or that the upload was successful before returning the URLs.
  • The function uploadImages is defined within the PUT function, which could be extracted to a separate utility function for reusability and clarity.
  • The error handling in the uploadImages function returns 0 in case of an error, which is not a standard way to handle errors in asynchronous functions. It would be better to throw an error and handle it appropriately.
  • The FormData object is being used without checking if the incoming data contains files, which could lead to errors if the data is not as expected.
  • The console.log statements should be replaced with a proper logging mechanism.

4 changes: 2 additions & 2 deletions src/app/api/emotion/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { verifyJwt } from '@/app/lib/jwt';


type reqBody = {
text: string;
text: string[];
}
type resBody = {
result: string;
Comment on lines 4 to 10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [26-29]

Error handling in the catch block could be improved by returning a more informative error response to the client. Currently, it returns the error object directly, which might expose sensitive information or be uninformative. Consider returning a standardized error message and logging the detailed error server-side.

-        console.log(err)
-        return NextResponse.json(err)
+        console.error(err);
+        return new Response(JSON.stringify({ "error": "An error occurred while processing your request." }), { status: 500 });

Expand All @@ -19,7 +19,7 @@ export async function POST (req: Request) {
return new Response(JSON.stringify({"result":"No Authorization"}))
}
try {
const res = await axios.post(`http://127.0.0.1:8000/predict/emotion/`,{
const res = await axios.post(`${process.env.MODEL_URL}/emotion/`,{
text: body.text
});
const result: resBody = res.data
Expand Down
8 changes: 4 additions & 4 deletions src/app/api/login/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { signJwtAccessToken, signJwtRefreshToken } from '@/app/lib/jwt'
import queryPromise from '@/app/lib/db'
import { NextResponse } from 'next/server';
const crypto = require('crypto');

interface reqBody {
Expand All @@ -10,9 +11,8 @@ interface reqBody {
export const POST = async(req: Request) => {
const body: reqBody = await req.json();
let sql = 'SELECT user_id, user_password, user_name, user_salt FROM tb_user WHERE user_id = ?';
let values = [body.username];
let result = await queryPromise(sql, values);
if(result.length < 1) return new Response(JSON.stringify({"result":"no user"}));
let result = await queryPromise(sql, [body.username]);
if(result.length < 1) return NextResponse.json({result:'아이디가 없습니다.'})
const hashPassword = crypto.createHash('sha512').update(body.password + result[0].user_salt).digest('hex');
const chk = hashPassword === result[0].user_password;
if(chk) {
Expand All @@ -28,5 +28,5 @@ export const POST = async(req: Request) => {
refreshToken
};
return new Response(JSON.stringify(rst))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The response for a successful login attempt should also use NextResponse.json for consistency with the error handling responses.

-        return new Response(JSON.stringify(rst))
+        return NextResponse.json(rst)

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
return new Response(JSON.stringify(rst))
return NextResponse.json(rst)

} else return new Response("Wrong Password");
} else return NextResponse.json({result:'비밀번호가 일치하지 않습니다.'})
}
2 changes: 1 addition & 1 deletion src/app/api/summary/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export async function POST (req: Request) {
return new Response(JSON.stringify({"result":"No Authorization"}))
}
try {
const res = await axios.post(`http://127.0.0.1:8000/predict/summary/`,{
const res = await axios.post(`${process.env.MODEL_URL}/summary/`,{
text: body.text
});
const result: resBody = res.data
Expand Down
6 changes: 3 additions & 3 deletions src/app/api/test/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export async function POST(req: Request) {
`;

// 쿼리 실행
await queryPromise(sql, [user_id, emotion_img, user_pw, user_name, provider]);

const result = await queryPromise(sql, [user_id, emotion_img, user_pw, user_name, provider]);
console.log('test------------------------')
// 성공적인 응답 반환
const result = { emotion_img };
console.log(result);

// NextResponse 생성자를 사용하여 응답 반환
return new NextResponse(JSON.stringify(result), {
Comment on lines 24 to 33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code correctly logs the result of the database query. However, it's generally not a good practice to leave console logs in production code as it can lead to unintentional information disclosure and clutter the server logs. Consider removing the console log or using a more sophisticated logging mechanism that can be toggled for development and production environments.

- console.log('test------------------------')
- console.log(result);

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
`;
// 쿼리 실행
await queryPromise(sql, [user_id, emotion_img, user_pw, user_name, provider]);
const result = await queryPromise(sql, [user_id, emotion_img, user_pw, user_name, provider]);
console.log('test------------------------')
// 성공적인 응답 반환
const result = { emotion_img };
console.log(result);
// NextResponse 생성자를 사용하여 응답 반환
return new NextResponse(JSON.stringify(result), {
`;
// 쿼리 실행
const result = await queryPromise(sql, [user_id, emotion_img, user_pw, user_name, provider]);
// 성공적인 응답 반환
// NextResponse 생성자를 사용하여 응답 반환
return new NextResponse(JSON.stringify(result), {

Expand Down
2 changes: 1 addition & 1 deletion src/app/api/trans/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export async function POST (req: Request) {
return new Response(JSON.stringify({"result":"No Authorization"}))
}
try {
const res = await axios.post(`http://127.0.0.1:8000/predict/trans/`,{
const res = await axios.post(`${process.env.MODEL_URL}/trans/`,{
text: body.text
});
const result: resBody = res.data
Expand Down
Loading