import { query } from '../../../utils/db' import { requireAuth } from '../../../utils/session' /** * 프로젝트 커밋 목록 조회 * GET /api/project/[id]/commits * * Query params: * - startDate: 시작일 (YYYY-MM-DD) * - endDate: 종료일 (YYYY-MM-DD) * - repoId: 저장소 ID (옵션) * - authorId: 작성자 ID (옵션) * - page: 페이지 번호 (기본 1) * - limit: 페이지당 개수 (기본 50) */ export default defineEventHandler(async (event) => { await requireAuth(event) const projectId = parseInt(getRouterParam(event, 'id') || '0') const queryParams = getQuery(event) if (!projectId) { throw createError({ statusCode: 400, message: '프로젝트 ID가 필요합니다.' }) } const startDate = queryParams.startDate as string const endDate = queryParams.endDate as string const repoId = queryParams.repoId ? parseInt(queryParams.repoId as string) : null const authorId = queryParams.authorId ? parseInt(queryParams.authorId as string) : null const page = parseInt(queryParams.page as string) || 1 const limit = Math.min(parseInt(queryParams.limit as string) || 50, 100) const offset = (page - 1) * limit // 조건 빌드 const conditions = ['r.project_id = $1'] const values: any[] = [projectId] let paramIndex = 2 if (startDate) { conditions.push(`c.commit_date >= $${paramIndex++}`) values.push(startDate) } if (endDate) { conditions.push(`c.commit_date <= $${paramIndex++}::date + INTERVAL '1 day'`) values.push(endDate) } if (repoId) { conditions.push(`c.repo_id = $${paramIndex++}`) values.push(repoId) } if (authorId) { conditions.push(`c.employee_id = $${paramIndex++}`) values.push(authorId) } const whereClause = conditions.join(' AND ') // 커밋 목록 조회 const commits = await query(` SELECT c.commit_id, c.commit_hash, c.commit_message, c.commit_author, c.commit_email, c.commit_date, c.employee_id, c.files_changed, c.insertions, c.deletions, r.repo_id, r.repo_name, r.repo_path, s.server_type, s.server_name, e.employee_name, e.display_name FROM wr_commit_log c JOIN wr_repository r ON c.repo_id = r.repo_id JOIN wr_vcs_server s ON r.server_id = s.server_id LEFT JOIN wr_employee_info e ON c.employee_id = e.employee_id WHERE ${whereClause} ORDER BY c.commit_date DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++} `, [...values, limit, offset]) // 전체 개수 조회 const countResult = await query(` SELECT COUNT(*) as total FROM wr_commit_log c JOIN wr_repository r ON c.repo_id = r.repo_id WHERE ${whereClause} `, values) const total = parseInt(countResult[0]?.total || '0') // 통계 (해당 기간) const statsResult = await query(` SELECT COUNT(*) as commit_count, COALESCE(SUM(c.insertions), 0) as total_insertions, COALESCE(SUM(c.deletions), 0) as total_deletions, COUNT(DISTINCT c.employee_id) as author_count FROM wr_commit_log c JOIN wr_repository r ON c.repo_id = r.repo_id WHERE ${whereClause} `, values) return { commits: commits.map(c => ({ commitId: c.commit_id, commitHash: c.commit_hash, commitMessage: c.commit_message, commitAuthor: c.commit_author, commitEmail: c.commit_email, commitDate: c.commit_date, employeeId: c.employee_id, employeeName: c.display_name || c.employee_name, filesChanged: c.files_changed, insertions: c.insertions, deletions: c.deletions, repoId: c.repo_id, repoName: c.repo_name, repoPath: c.repo_path, serverType: c.server_type, serverName: c.server_name })), pagination: { page, limit, total, totalPages: Math.ceil(total / limit) }, stats: { commitCount: parseInt(statsResult[0]?.commit_count || '0'), totalInsertions: parseInt(statsResult[0]?.total_insertions || '0'), totalDeletions: parseInt(statsResult[0]?.total_deletions || '0'), authorCount: parseInt(statsResult[0]?.author_count || '0') } } })