Files
TK-BOM-Project/frontend/src/api.js
2025-10-15 13:54:46 +09:00

210 lines
5.6 KiB
JavaScript

import axios from 'axios';
import { logApiError } from './utils/errorLogger';
// 환경변수에서 API URL을 읽음 (Vite 기준)
// 프로덕션에서는 nginx 프록시를 통해 /api 경로 사용
const API_BASE_URL = '/api';
console.log('API Base URL:', API_BASE_URL);
console.log('Environment:', import.meta.env.MODE);
// axios 인스턴스 생성
export const api = axios.create({
baseURL: API_BASE_URL,
timeout: 30000, // 30초로 증가
headers: {
'Content-Type': 'application/json',
},
});
// 재시도 로직을 위한 설정
const MAX_RETRIES = 3;
const RETRY_DELAY = 1000; // 1초
// 요청 인터셉터: 토큰 자동 추가
api.interceptors.request.use(
config => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 재시도 함수
const retryRequest = async (config, retries = MAX_RETRIES) => {
try {
return await api(config);
} catch (error) {
if (retries > 0 && (error.code === 'ECONNABORTED' || error.response?.status >= 500)) {
console.log(`API 재시도 중... (${MAX_RETRIES - retries + 1}/${MAX_RETRIES})`);
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
return retryRequest(config, retries - 1);
}
throw error;
}
};
// 응답 인터셉터: 에러 처리 및 자동 로그아웃
api.interceptors.response.use(
response => response,
error => {
// 오류 로깅
const endpoint = error.config?.url;
const requestData = error.config?.data;
logApiError(error, endpoint, requestData);
console.error('API Error:', {
url: error.config?.url,
method: error.config?.method,
status: error.response?.status,
data: error.response?.data,
message: error.message
});
// 401/403 에러 시 자동 로그아웃
if (error.response?.status === 401 || error.response?.status === 403) {
const token = localStorage.getItem('access_token');
if (token) {
console.log('토큰이 유효하지 않습니다. 자동 로그아웃 처리합니다.');
localStorage.removeItem('access_token');
localStorage.removeItem('user_data');
// 페이지 새로고침으로 로그인 페이지로 이동
window.location.reload();
}
}
return Promise.reject(error);
}
);
// 예시: 파일 업로드 (multipart/form-data)
export function uploadFile(formData, options = {}) {
const config = {
method: 'post',
url: '/files/upload',
data: formData,
headers: { 'Content-Type': 'multipart/form-data' },
...options,
};
return retryRequest(config);
}
// 예시: 자재 목록 조회 (신버전 API 사용)
export function fetchMaterials(params) {
return api.get('/files/materials-v2', { params });
}
// 예시: 자재 요약 통계
export function fetchMaterialsSummary(params) {
return api.get('/files/materials/summary', { params });
}
// 파일 목록 조회
export function fetchFiles(params) {
return api.get('/files', { params });
}
// 파일 삭제
export function deleteFile(fileId) {
return api.delete(`/files/delete/${fileId}`);
}
// 예시: Job 목록 조회
export function fetchJobs(params) {
return api.get('/jobs/', { params });
}
// 예시: Job 생성
export function createJob(data) {
return api.post('/jobs/', data);
}
// 리비전 비교
export function compareRevisions(jobNo, filename, oldRevision, newRevision) {
return api.get('/files/materials/compare-revisions', {
params: {
job_no: jobNo,
filename: filename,
old_revision: oldRevision,
new_revision: newRevision
}
});
}
// 프로젝트 수정
export function updateProject(projectId, data) {
return api.put(`/projects/${projectId}`, data);
}
// 프로젝트 삭제
export function deleteProject(projectId) {
return api.delete(`/projects/${projectId}`);
}
// 스풀 관련 API
export function fetchProjectSpools(projectId) {
return api.get(`/spools/project/${projectId}/spools`);
}
export function validateSpoolIdentifier(identifier) {
return api.post('/spools/validate-identifier', { spool_identifier: identifier });
}
export function generateSpoolIdentifier(dwgName, areaNumber, spoolNumber) {
return api.post('/spools/generate-identifier', {
dwg_name: dwgName,
area_number: areaNumber,
spool_number: spoolNumber
});
}
// 자재 비교 관련 API
export function compareMaterialRevisions(jobNo, currentRevision, previousRevision = null, saveResult = true) {
return api.post('/materials/compare-revisions', null, {
params: {
job_no: jobNo,
current_revision: currentRevision,
previous_revision: previousRevision,
save_result: saveResult
}
});
}
export function getMaterialComparisonHistory(jobNo, limit = 10) {
return api.get('/materials/comparison-history', {
params: { job_no: jobNo, limit }
});
}
export function getMaterialInventoryStatus(jobNo, materialHash = null) {
return api.get('/materials/inventory-status', {
params: { job_no: jobNo, material_hash: materialHash }
});
}
export function confirmMaterialPurchase(jobNo, revision, confirmations, confirmedBy = 'user') {
return api.post('/materials/confirm-purchase', null, {
params: {
job_no: jobNo,
revision: revision,
confirmed_by: confirmedBy
},
data: confirmations
});
}
export function getMaterialPurchaseStatus(jobNo, revision = null, status = null) {
return api.get('/materials/purchase-status', {
params: { job_no: jobNo, revision, status }
});
}
// Default export for convenience
export default api;