fix: 캘린더 모달 중복 카드 문제 및 삭제 권한 개선
- monthly_worker_status 조회 시 GROUP BY로 중복 데이터 합산 - 작업보고서 삭제 권한을 그룹장 이상으로 제한 (admin, system, group_leader) - 중복 데이터 정리를 위한 마이그레이션 SQL 추가 (009_fix_duplicate_monthly_status.sql) - synology_deployment 버전에도 동일 수정 적용
This commit is contained in:
388
synology_deployment/api/routes/performanceRoutes.js
Normal file
388
synology_deployment/api/routes/performanceRoutes.js
Normal file
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* @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;
|
||||
Reference in New Issue
Block a user