feat: Swagger/OpenAPI 문서화 시스템 구축
- Swagger 패키지 설치 및 설정: * swagger-jsdoc, swagger-ui-express 패키지 추가 * /api-docs 엔드포인트에서 Swagger UI 제공 * /api-docs.json 엔드포인트에서 JSON 스펙 제공 - 포괄적인 Swagger 설정 파일 생성: * config/swagger.js: OpenAPI 3.0 스펙 정의 * 공통 스키마 정의 (User, Worker, Project, Task, DailyWorkReport) * 표준 응답 스키마 (SuccessResponse, ErrorResponse, PaginatedResponse) * JWT Bearer 인증 스키마 설정 - API 문서화 적용: * Authentication API: 로그인 엔드포인트 문서화 * Workers API: 전체 CRUD 작업 문서화 * 상세한 요청/응답 스키마 및 예시 포함 * 에러 코드별 응답 정의 - Swagger UI 커스터마이징: * 브랜딩 및 UI 개선 * 인증 토큰 지속성 설정 * 필터링 및 탐색 기능 활성화 - 접근 방법: * http://localhost:20005/api-docs - Swagger UI * http://localhost:20005/api-docs.json - JSON 스펙
This commit is contained in:
497
api.hyungi.net/config/swagger.js
Normal file
497
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;
|
||||
@@ -9,6 +9,10 @@ const rateLimit = require('express-rate-limit');
|
||||
const { errorMiddleware } = require('./utils/errorHandler');
|
||||
const { responseMiddleware } = require('./utils/responseFormatter');
|
||||
|
||||
// Swagger 설정
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const swaggerSpec = require('./config/swagger');
|
||||
|
||||
const app = express();
|
||||
|
||||
// 헬스체크와 개발용 엔드포인트는 CORS 이후에 등록
|
||||
@@ -322,6 +326,27 @@ app.use('/api/tools', toolsRoute);
|
||||
// 📤 파일 업로드
|
||||
app.use('/api', uploadBgRoutes);
|
||||
|
||||
// ===== 📚 Swagger API 문서 =====
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
||||
explorer: true,
|
||||
customCss: '.swagger-ui .topbar { display: none }',
|
||||
customSiteTitle: 'TK Work Management API',
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
displayRequestDuration: true,
|
||||
docExpansion: 'none',
|
||||
filter: true,
|
||||
showExtensions: true,
|
||||
showCommonExtensions: true
|
||||
}
|
||||
}));
|
||||
|
||||
// Swagger JSON 스펙 제공
|
||||
app.get('/api-docs.json', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(swaggerSpec);
|
||||
});
|
||||
|
||||
// ===== 🚨 에러 핸들러 (모든 라우트 뒤에 위치) =====
|
||||
app.use(errorMiddleware);
|
||||
|
||||
|
||||
250
api.hyungi.net/package-lock.json
generated
250
api.hyungi.net/package-lock.json
generated
@@ -22,7 +22,53 @@
|
||||
"mysql2": "^3.14.1",
|
||||
"pm2": "^5.3.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"sqlite3": "^5.1.6"
|
||||
"sqlite3": "^5.1.6",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/json-schema-ref-parser": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
|
||||
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jsdevtools/ono": "^7.1.3",
|
||||
"@types/json-schema": "^7.0.6",
|
||||
"call-me-maybe": "^1.0.1",
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/openapi-schemas": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
|
||||
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/swagger-methods": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
|
||||
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@apidevtools/swagger-parser": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
|
||||
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apidevtools/json-schema-ref-parser": "^9.0.6",
|
||||
"@apidevtools/openapi-schemas": "^2.0.4",
|
||||
"@apidevtools/swagger-methods": "^3.0.2",
|
||||
"@jsdevtools/ono": "^7.1.3",
|
||||
"call-me-maybe": "^1.0.1",
|
||||
"z-schema": "^5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openapi-types": ">=7"
|
||||
}
|
||||
},
|
||||
"node_modules/@gar/promisify": {
|
||||
@@ -37,6 +83,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz",
|
||||
"integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="
|
||||
},
|
||||
"node_modules/@jsdevtools/ono": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@levischuck/tiny-cbor": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
|
||||
@@ -342,6 +394,13 @@
|
||||
"debug": "^4.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@scarf/scarf": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
||||
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@simplewebauthn/server": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.1.1.tgz",
|
||||
@@ -374,6 +433,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
|
||||
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
@@ -612,8 +677,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
@@ -748,7 +812,6 @@
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -892,6 +955,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-me-maybe": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
|
||||
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
@@ -1015,8 +1084,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "1.6.2",
|
||||
@@ -1235,6 +1303,18 @@
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"esutils": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||
@@ -1682,8 +1762,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
@@ -2037,7 +2116,6 @@
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
@@ -2267,6 +2345,13 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
|
||||
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
@@ -2277,6 +2362,13 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
@@ -2297,6 +2389,12 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"node_modules/lodash.mergewith": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
|
||||
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
@@ -2449,7 +2547,6 @@
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -2861,6 +2958,13 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/openapi-types": {
|
||||
"version": "12.1.3",
|
||||
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
|
||||
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
@@ -3012,7 +3116,6 @@
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4031,6 +4134,92 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-jsdoc": {
|
||||
"version": "6.2.8",
|
||||
"resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
|
||||
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "6.2.0",
|
||||
"doctrine": "3.0.0",
|
||||
"glob": "7.1.6",
|
||||
"lodash.mergewith": "^4.6.2",
|
||||
"swagger-parser": "^10.0.3",
|
||||
"yaml": "2.0.0-1"
|
||||
},
|
||||
"bin": {
|
||||
"swagger-jsdoc": "bin/swagger-jsdoc.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-jsdoc/node_modules/commander": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
|
||||
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-jsdoc/node_modules/glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-parser": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz",
|
||||
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "10.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-dist": {
|
||||
"version": "5.30.1",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.1.tgz",
|
||||
"integrity": "sha512-4mNAUM31sr52K3JcK9qiGbfsFKNh/dm3PkEe+F9FAM31YY/NoRYUgsR/L6d7LLFn6PgZXtBG2ygp8+7UnpUIPg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scarf/scarf": "=1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
|
||||
"integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"swagger-ui-dist": ">=5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= v0.10.32"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": ">=4.0.0 || >=5.0.0-beta"
|
||||
}
|
||||
},
|
||||
"node_modules/systeminformation": {
|
||||
"version": "5.27.1",
|
||||
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.27.1.tgz",
|
||||
@@ -4389,6 +4578,15 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.0.0-1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz",
|
||||
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
@@ -4421,6 +4619,36 @@
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/z-schema": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz",
|
||||
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"validator": "^13.7.0"
|
||||
},
|
||||
"bin": {
|
||||
"z-schema": "bin/z-schema"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"commander": "^9.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/z-schema/node_modules/commander": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
|
||||
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"mysql2": "^3.14.1",
|
||||
"pm2": "^5.3.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"sqlite3": "^5.1.6"
|
||||
"sqlite3": "^5.1.6",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Authentication
|
||||
* description: 사용자 인증 및 권한 관리 API
|
||||
*/
|
||||
|
||||
// routes/authRoutes.js - 비밀번호 변경 및 보안 기능 포함 완전판
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
@@ -71,7 +78,49 @@ const recordLoginHistory = async (connection, userId, success, ipAddress, userAg
|
||||
};
|
||||
|
||||
/**
|
||||
* 로그인 - DB 연동 (보안 강화)
|
||||
* @swagger
|
||||
* /api/auth/login:
|
||||
* post:
|
||||
* tags: [Authentication]
|
||||
* summary: 사용자 로그인
|
||||
* description: 사용자명과 비밀번호로 로그인하여 JWT 토큰을 발급받습니다.
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/LoginRequest'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 로그인 성공
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/LoginResponse'
|
||||
* 400:
|
||||
* description: 잘못된 요청 (사용자명 또는 비밀번호 누락)
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
* 401:
|
||||
* description: 인증 실패 (잘못된 사용자명 또는 비밀번호)
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
* 429:
|
||||
* description: 너무 많은 로그인 시도
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
* 500:
|
||||
* description: 서버 내부 오류
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
*/
|
||||
router.post('/login', authController.login);
|
||||
|
||||
|
||||
@@ -1,20 +1,228 @@
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Workers
|
||||
* description: 작업자 관리 API
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const workerController = require('../controllers/workerController');
|
||||
|
||||
// 작업자 생성
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workers:
|
||||
* post:
|
||||
* tags: [Workers]
|
||||
* summary: 작업자 생성
|
||||
* description: 새로운 작업자를 생성합니다.
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - worker_name
|
||||
* properties:
|
||||
* worker_name:
|
||||
* type: string
|
||||
* example: "김철수"
|
||||
* position:
|
||||
* type: string
|
||||
* example: "용접공"
|
||||
* department:
|
||||
* type: string
|
||||
* example: "생산부"
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "010-1234-5678"
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* example: "worker@technicalkorea.com"
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 작업자 생성 성공
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/SuccessResponse'
|
||||
* 400:
|
||||
* description: 잘못된 요청
|
||||
* 401:
|
||||
* description: 인증 필요
|
||||
* 500:
|
||||
* description: 서버 오류
|
||||
* get:
|
||||
* tags: [Workers]
|
||||
* 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: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Worker'
|
||||
* meta:
|
||||
* type: object
|
||||
* properties:
|
||||
* count:
|
||||
* type: integer
|
||||
* example: 10
|
||||
* 401:
|
||||
* description: 인증 필요
|
||||
* 500:
|
||||
* description: 서버 오류
|
||||
*/
|
||||
router.post('/', workerController.createWorker);
|
||||
|
||||
// 전체 작업자 조회
|
||||
router.get('/', workerController.getAllWorkers);
|
||||
|
||||
// 특정 작업자 조회
|
||||
/**
|
||||
* @swagger
|
||||
* /api/workers/{worker_id}:
|
||||
* get:
|
||||
* tags: [Workers]
|
||||
* summary: 특정 작업자 조회
|
||||
* description: ID로 특정 작업자 정보를 조회합니다.
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: worker_id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 작업자 ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 작업자 조회 성공
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* example: true
|
||||
* message:
|
||||
* type: string
|
||||
* example: "작업자 조회 성공"
|
||||
* data:
|
||||
* $ref: '#/components/schemas/Worker'
|
||||
* 400:
|
||||
* description: 잘못된 작업자 ID
|
||||
* 401:
|
||||
* description: 인증 필요
|
||||
* 404:
|
||||
* description: 작업자를 찾을 수 없음
|
||||
* 500:
|
||||
* description: 서버 오류
|
||||
* put:
|
||||
* tags: [Workers]
|
||||
* summary: 작업자 정보 수정
|
||||
* description: 작업자 정보를 수정합니다.
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: worker_id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 작업자 ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* worker_name:
|
||||
* type: string
|
||||
* example: "김철수"
|
||||
* position:
|
||||
* type: string
|
||||
* example: "용접공"
|
||||
* department:
|
||||
* type: string
|
||||
* example: "생산부"
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "010-1234-5678"
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* example: "worker@technicalkorea.com"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 작업자 수정 성공
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/SuccessResponse'
|
||||
* 400:
|
||||
* description: 잘못된 요청
|
||||
* 401:
|
||||
* description: 인증 필요
|
||||
* 404:
|
||||
* description: 작업자를 찾을 수 없음
|
||||
* 500:
|
||||
* description: 서버 오류
|
||||
* delete:
|
||||
* tags: [Workers]
|
||||
* summary: 작업자 삭제
|
||||
* description: 작업자를 삭제합니다.
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: worker_id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 작업자 ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 작업자 삭제 성공
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* example: true
|
||||
* message:
|
||||
* type: string
|
||||
* example: "작업자가 성공적으로 삭제되었습니다."
|
||||
* 400:
|
||||
* description: 잘못된 작업자 ID
|
||||
* 401:
|
||||
* description: 인증 필요
|
||||
* 404:
|
||||
* description: 작업자를 찾을 수 없음
|
||||
* 500:
|
||||
* description: 서버 오류
|
||||
*/
|
||||
router.get('/:worker_id', workerController.getWorkerById);
|
||||
|
||||
// 작업자 업데이트
|
||||
router.put('/:worker_id', workerController.updateWorker);
|
||||
|
||||
// 작업자 삭제
|
||||
router.delete('/:worker_id', workerController.removeWorker);
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user