- 순찰/점검 기능 개선 (zone-detail 페이지 추가) - 출근/근태 시스템 개선 (연차 조회, 근무현황) - 작업분석 대분류 그룹화 및 마이그레이션 스크립트 - 모바일 네비게이션 UI 추가 - NAS 배포 도구 및 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
498 lines
13 KiB
JavaScript
498 lines
13 KiB
JavaScript
// config/swagger.js - Swagger/OpenAPI 설정
|
|
|
|
const swaggerJSDoc = require('swagger-jsdoc');
|
|
|
|
const swaggerDefinition = {
|
|
openapi: '3.0.0',
|
|
info: {
|
|
title: 'Technical Korea Work Management API',
|
|
version: '2.1.0',
|
|
description: '보안이 강화된 생산관리 시스템 API - 작업자, 프로젝트, 일일 작업 보고서 관리',
|
|
contact: {
|
|
name: 'Technical Korea',
|
|
email: 'admin@technicalkorea.com'
|
|
},
|
|
license: {
|
|
name: 'MIT',
|
|
url: 'https://opensource.org/licenses/MIT'
|
|
}
|
|
},
|
|
servers: [
|
|
{
|
|
url: 'http://localhost:20005',
|
|
description: '개발 서버 (Docker)'
|
|
},
|
|
{
|
|
url: 'http://localhost:3005',
|
|
description: '로컬 개발 서버'
|
|
}
|
|
],
|
|
components: {
|
|
securitySchemes: {
|
|
bearerAuth: {
|
|
type: 'http',
|
|
scheme: 'bearer',
|
|
bearerFormat: 'JWT',
|
|
description: 'JWT 토큰을 사용한 인증. 로그인 후 받은 토큰을 "Bearer {token}" 형식으로 입력하세요.'
|
|
}
|
|
},
|
|
schemas: {
|
|
// 공통 응답 스키마
|
|
SuccessResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
success: {
|
|
type: 'boolean',
|
|
example: true
|
|
},
|
|
message: {
|
|
type: 'string',
|
|
example: '요청이 성공적으로 처리되었습니다.'
|
|
},
|
|
data: {
|
|
type: 'object',
|
|
description: '응답 데이터'
|
|
},
|
|
timestamp: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
example: '2024-01-01T00:00:00.000Z'
|
|
}
|
|
}
|
|
},
|
|
ErrorResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
success: {
|
|
type: 'boolean',
|
|
example: false
|
|
},
|
|
error: {
|
|
type: 'string',
|
|
example: '오류 메시지'
|
|
},
|
|
timestamp: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
example: '2024-01-01T00:00:00.000Z'
|
|
}
|
|
}
|
|
},
|
|
PaginatedResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
success: {
|
|
type: 'boolean',
|
|
example: true
|
|
},
|
|
message: {
|
|
type: 'string',
|
|
example: '데이터 조회 성공'
|
|
},
|
|
data: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object'
|
|
}
|
|
},
|
|
meta: {
|
|
type: 'object',
|
|
properties: {
|
|
pagination: {
|
|
type: 'object',
|
|
properties: {
|
|
currentPage: { type: 'integer', example: 1 },
|
|
totalPages: { type: 'integer', example: 10 },
|
|
totalCount: { type: 'integer', example: 100 },
|
|
limit: { type: 'integer', example: 10 },
|
|
hasNextPage: { type: 'boolean', example: true },
|
|
hasPrevPage: { type: 'boolean', example: false }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
timestamp: {
|
|
type: 'string',
|
|
format: 'date-time'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 사용자 관련 스키마
|
|
User: {
|
|
type: 'object',
|
|
properties: {
|
|
user_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '사용자 ID'
|
|
},
|
|
username: {
|
|
type: 'string',
|
|
example: 'admin',
|
|
description: '사용자명'
|
|
},
|
|
name: {
|
|
type: 'string',
|
|
example: '관리자',
|
|
description: '실명'
|
|
},
|
|
email: {
|
|
type: 'string',
|
|
format: 'email',
|
|
example: 'admin@technicalkorea.com',
|
|
description: '이메일 주소'
|
|
},
|
|
role: {
|
|
type: 'string',
|
|
example: 'admin',
|
|
description: '역할'
|
|
},
|
|
access_level: {
|
|
type: 'string',
|
|
enum: ['user', 'admin', 'system'],
|
|
example: 'admin',
|
|
description: '접근 권한 레벨'
|
|
},
|
|
worker_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '연결된 작업자 ID'
|
|
},
|
|
is_active: {
|
|
type: 'boolean',
|
|
example: true,
|
|
description: '활성 상태'
|
|
},
|
|
last_login_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '마지막 로그인 시간'
|
|
},
|
|
created_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '생성 시간'
|
|
},
|
|
updated_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '수정 시간'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 작업자 관련 스키마
|
|
Worker: {
|
|
type: 'object',
|
|
properties: {
|
|
worker_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '작업자 ID'
|
|
},
|
|
worker_name: {
|
|
type: 'string',
|
|
example: '김철수',
|
|
description: '작업자 이름'
|
|
},
|
|
position: {
|
|
type: 'string',
|
|
example: '용접공',
|
|
description: '직책'
|
|
},
|
|
department: {
|
|
type: 'string',
|
|
example: '생산부',
|
|
description: '부서'
|
|
},
|
|
phone: {
|
|
type: 'string',
|
|
example: '010-1234-5678',
|
|
description: '전화번호'
|
|
},
|
|
email: {
|
|
type: 'string',
|
|
format: 'email',
|
|
example: 'worker@technicalkorea.com',
|
|
description: '이메일'
|
|
},
|
|
hire_date: {
|
|
type: 'string',
|
|
format: 'date',
|
|
example: '2024-01-01',
|
|
description: '입사일'
|
|
},
|
|
is_active: {
|
|
type: 'boolean',
|
|
example: true,
|
|
description: '활성 상태'
|
|
},
|
|
created_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '생성 시간'
|
|
},
|
|
updated_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '수정 시간'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 프로젝트 관련 스키마
|
|
Project: {
|
|
type: 'object',
|
|
properties: {
|
|
project_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '프로젝트 ID'
|
|
},
|
|
project_name: {
|
|
type: 'string',
|
|
example: '신규 플랜트 건설',
|
|
description: '프로젝트 이름'
|
|
},
|
|
description: {
|
|
type: 'string',
|
|
example: '대형 화학 플랜트 건설 프로젝트',
|
|
description: '프로젝트 설명'
|
|
},
|
|
start_date: {
|
|
type: 'string',
|
|
format: 'date',
|
|
example: '2024-01-01',
|
|
description: '시작일'
|
|
},
|
|
end_date: {
|
|
type: 'string',
|
|
format: 'date',
|
|
example: '2024-12-31',
|
|
description: '종료일'
|
|
},
|
|
status: {
|
|
type: 'string',
|
|
example: 'active',
|
|
description: '프로젝트 상태'
|
|
},
|
|
created_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '생성 시간'
|
|
},
|
|
updated_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '수정 시간'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 작업 관련 스키마
|
|
Task: {
|
|
type: 'object',
|
|
properties: {
|
|
task_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '작업 ID'
|
|
},
|
|
task_name: {
|
|
type: 'string',
|
|
example: '용접 작업',
|
|
description: '작업 이름'
|
|
},
|
|
description: {
|
|
type: 'string',
|
|
example: '파이프 용접 작업',
|
|
description: '작업 설명'
|
|
},
|
|
category: {
|
|
type: 'string',
|
|
example: '용접',
|
|
description: '작업 카테고리'
|
|
},
|
|
is_active: {
|
|
type: 'boolean',
|
|
example: true,
|
|
description: '활성 상태'
|
|
},
|
|
created_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '생성 시간'
|
|
},
|
|
updated_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '수정 시간'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 일일 작업 보고서 관련 스키마
|
|
DailyWorkReport: {
|
|
type: 'object',
|
|
properties: {
|
|
id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '보고서 ID'
|
|
},
|
|
report_date: {
|
|
type: 'string',
|
|
format: 'date',
|
|
example: '2024-01-01',
|
|
description: '작업 날짜'
|
|
},
|
|
worker_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '작업자 ID'
|
|
},
|
|
project_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '프로젝트 ID'
|
|
},
|
|
work_type_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '작업 유형 ID'
|
|
},
|
|
work_status_id: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '작업 상태 ID (1:정규, 2:에러)'
|
|
},
|
|
error_type_id: {
|
|
type: 'integer',
|
|
example: null,
|
|
description: '에러 유형 ID (에러일 때만)'
|
|
},
|
|
work_hours: {
|
|
type: 'number',
|
|
format: 'decimal',
|
|
example: 8.5,
|
|
description: '작업 시간'
|
|
},
|
|
created_by: {
|
|
type: 'integer',
|
|
example: 1,
|
|
description: '작성자 user_id'
|
|
},
|
|
created_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '생성 시간'
|
|
},
|
|
updated_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
description: '수정 시간'
|
|
},
|
|
// 조인된 데이터
|
|
worker_name: {
|
|
type: 'string',
|
|
example: '김철수',
|
|
description: '작업자 이름'
|
|
},
|
|
project_name: {
|
|
type: 'string',
|
|
example: '신규 플랜트 건설',
|
|
description: '프로젝트 이름'
|
|
},
|
|
work_type_name: {
|
|
type: 'string',
|
|
example: '용접',
|
|
description: '작업 유형 이름'
|
|
},
|
|
work_status_name: {
|
|
type: 'string',
|
|
example: '정규',
|
|
description: '작업 상태 이름'
|
|
},
|
|
error_type_name: {
|
|
type: 'string',
|
|
example: null,
|
|
description: '에러 유형 이름'
|
|
}
|
|
}
|
|
},
|
|
|
|
// 로그인 관련 스키마
|
|
LoginRequest: {
|
|
type: 'object',
|
|
required: ['username', 'password'],
|
|
properties: {
|
|
username: {
|
|
type: 'string',
|
|
example: 'admin',
|
|
description: '사용자명'
|
|
},
|
|
password: {
|
|
type: 'string',
|
|
example: 'password123',
|
|
description: '비밀번호'
|
|
}
|
|
}
|
|
},
|
|
LoginResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
success: {
|
|
type: 'boolean',
|
|
example: true
|
|
},
|
|
message: {
|
|
type: 'string',
|
|
example: '로그인 성공'
|
|
},
|
|
data: {
|
|
type: 'object',
|
|
properties: {
|
|
user: {
|
|
$ref: '#/components/schemas/User'
|
|
},
|
|
token: {
|
|
type: 'string',
|
|
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
description: 'JWT 토큰'
|
|
},
|
|
redirectUrl: {
|
|
type: 'string',
|
|
example: '/pages/dashboard/group-leader.html',
|
|
description: '리다이렉트 URL'
|
|
}
|
|
}
|
|
},
|
|
timestamp: {
|
|
type: 'string',
|
|
format: 'date-time'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
security: [
|
|
{
|
|
bearerAuth: []
|
|
}
|
|
]
|
|
};
|
|
|
|
const options = {
|
|
definition: swaggerDefinition,
|
|
apis: [
|
|
'./routes/*.js',
|
|
'./controllers/*.js',
|
|
'./index.js'
|
|
]
|
|
};
|
|
|
|
const swaggerSpec = swaggerJSDoc(options);
|
|
|
|
module.exports = swaggerSpec;
|