merge: 원격 저장소 변경사항과 로컬 리팩토링 작업 병합

- WorkAnalysis.js 충돌 해결 (소문자 테이블명 유지)
- 원격 저장소의 새로운 마이그레이션 파일들과 통합
This commit is contained in:
Hyungi Ahn
2025-11-03 09:27:42 +09:00
18 changed files with 13952 additions and 12 deletions

63
README.md Normal file
View File

@@ -0,0 +1,63 @@
# TK-FB-Project - 통합 실행 가이드
## 🚀 한 번에 모든 서비스 실행
### 🎯 간편 실행 (권장)
```bash
cd /Users/hyungi/docker/TK-FB-Project
./start.sh
```
### 🛑 간편 중지
```bash
./stop.sh
```
### 📋 직접 실행
```bash
docker-compose up -d
docker-compose down
```
## 📊 서비스 목록
| 서비스 | 포트 | 접속 URL | 설명 |
|--------|------|----------|------|
| **웹 UI** | 20000 | http://localhost:20000 | 메인 웹 인터페이스 |
| **API 서버** | 20005 | http://localhost:20005 | Node.js API 서버 ✅ |
| **FastAPI 브릿지** | 20010 | http://localhost:20010 | Python FastAPI 서비스 |
| **phpMyAdmin** | 20080 | http://localhost:20080 | DB 관리도구 |
| **MariaDB** | 20306 | - | 데이터베이스 서버 |
## 🛠️ 관리 명령어
### 모든 서비스 중지
```bash
cd /Users/hyungi/docker/TK-FB-Project
docker-compose down
```
### 서비스 상태 확인
```bash
docker ps | grep fb_
```
### 로그 확인
```bash
docker-compose logs -f
```
## 💾 데이터베이스 정보
- **호스트**: localhost:20306
- **데이터베이스**: hyungi
- **사용자**: hyungi
- **비밀번호**: hyungi_password_2025
- **Root 비밀번호**: hyungi_root_password_2025
## ✨ 주요 개선사항
1. **통합 실행**: 한 번의 명령으로 모든 서비스 실행
2. **깔끔한 DB 초기화**: 마이그레이션 오류 해결
3. **일관된 네이밍**: fb_ 접두사로 컨테이너 구분
4. **안정적인 포트**: 20000번대 포트 사용

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,7 @@ class WorkAnalysis {
totalHours: parseFloat(stats.total_hours) || 0,
totalReports: parseInt(stats.total_reports) || 0,
activeProjects: parseInt(stats.active_projects) || 0,
activeWorkers: parseInt(stats.active_workers) || 0,
activeworkers: parseInt(stats.active_workers) || 0,
errorRate: parseFloat(errorRate.toFixed(2)) || 0,
avgHoursPerReport: parseFloat(stats.avg_hours_per_report) || 0
};
@@ -82,7 +82,7 @@ class WorkAnalysis {
SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as errorCount,
COUNT(DISTINCT dwr.report_date) as workingDays
FROM daily_work_reports dwr
LEFT JOIN Workers w ON dwr.worker_id = w.worker_id
LEFT JOIN workers w ON dwr.worker_id = w.worker_id
WHERE dwr.report_date BETWEEN ? AND ?
GROUP BY dwr.worker_id, w.worker_name
ORDER BY totalHours DESC
@@ -269,7 +269,7 @@ class WorkAnalysis {
totalHours: parseFloat(row.total_hours) || 0,
totalReports: parseInt(row.total_reports) || 0,
avgHours: parseFloat(row.avg_hours) || 0,
activeWorkers: parseInt(row.active_workers) || 0
activeworkers: parseInt(row.active_workers) || 0
}));
} catch (error) {
throw new Error(`요일별 패턴 분석 실패: ${error.message}`);
@@ -301,7 +301,7 @@ class WorkAnalysis {
error_type_name: row.error_type_name || `에러유형 ${row.error_type_id}`,
errorCount: parseInt(row.error_count) || 0,
totalHours: parseFloat(row.total_hours) || 0,
affectedWorkers: parseInt(row.affected_workers) || 0,
affectedworkers: parseInt(row.affected_workers) || 0,
affectedProjects: parseInt(row.affected_projects) || 0
}));
} catch (error) {
@@ -333,7 +333,7 @@ class WorkAnalysis {
monthName: row.month_name,
totalHours: parseFloat(row.total_hours) || 0,
totalReports: parseInt(row.total_reports) || 0,
activeWorkers: parseInt(row.active_workers) || 0,
activeworkers: parseInt(row.active_workers) || 0,
activeProjects: parseInt(row.active_projects) || 0,
errorCount: parseInt(row.error_count) || 0,
errorRate: row.total_reports > 0 ? parseFloat(((row.error_count / row.total_reports) * 100).toFixed(2)) : 0
@@ -362,7 +362,7 @@ class WorkAnalysis {
AND report_date BETWEEN ? AND ?
)) * 100, 2) as percentage
FROM daily_work_reports dwr
LEFT JOIN Workers w ON dwr.worker_id = w.worker_id
LEFT JOIN workers w ON dwr.worker_id = w.worker_id
LEFT JOIN work_types wt ON dwr.work_type_id = wt.id
LEFT JOIN Projects p ON dwr.project_id = p.project_id
WHERE dwr.report_date BETWEEN ? AND ?

