feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가) - 출근/근태 시스템 개선 (연차 조회, 근무현황) - 작업분석 대분류 그룹화 및 마이그레이션 스크립트 - 모바일 네비게이션 UI 추가 - NAS 배포 도구 및 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
497
deploy/tkfb-package/api.hyungi.net/config/swagger.js
Normal file
497
deploy/tkfb-package/api.hyungi.net/config/swagger.js
Normal file
@@ -0,0 +1,497 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user