Skip to content
Snippets Groups Projects
Commit ddcc7df9 authored by Lee WooChang's avatar Lee WooChang
Browse files

feat: my API 추가

parent 127479f2
Branches
No related tags found
1 merge request!13my API 추가
Pipeline #10743 failed
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"crypto": "^1.0.1",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.21.1", "express": "^4.21.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
...@@ -884,6 +885,13 @@ ...@@ -884,6 +885,13 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/crypto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
"deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.",
"license": "ISC"
},
"node_modules/data-view-buffer": { "node_modules/data-view-buffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
}, },
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"crypto": "^1.0.1",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.21.1", "express": "^4.21.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
......
import jwt from 'jsonwebtoken';
const authMiddleware = (req, res, next) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(401).send({ message: '토큰이 필요합니다.' });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch {
res.status(401).send({ message: '유효하지 않은 토큰입니다.' });
}
};
export default authMiddleware;
import pool from '../db.js';
const myRepository = {
async getCombinationsByUserId(userId) {
const query = `
SELECT id, name
FROM combinations
WHERE owner_id = $1
`;
const values = [userId];
const { rows } = await pool.query(query, values);
return rows;
},
async getPartIdsByCombinationId(combinationId) {
const query = `
SELECT part_id
FROM relations
WHERE combination_id = $1
`;
const values = [combinationId];
const { rows } = await pool.query(query, values);
return rows.map((row) => row.part_id);
},
async saveTransaction(userId, transactionId) {
const query = `
INSERT INTO transactions (user_id, id, created_at, updated_at)
VALUES ($1, $2, NOW(), NOW())
`;
const values = [userId, transactionId];
await pool.query(query, values);
},
async getCombinationIdByTransactionId(transactionId) {
const query = `
SELECT combination_id
FROM Pcregister_Transection
WHERE transection_id = $1
`;
const values = [transactionId];
const { rows } = await pool.query(query, values);
return rows.length > 0 ? rows[0].combination_id : null;
},
};
export default myRepository;
import { Router } from 'express'; import { Router } from 'express';
import myService from '../services/myService.js';
import authMiddleware from '../middlewares/authMiddleware.js';
import { wrapAsync } from '../utils.js';
import { ReportableError } from '../errors.js';
const myRouter = Router(); const myRouter = Router();
myRouter.get('/', (req, res) => { myRouter.get(
return res.send({ message: 'mypage things' }); '/pc',
}); authMiddleware,
wrapAsync(async (req, res) => {
const userId = req.user?.userId;
if (!userId) {
throw new ReportableError(400, '유효한 사용자 정보가 없습니다.');
}
const data = await myService.getUserPCs(userId);
res.sendResponse('', 200, data);
})
);
myRouter.get(
'/registration-code',
authMiddleware,
wrapAsync(async (req, res) => {
const userId = req.user?.userId;
if (!userId) {
throw new ReportableError(400, '사용자 ID가 없습니다.');
}
const code = await myService.createRegistrationCode(userId);
res.sendResponse('', 200, code);
})
);
myRouter.get(
'/registration-code/:code',
authMiddleware,
wrapAsync(async (req, res) => {
const { code } = req.params;
if (!code) {
throw new ReportableError(400, '코드가 필요합니다.');
}
const data = await myService.getCombinationId(code);
res.sendResponse('', 200, data);
})
);
myRouter.post(
'/',
wrapAsync(async (req, res) => {
const authorizationHeader = req.headers['authorization'];
const code = authorizationHeader?.split(' ')[1];
if (!code) {
throw new ReportableError(401, '코드가 필요합니다.');
}
const { xml } = req.body;
if (!xml) {
throw new ReportableError(400, 'XML 데이터가 필요합니다.');
}
await myService.saveDocument(code, xml);
res.sendResponse('', 200, {});
})
);
export default myRouter; export default myRouter;
import { Redis } from '../redis.js';
import { ReportableError } from '../errors.js';
import myRepository from '../repositories/myRepository.js';
import crypto from 'crypto';
const myService = {
generateCode() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let code = '';
for (let i = 0; i < 12; i++) {
code += chars[Math.floor(Math.random() * chars.length)];
}
return code;
},
async getUserIdFromCode(code) {
const redisKey = `mypc:code:${code}:user_id`;
const userId = await Redis.get(redisKey);
if (!userId) {
throw new ReportableError(401, '유효하지 않은 코드입니다.');
}
return userId;
},
async saveDocument(code, xml) {
const redisKey = `mypc:code:${code}:document`;
const userIdKey = `mypc:code:${code}:user_id`;
const queueKey = 'mypc:queue';
const transactionIdKey = `mypc:code:${code}:transaction_id`;
const transactionId = crypto.randomBytes(6).toString('hex');
try {
const userId = await Redis.get(userIdKey);
if (!userId) {
throw new ReportableError(
404,
'해당 코드와 연결된 사용자 ID를 찾을 수 없습니다.'
);
}
await Redis.set(redisKey, xml, 'EX', 600);
await Redis.set(transactionIdKey, transactionId, 'EX', 600);
await Redis.lPush(queueKey, code);
await myRepository.saveTransaction(userId, transactionId);
} catch (err) {
throw new ReportableError(500, err.toString());
}
},
async createRegistrationCode(userId) {
if (!userId) {
throw new ReportableError(400, '사용자 ID가 필요합니다.');
}
const code = this.generateCode();
const redisKey = `mypc:code:${code}:user_id`;
const redisValue = userId;
try {
await Redis.set(redisKey, redisValue, 'EX', 600);
} catch {
throw new ReportableError(
500,
'등록 코드를 생성하는 중 문제가 발생했습니다.'
);
}
return { code };
},
async getCombinationId(req, res) {
const { code } = req.params;
const transactionIdKey = `mypc:code:${code}:transectionId`;
try {
const transactionId = await Redis.get(transactionIdKey);
if (!transactionId) {
throw new ReportableError(
404,
'해당 코드와 연결된 트랜잭션 ID를 찾을 수 없습니다.'
);
}
const combinationId =
await myRepository.getCombinationIdByTransactionId(transactionId);
res.sendResponse('', 200, {
combinationId: combinationId || null,
});
} catch {
throw new ReportableError(
500,
'등록 코드 상태를 확인하는 중 문제가 발생했습니다.'
);
}
},
async getUserPCs(userId) {
if (!userId) {
throw new ReportableError(400, '유효한 사용자 ID가 필요합니다.');
}
const combinations = await myRepository.getCombinationsByUserId(userId);
const pcs = await Promise.all(
combinations.map(async (combination) => {
const partIds = await myRepository.getPartIdsByCombinationId(
combination?.id
);
return {
id: combination.id,
name: combination.name,
parts: partIds,
};
})
);
return pcs;
},
};
export default myService;
...@@ -472,6 +472,11 @@ cross-spawn@^7.0.5: ...@@ -472,6 +472,11 @@ cross-spawn@^7.0.5:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
crypto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz"
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
data-view-buffer@^1.0.1: data-view-buffer@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz" resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment