Files
TK-FB-Project/synology_deployment/api/utils/responseFormatter.js
Hyungi Ahn a9bce9d20b fix: 캘린더 모달 중복 카드 문제 및 삭제 권한 개선
- monthly_worker_status 조회 시 GROUP BY로 중복 데이터 합산
- 작업보고서 삭제 권한을 그룹장 이상으로 제한 (admin, system, group_leader)
- 중복 데이터 정리를 위한 마이그레이션 SQL 추가 (009_fix_duplicate_monthly_status.sql)
- synology_deployment 버전에도 동일 수정 적용
2025-12-02 13:08:44 +09:00

189 lines
4.2 KiB
JavaScript

// utils/responseFormatter.js - 통합 응답 포맷터
/**
* 성공 응답 포맷터
*/
const successResponse = (data = null, message = '요청이 성공적으로 처리되었습니다.', meta = null) => {
const response = {
success: true,
message,
timestamp: new Date().toISOString()
};
if (data !== null) {
response.data = data;
}
if (meta) {
response.meta = meta;
}
return response;
};
/**
* 페이지네이션 응답 포맷터
*/
const paginatedResponse = (data, totalCount, page = 1, limit = 10, message = '데이터 조회 성공') => {
const totalPages = Math.ceil(totalCount / limit);
return successResponse(data, message, {
pagination: {
currentPage: parseInt(page),
totalPages,
totalCount: parseInt(totalCount),
limit: parseInt(limit),
hasNextPage: page < totalPages,
hasPrevPage: page > 1
}
});
};
/**
* 리스트 응답 포맷터
*/
const listResponse = (items, message = '목록 조회 성공') => {
return successResponse(items, message, {
count: items.length
});
};
/**
* 생성 응답 포맷터
*/
const createdResponse = (data, message = '데이터가 성공적으로 생성되었습니다.') => {
return successResponse(data, message);
};
/**
* 업데이트 응답 포맷터
*/
const updatedResponse = (data = null, message = '데이터가 성공적으로 업데이트되었습니다.') => {
return successResponse(data, message);
};
/**
* 삭제 응답 포맷터
*/
const deletedResponse = (message = '데이터가 성공적으로 삭제되었습니다.') => {
return successResponse(null, message);
};
/**
* 통계 응답 포맷터
*/
const statsResponse = (stats, period = null, message = '통계 조회 성공') => {
const meta = {};
if (period) {
meta.period = period;
}
meta.generatedAt = new Date().toISOString();
return successResponse(stats, message, meta);
};
/**
* 인증 응답 포맷터
*/
const authResponse = (user, token, redirectUrl = null, message = '로그인 성공') => {
const data = {
user,
token
};
if (redirectUrl) {
data.redirectUrl = redirectUrl;
}
return successResponse(data, message);
};
/**
* 파일 업로드 응답 포맷터
*/
const uploadResponse = (fileInfo, message = '파일 업로드 성공') => {
return successResponse({
filename: fileInfo.filename,
originalName: fileInfo.originalname,
size: fileInfo.size,
mimetype: fileInfo.mimetype,
path: fileInfo.path,
uploadedAt: new Date().toISOString()
}, message);
};
/**
* 헬스체크 응답 포맷터
*/
const healthResponse = (status = 'healthy', services = {}) => {
return successResponse({
status,
services,
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.version
}, `서버 상태: ${status}`);
};
/**
* Express 응답 확장 미들웨어
*/
const responseMiddleware = (req, res, next) => {
// 성공 응답 헬퍼들을 res 객체에 추가
res.success = (data, message, meta) => {
return res.json(successResponse(data, message, meta));
};
res.paginated = (data, totalCount, page, limit, message) => {
return res.json(paginatedResponse(data, totalCount, page, limit, message));
};
res.list = (items, message) => {
return res.json(listResponse(items, message));
};
res.created = (data, message) => {
return res.status(201).json(createdResponse(data, message));
};
res.updated = (data, message) => {
return res.json(updatedResponse(data, message));
};
res.deleted = (message) => {
return res.json(deletedResponse(message));
};
res.stats = (stats, period, message) => {
return res.json(statsResponse(stats, period, message));
};
res.auth = (user, token, redirectUrl, message) => {
return res.json(authResponse(user, token, redirectUrl, message));
};
res.upload = (fileInfo, message) => {
return res.json(uploadResponse(fileInfo, message));
};
res.health = (status, services) => {
return res.json(healthResponse(status, services));
};
next();
};
module.exports = {
successResponse,
paginatedResponse,
listResponse,
createdResponse,
updatedResponse,
deletedResponse,
statsResponse,
authResponse,
uploadResponse,
healthResponse,
responseMiddleware
};