Files
TK-FB-Project/synology_deployment/api/routes/performanceRoutes.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

389 lines
12 KiB
JavaScript

/**
* @swagger
* tags:
* name: Performance
* description: 성능 모니터링 및 최적화 API
*/
const express = require('express');
const router = express.Router();
const { asyncHandler } = require('../utils/errorHandler');
const cache = require('../utils/cache');
const { getPerformanceStats, suggestIndexes, analyzeQuery } = require('../utils/queryOptimizer');
/**
* @swagger
* /api/performance/cache/stats:
* get:
* tags: [Performance]
* summary: 캐시 통계 조회
* description: 현재 캐시 시스템의 상태와 통계를 조회합니다.
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 캐시 통계 조회 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "캐시 통계 조회 성공"
* data:
* type: object
* properties:
* type:
* type: string
* example: "memory"
* connected:
* type: boolean
* example: true
* keys:
* type: integer
* example: 42
* hits:
* type: integer
* example: 150
* misses:
* type: integer
* example: 25
* hitRate:
* type: number
* example: 0.857
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/cache/stats', asyncHandler(async (req, res) => {
const stats = cache.getStats();
res.success(stats, '캐시 통계 조회 성공');
}));
/**
* @swagger
* /api/performance/cache/flush:
* post:
* tags: [Performance]
* summary: 캐시 초기화
* description: 모든 캐시 데이터를 삭제합니다. (관리자 전용)
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 캐시 초기화 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "캐시가 성공적으로 초기화되었습니다."
* 401:
* description: 인증 필요
* 403:
* description: 권한 부족
* 500:
* description: 서버 오류
*/
router.post('/cache/flush', asyncHandler(async (req, res) => {
// 관리자 권한 확인
if (req.user?.access_level !== 'admin' && req.user?.access_level !== 'system') {
return res.status(403).json({
success: false,
error: '캐시 초기화 권한이 없습니다.'
});
}
await cache.flush();
res.success(null, '캐시가 성공적으로 초기화되었습니다.');
}));
/**
* @swagger
* /api/performance/database/stats:
* get:
* tags: [Performance]
* summary: 데이터베이스 성능 통계
* description: 데이터베이스 연결 상태와 성능 지표를 조회합니다.
* security:
* - bearerAuth: []
* responses:
* 200:
* description: DB 성능 통계 조회 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "DB 성능 통계 조회 성공"
* data:
* type: object
* properties:
* connections:
* type: object
* properties:
* current:
* type: integer
* example: 5
* max:
* type: integer
* example: 151
* slowQueries:
* type: integer
* example: 0
* timestamp:
* type: string
* format: date-time
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/database/stats', asyncHandler(async (req, res) => {
const stats = await getPerformanceStats();
res.success(stats, 'DB 성능 통계 조회 성공');
}));
/**
* @swagger
* /api/performance/database/indexes/{tableName}:
* get:
* tags: [Performance]
* summary: 인덱스 최적화 제안
* description: 특정 테이블의 인덱스 상태를 분석하고 최적화 제안을 제공합니다.
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: tableName
* required: true
* schema:
* type: string
* description: 분석할 테이블명
* example: "workers"
* responses:
* 200:
* description: 인덱스 분석 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "인덱스 분석 완료"
* data:
* type: object
* properties:
* tableName:
* type: string
* example: "workers"
* currentIndexes:
* type: array
* items:
* type: object
* properties:
* name:
* type: string
* column:
* type: string
* unique:
* type: boolean
* suggestions:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* column:
* type: string
* reason:
* type: string
* sql:
* type: string
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/database/indexes/:tableName', asyncHandler(async (req, res) => {
const { tableName } = req.params;
const analysis = await suggestIndexes(tableName);
res.success(analysis, '인덱스 분석 완료');
}));
/**
* @swagger
* /api/performance/query/analyze:
* post:
* tags: [Performance]
* summary: 쿼리 성능 분석
* description: SQL 쿼리의 실행 계획과 성능을 분석합니다. (관리자 전용)
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - query
* properties:
* query:
* type: string
* example: "SELECT * FROM workers WHERE department = ?"
* params:
* type: array
* items:
* type: string
* example: ["생산부"]
* responses:
* 200:
* description: 쿼리 분석 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "쿼리 분석 완료"
* data:
* type: object
* properties:
* executionTime:
* type: integer
* example: 15
* explainResult:
* type: array
* items:
* type: object
* recommendations:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* message:
* type: string
* suggestion:
* type: string
* 401:
* description: 인증 필요
* 403:
* description: 권한 부족
* 500:
* description: 서버 오류
*/
router.post('/query/analyze', asyncHandler(async (req, res) => {
// 관리자 권한 확인
if (req.user?.access_level !== 'admin' && req.user?.access_level !== 'system') {
return res.status(403).json({
success: false,
error: '쿼리 분석 권한이 없습니다.'
});
}
const { query, params = [] } = req.body;
if (!query) {
return res.status(400).json({
success: false,
error: '분석할 쿼리가 필요합니다.'
});
}
const analysis = await analyzeQuery(query, params);
res.success(analysis, '쿼리 분석 완료');
}));
/**
* @swagger
* /api/performance/system/info:
* get:
* tags: [Performance]
* summary: 시스템 정보 조회
* description: 서버의 메모리, CPU, 업타임 등 시스템 정보를 조회합니다.
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 시스템 정보 조회 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "시스템 정보 조회 성공"
* data:
* type: object
* properties:
* uptime:
* type: number
* example: 3600.5
* memory:
* type: object
* properties:
* rss:
* type: integer
* heapTotal:
* type: integer
* heapUsed:
* type: integer
* external:
* type: integer
* nodeVersion:
* type: string
* example: "v18.17.0"
* platform:
* type: string
* example: "linux"
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/system/info', asyncHandler(async (req, res) => {
const systemInfo = {
uptime: process.uptime(),
memory: process.memoryUsage(),
nodeVersion: process.version,
platform: process.platform,
cpuUsage: process.cpuUsage(),
timestamp: new Date().toISOString()
};
res.success(systemInfo, '시스템 정보 조회 성공');
}));
module.exports = router;