View File

@@ -26,7 +26,7 @@ const getAnalysis = async (startDate, endDate) => {
const summarySql = `
SELECT
COUNT(DISTINCT dwr.project_id) as totalProjects,
COUNT(DISTINCT dwr.worker_id) as totalWorkers,
COUNT(DISTINCT dwr.worker_id) as totalworkers,
COUNT(DISTINCT dwr.task_id) as totalTasks,
SUM(${workHoursCalc}) as totalHours
FROM DailyWorkReports dwr
@@ -48,7 +48,7 @@ const getAnalysis = async (startDate, endDate) => {
const byWorkerSql = `
SELECT w.worker_name as name, SUM(${workHoursCalc}) as hours, COUNT(DISTINCT dwr.project_id) as participants
FROM DailyWorkReports dwr
JOIN Workers w ON dwr.worker_id = w.worker_id
JOIN workers w ON dwr.worker_id = w.worker_id
${whereClause}
GROUP BY w.worker_name
HAVING hours > 0
@@ -74,7 +74,7 @@ const getAnalysis = async (startDate, endDate) => {
(${workHoursCalc}) as work_hours, dwr.memo
FROM DailyWorkReports dwr
JOIN Projects p ON dwr.project_id = p.project_id
JOIN Workers w ON dwr.worker_id = w.worker_id
JOIN workers w ON dwr.worker_id = w.worker_id
JOIN Tasks t ON dwr.task_id = t.task_id
${whereClause}
HAVING work_hours > 0

View File

@@ -46,7 +46,7 @@ const getAllByDate = async (date) => {
d.id, d.date, w.worker_name, p.project_name, d.start_time, d.end_time,
t.category, t.subcategory, d.description
FROM DailyIssueReports d
LEFT JOIN Workers w ON d.worker_id = w.worker_id
LEFT JOIN workers w ON d.worker_id = w.worker_id
LEFT JOIN Projects p ON d.project_id = p.project_id
LEFT JOIN IssueTypes t ON d.issue_type_id = t.issue_type_id
WHERE d.date = ?

View File

@@ -90,7 +90,7 @@ const create = async (report, callback) => {
wr.work_details,
wr.memo
FROM WorkReports wr
LEFT JOIN Workers w ON wr.worker_id = w.worker_id
LEFT JOIN workers w ON wr.worker_id = w.worker_id
LEFT JOIN Projects p ON wr.project_id = p.project_id
LEFT JOIN Tasks t ON wr.task_id = t.task_id
WHERE wr.\`date\` = ?

116
docker-compose.yml Normal file
View File

@@ -0,0 +1,116 @@
version: "3.8"
services:
# MariaDB 데이터베이스
db:
image: mariadb:10.9
container_name: fb_db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=hyungi_root_password_2025
- MYSQL_DATABASE=hyungi
- MYSQL_USER=hyungi
- MYSQL_PASSWORD=hyungi_password_2025
volumes:
- db_data:/var/lib/mysql
- ./api.hyungi.net/migrations:/docker-entrypoint-initdb.d
ports:
- "20306:3306"
networks:
- fb_network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
# API 서버 (Node.js)
api:
build:
context: ./api.hyungi.net
dockerfile: Dockerfile
container_name: fb_api
depends_on:
db:
condition: service_healthy
restart: unless-stopped
ports:
- "20005:20005"
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_NAME=hyungi
- DB_USER=hyungi
- DB_PASSWORD=hyungi_password_2025
- DB_ROOT_PASSWORD=hyungi_root_password_2025
volumes:
- ./api.hyungi.net/public/img:/usr/src/app/public/img:ro
- ./api.hyungi.net/uploads:/usr/src/app/uploads
- ./api.hyungi.net/logs:/usr/src/app/logs
networks:
- fb_network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# 웹 UI (Nginx)
web-ui:
build:
context: ./web-ui
dockerfile: Dockerfile
container_name: fb_web_ui
restart: unless-stopped
ports:
- "20000:80"
volumes:
- ./web-ui:/usr/share/nginx/html:ro
networks:
- fb_network
depends_on:
- api
# FastAPI 브릿지
fastapi-bridge:
build:
context: ./fastapi-bridge
dockerfile: Dockerfile
container_name: fb_fastapi_bridge
restart: unless-stopped
ports:
- "20010:8000"
environment:
- EXPRESS_API_URL=http://api:20005
- NODE_ENV=production
networks:
- fb_network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
# phpMyAdmin (DB 관리도구)
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
container_name: fb_phpmyadmin
depends_on:
- db
restart: unless-stopped
ports:
- "20080:80"
environment:
- PMA_HOST=db
- PMA_USER=root
- PMA_PASSWORD=hyungi_root_password_2025
- UPLOAD_LIMIT=50M
networks:
- fb_network
volumes:
db_data:
driver: local
networks:
fb_network:
driver: bridge

View File

@@ -7,7 +7,7 @@ from typing import List
class Settings:
# 기본 설정
FASTAPI_PORT: int = int(os.getenv("FASTAPI_PORT", "8000"))
EXPRESS_API_URL: str = os.getenv("EXPRESS_API_URL", "http://localhost:3005")
EXPRESS_API_URL: str = os.getenv("EXPRESS_API_URL", "http://api:20005")
REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379")
NODE_ENV: str = os.getenv("NODE_ENV", "development")

View File

@@ -0,0 +1,17 @@
version: "3.8"
services:
fastapi-bridge:
build:
context: .
dockerfile: Dockerfile
container_name: fastapi_bridge_hyungi
restart: unless-stopped
ports:
- "20010:8000"
networks:
- hyungi_network
networks:
hyungi_network:
external: true

4567
hyungi.sql Normal file

File diff suppressed because it is too large Load Diff

30
start.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# TK-FB-Project 통합 실행 스크립트
echo "🚀 TK-FB-Project 시작 중..."
echo "========================================"
# Docker Compose로 모든 서비스 실행
docker-compose up -d
# 잠시 대기
sleep 3
# 서비스 상태 확인
echo ""
echo "📊 서비스 상태 확인 중..."
echo "========================================"
docker ps | grep fb_
echo ""
echo "🌐 접속 URL 정보:"
echo "========================================"
echo "• 웹 UI: http://localhost:20000"
echo "• API 서버: http://localhost:20005"
echo "• FastAPI: http://localhost:20010"
echo "• phpMyAdmin: http://localhost:20080"
echo "• 데이터베이스: localhost:20306"
echo ""
echo "✅ 모든 서비스가 시작되었습니다!"
echo " 잠시 후 브라우저에서 http://localhost:20000 으로 접속하세요."

12
stop.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# TK-FB-Project 통합 중지 스크립트
echo "⏹️ TK-FB-Project 중지 중..."
echo "========================================"
# Docker Compose로 모든 서비스 중지
docker-compose down
echo ""
echo "✅ 모든 서비스가 중지되었습니다!"

1
test_push.txt Normal file
View File

@@ -0,0 +1 @@
# 맥미니 푸시 테스트 Wed Oct 15 17:50:39 KST 2025