summaryrefslogtreecommitdiff
path: root/app/api/cron
diff options
context:
space:
mode:
Diffstat (limited to 'app/api/cron')
-rw-r--r--app/api/cron/form-tags/start/route.ts136
-rw-r--r--app/api/cron/form-tags/status/route.ts46
-rw-r--r--app/api/cron/forms/route.ts57
-rw-r--r--app/api/cron/forms/start/route.ts100
-rw-r--r--app/api/cron/forms/status/route.ts46
-rw-r--r--app/api/cron/object-classes/route.ts4
-rw-r--r--app/api/cron/projects/route.ts5
-rw-r--r--app/api/cron/tag-types/route.ts2
-rw-r--r--app/api/cron/tags/start/route.ts133
-rw-r--r--app/api/cron/tags/status/route.ts46
10 files changed, 567 insertions, 8 deletions
diff --git a/app/api/cron/form-tags/start/route.ts b/app/api/cron/form-tags/start/route.ts
new file mode 100644
index 00000000..6a029c4c
--- /dev/null
+++ b/app/api/cron/form-tags/start/route.ts
@@ -0,0 +1,136 @@
+// app/api/cron/tags/start/route.ts
+import { NextRequest } from 'next/server';
+import { v4 as uuidv4 } from 'uuid';
+import { revalidateTag } from 'next/cache';
+
+// 동기화 작업의 상태를 저장할 Map
+// 실제 프로덕션에서는 Redis 또는 DB에 저장하는 것이 좋습니다
+const syncJobs = new Map<string, {
+ status: 'queued' | 'processing' | 'completed' | 'failed';
+ startTime: Date;
+ endTime?: Date;
+ result?: any;
+ error?: string;
+ progress?: number;
+ projectCode?: string;
+ formCode?: string;
+ packageId?: number;
+}>();
+
+export async function POST(request: NextRequest) {
+ try {
+ // 요청 데이터 가져오기
+ let projectCode: string | undefined;
+ let formCode: string | undefined;
+ let packageId: number | undefined;
+
+
+ const body = await request.json();
+ projectCode = body.projectCode;
+ formCode = body.formCode;
+ packageId = body.contractItemId;
+
+
+ // 고유 ID 생성
+ const syncId = uuidv4();
+
+ // 작업 상태 초기화
+ syncJobs.set(syncId, {
+ status: 'queued',
+ startTime: new Date(),
+ formCode,
+ projectCode,
+ packageId
+
+ });
+
+ // 비동기 작업 시작 (백그라운드에서 실행)
+ processTagImport(syncId).catch(error => {
+ console.error('Background tag import job failed:', error);
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred'
+ });
+ });
+
+ // 즉시 응답 반환 (작업 ID 포함)
+ return Response.json({
+ success: true,
+ message: 'Tag import job started',
+ syncId
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Failed to start tag import job:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to start tag import job'
+ }, { status: 500 });
+ }
+}
+
+// 백그라운드에서 실행되는 태그 가져오기 작업
+async function processTagImport(syncId: string) {
+ try {
+ const jobInfo = syncJobs.get(syncId)!;
+ const formCode = jobInfo.formCode;
+ const projectCode = jobInfo.projectCode;
+ const packageId = jobInfo.packageId || 0;
+
+ // 상태 업데이트: 처리 중
+ syncJobs.set(syncId, {
+ ...jobInfo,
+ status: 'processing',
+ progress: 0,
+ });
+
+ if (!formCode || !projectCode ) {
+ throw new Error('formCode,projectCode is required');
+ }
+
+ // 여기서 실제 태그 가져오기 로직 import
+ const { importTagsFromSEDP } = await import('@/lib/sedp/get-form-tags');
+
+ // 진행 상황 업데이트를 위한 콜백 함수
+ const updateProgress = (progress: number) => {
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ progress
+ });
+ };
+
+ // 실제 태그 가져오기 실행
+ const result = await importTagsFromSEDP(formCode, projectCode, packageId, updateProgress);
+
+ // 명시적으로 캐시 무효화
+ revalidateTag(`forms-${packageId}`);
+
+ // 상태 업데이트: 완료
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'completed',
+ endTime: new Date(),
+ result,
+ progress: 100,
+ });
+
+ return result;
+ } catch (error: any) {
+ // 에러 발생 시 상태 업데이트
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred',
+ });
+
+ throw error; // 에러 다시 던지기
+ }
+}
+
+// 서버 메모리에 저장된 작업 상태 접근 함수 (다른 API에서 사용)
+export function getSyncJobStatus(id: string) {
+ return syncJobs.get(id);
+} \ No newline at end of file
diff --git a/app/api/cron/form-tags/status/route.ts b/app/api/cron/form-tags/status/route.ts
new file mode 100644
index 00000000..9d288f52
--- /dev/null
+++ b/app/api/cron/form-tags/status/route.ts
@@ -0,0 +1,46 @@
+// app/api/cron/tags/status/route.ts
+import { NextRequest } from 'next/server';
+import { getSyncJobStatus } from '../start/route';
+
+export async function GET(request: NextRequest) {
+ try {
+ // URL에서 작업 ID 가져오기
+ const searchParams = request.nextUrl.searchParams;
+ const syncId = searchParams.get('id');
+
+ if (!syncId) {
+ return Response.json({
+ success: false,
+ error: 'Missing sync ID parameter'
+ }, { status: 400 });
+ }
+
+ // 작업 상태 조회
+ const jobStatus = getSyncJobStatus(syncId);
+
+ if (!jobStatus) {
+ return Response.json({
+ success: false,
+ error: 'Sync job not found'
+ }, { status: 404 });
+ }
+
+ // 작업 상태 반환
+ return Response.json({
+ success: true,
+ status: jobStatus.status,
+ startTime: jobStatus.startTime,
+ endTime: jobStatus.endTime,
+ progress: jobStatus.progress,
+ result: jobStatus.result,
+ error: jobStatus.error
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Error retrieving tag import status:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to retrieve tag import status'
+ }, { status: 500 });
+ }
+} \ No newline at end of file
diff --git a/app/api/cron/forms/route.ts b/app/api/cron/forms/route.ts
index f58c146b..abe6753a 100644
--- a/app/api/cron/forms/route.ts
+++ b/app/api/cron/forms/route.ts
@@ -1,20 +1,65 @@
-// src/app/api/cron/tag-form-mappings/route.ts
+// app/api/cron/forms/route.ts
import { syncTagFormMappings } from '@/lib/sedp/sync-form';
import { NextRequest } from 'next/server';
+import { revalidateTag } from 'next/cache';
+
+// TypeScript에서 global 객체를 확장하기 위한 type 선언
+declare global {
+ var pendingTasks: Set<Promise<any>>;
+}
+
+// 글로벌 태스크 집합 초기화 (서버가 시작될 때만 한 번 실행됨)
+if (!global.pendingTasks) {
+ global.pendingTasks = new Set<Promise<any>>();
+}
+
+// 이 함수는 비동기 작업을 더 안전하게 처리하기 위한 도우미 함수입니다
+function runBackgroundTask<T>(task: Promise<T>, taskName: string): Promise<T> {
+ // 작업을 추적 세트에 추가
+ global.pendingTasks.add(task);
+
+ // finally 블록을 사용하여 작업이 완료될 때 세트에서 제거
+ task
+ .then(result => {
+ console.log(`Background task '${taskName}' completed successfully`);
+ return result;
+ })
+ .catch(error => {
+ console.error(`Background task '${taskName}' failed:`, error);
+ })
+ .finally(() => {
+ global.pendingTasks.delete(task);
+ });
+
+ return task;
+}
export async function GET(request: NextRequest) {
try {
console.log('태그 폼 매핑 동기화 API 호출됨:', new Date().toISOString());
- // syncTagFormMappings 함수 호출
- const result = await syncTagFormMappings();
+ // 비동기 작업을 생성하고 전역 객체에 저장
+ const syncTask = syncTagFormMappings()
+ .then(result => {
+ // 작업이 완료되면 캐시 무효화
+ revalidateTag('form-lists');
+ return result;
+ });
+
+ // 백그라운드에서 작업이 계속 실행되도록 보장
+ runBackgroundTask(syncTask, 'form-sync');
- // 성공 시 결과와 함께 200 OK 반환
- return Response.json({ success: true, result }, { status: 200 });
+ // 먼저 상태를 반환하고, 그 동안 백그라운드에서 작업 계속
+ return new Response(
+ JSON.stringify({
+ success: true,
+ message: 'Form sync started in background. This may take a while.'
+ }),
+ { status: 202, headers: { 'Content-Type': 'application/json' } }
+ );
} catch (error: any) {
console.error('태그 폼 매핑 동기화 API 에러:', error);
- // 에러 시에는 message를 담아 500 반환
const message = error.message || 'Something went wrong';
return Response.json({ success: false, error: message }, { status: 500 });
}
diff --git a/app/api/cron/forms/start/route.ts b/app/api/cron/forms/start/route.ts
new file mode 100644
index 00000000..a99c4677
--- /dev/null
+++ b/app/api/cron/forms/start/route.ts
@@ -0,0 +1,100 @@
+// app/api/cron/forms/start/route.ts
+import { NextRequest } from 'next/server';
+import { v4 as uuidv4 } from 'uuid';
+import { revalidateTag } from 'next/cache';
+
+// 동기화 작업의 상태를 저장할 Map
+// 실제 프로덕션에서는 Redis 또는 DB에 저장하는 것이 좋습니다
+const syncJobs = new Map<string, {
+ status: 'queued' | 'processing' | 'completed' | 'failed';
+ startTime: Date;
+ endTime?: Date;
+ result?: any;
+ error?: string;
+ progress?: number;
+}>();
+
+export async function POST(request: NextRequest) {
+ try {
+ // 고유 ID 생성
+ const syncId = uuidv4();
+
+ // 작업 상태 초기화
+ syncJobs.set(syncId, {
+ status: 'queued',
+ startTime: new Date(),
+ });
+
+ // 비동기 작업 시작 (백그라운드에서 실행)
+ processSyncJob(syncId).catch(error => {
+ console.error('Background sync job failed:', error);
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred'
+ });
+ });
+
+ // 즉시 응답 반환 (작업 ID 포함)
+ return Response.json({
+ success: true,
+ message: 'Form sync job started',
+ syncId
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Failed to start sync job:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to start sync job'
+ }, { status: 500 });
+ }
+}
+
+// 백그라운드에서 실행되는 동기화 작업
+async function processSyncJob(syncId: string) {
+ try {
+ // 상태 업데이트: 처리 중
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'processing',
+ progress: 0,
+ });
+
+ // 여기서 실제 동기화 로직 가져오기
+ const { syncTagFormMappings } = await import('@/lib/sedp/sync-form');
+
+ // 실제 동기화 작업 실행
+ const result = await syncTagFormMappings();
+
+ // 명시적으로 캐시 무효화 (동적 import 대신 상단에서 import)
+ revalidateTag('form-lists');
+
+ // 상태 업데이트: 완료
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'completed',
+ endTime: new Date(),
+ result,
+ progress: 100,
+ });
+
+ return result;
+ } catch (error: any) {
+ // 에러 발생 시 상태 업데이트
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred',
+ });
+
+ throw error; // 에러 다시 던지기
+ }
+}
+
+// 서버 메모리에 저장된 작업 상태 접근 함수 (다른 API에서 사용)
+export function getSyncJobStatus(id: string) {
+ return syncJobs.get(id);
+} \ No newline at end of file
diff --git a/app/api/cron/forms/status/route.ts b/app/api/cron/forms/status/route.ts
new file mode 100644
index 00000000..c0e27b2e
--- /dev/null
+++ b/app/api/cron/forms/status/route.ts
@@ -0,0 +1,46 @@
+// app/api/cron/forms/status/route.ts
+import { NextRequest } from 'next/server';
+import { getSyncJobStatus } from '../start/route';
+
+export async function GET(request: NextRequest) {
+ try {
+ // URL에서 작업 ID 가져오기
+ const searchParams = request.nextUrl.searchParams;
+ const syncId = searchParams.get('id');
+
+ if (!syncId) {
+ return Response.json({
+ success: false,
+ error: 'Missing sync ID parameter'
+ }, { status: 400 });
+ }
+
+ // 작업 상태 조회
+ const jobStatus = getSyncJobStatus(syncId);
+
+ if (!jobStatus) {
+ return Response.json({
+ success: false,
+ error: 'Sync job not found'
+ }, { status: 404 });
+ }
+
+ // 작업 상태 반환
+ return Response.json({
+ success: true,
+ status: jobStatus.status,
+ startTime: jobStatus.startTime,
+ endTime: jobStatus.endTime,
+ progress: jobStatus.progress,
+ result: jobStatus.result,
+ error: jobStatus.error
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Error retrieving sync status:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to retrieve sync status'
+ }, { status: 500 });
+ }
+} \ No newline at end of file
diff --git a/app/api/cron/object-classes/route.ts b/app/api/cron/object-classes/route.ts
index 9a574b1b..6743da70 100644
--- a/app/api/cron/object-classes/route.ts
+++ b/app/api/cron/object-classes/route.ts
@@ -1,6 +1,7 @@
// src/app/api/cron/object-classes/route.ts
import { syncObjectClasses } from '@/lib/sedp/sync-object-class';
import { NextRequest } from 'next/server';
+import { revalidateTag } from 'next/cache';
export async function GET(request: NextRequest) {
try {
@@ -8,7 +9,8 @@ export async function GET(request: NextRequest) {
// syncObjectClasses 함수 호출
const result = await syncObjectClasses();
-
+ revalidateTag("equip-class")
+
// 성공 시 결과와 함께 200 OK 반환
return Response.json({ success: true, result }, { status: 200 });
} catch (error: any) {
diff --git a/app/api/cron/projects/route.ts b/app/api/cron/projects/route.ts
index d8e6af51..12c89bdb 100644
--- a/app/api/cron/projects/route.ts
+++ b/app/api/cron/projects/route.ts
@@ -1,6 +1,7 @@
// src/app/api/cron/projects/route.ts
import { syncProjects } from '@/lib/sedp/sync-projects';
import { NextRequest } from 'next/server';
+import { revalidateTag } from 'next/cache';
export async function GET(request: NextRequest) {
try {
@@ -8,7 +9,9 @@ export async function GET(request: NextRequest) {
// syncProjects 함수 호출
const result = await syncProjects();
-
+
+ revalidateTag('project-lists')
+
// 성공 시 결과와 함께 200 OK 반환
return Response.json({ success: true, result }, { status: 200 });
} catch (error: any) {
diff --git a/app/api/cron/tag-types/route.ts b/app/api/cron/tag-types/route.ts
index 35145984..43644833 100644
--- a/app/api/cron/tag-types/route.ts
+++ b/app/api/cron/tag-types/route.ts
@@ -1,5 +1,6 @@
import { syncTagSubfields } from '@/lib/sedp/sync-tag-types';
import { NextRequest } from 'next/server';
+import { revalidateTag } from 'next/cache';
export async function GET(request: NextRequest) {
try {
@@ -7,6 +8,7 @@ export async function GET(request: NextRequest) {
// syncTagSubfields 함수 호출
const result = await syncTagSubfields();
+ revalidateTag('tag-numbering')
// 성공 시 결과와 함께 200 OK 반환
return Response.json({ success: true, result }, { status: 200 });
diff --git a/app/api/cron/tags/start/route.ts b/app/api/cron/tags/start/route.ts
new file mode 100644
index 00000000..b506b9a3
--- /dev/null
+++ b/app/api/cron/tags/start/route.ts
@@ -0,0 +1,133 @@
+// app/api/cron/tags/start/route.ts
+import { NextRequest } from 'next/server';
+import { v4 as uuidv4 } from 'uuid';
+import { revalidateTag } from 'next/cache';
+
+// 동기화 작업의 상태를 저장할 Map
+// 실제 프로덕션에서는 Redis 또는 DB에 저장하는 것이 좋습니다
+const syncJobs = new Map<string, {
+ status: 'queued' | 'processing' | 'completed' | 'failed';
+ startTime: Date;
+ endTime?: Date;
+ result?: any;
+ error?: string;
+ progress?: number;
+ packageId?: number;
+}>();
+
+export async function POST(request: NextRequest) {
+ try {
+ // 요청 데이터 가져오기
+ let packageId: number | undefined;
+
+ try {
+ const body = await request.json();
+ packageId = body.packageId;
+ } catch (error) {
+ // 요청 본문이 없거나 JSON이 아닌 경우, URL 파라미터 확인
+ const searchParams = request.nextUrl.searchParams;
+ const packageIdParam = searchParams.get('packageId');
+ if (packageIdParam) {
+ packageId = parseInt(packageIdParam, 10);
+ }
+ }
+
+ // 고유 ID 생성
+ const syncId = uuidv4();
+
+ // 작업 상태 초기화
+ syncJobs.set(syncId, {
+ status: 'queued',
+ startTime: new Date(),
+ packageId
+ });
+
+ // 비동기 작업 시작 (백그라운드에서 실행)
+ processTagImport(syncId).catch(error => {
+ console.error('Background tag import job failed:', error);
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred'
+ });
+ });
+
+ // 즉시 응답 반환 (작업 ID 포함)
+ return Response.json({
+ success: true,
+ message: 'Tag import job started',
+ syncId
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Failed to start tag import job:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to start tag import job'
+ }, { status: 500 });
+ }
+}
+
+// 백그라운드에서 실행되는 태그 가져오기 작업
+async function processTagImport(syncId: string) {
+ try {
+ const jobInfo = syncJobs.get(syncId)!;
+ const packageId = jobInfo.packageId;
+
+ // 상태 업데이트: 처리 중
+ syncJobs.set(syncId, {
+ ...jobInfo,
+ status: 'processing',
+ progress: 0,
+ });
+
+ if (!packageId) {
+ throw new Error('Package ID is required');
+ }
+
+ // 여기서 실제 태그 가져오기 로직 import
+ const { importTagsFromSEDP } = await import('@/lib/sedp/get-tags');
+
+ // 진행 상황 업데이트를 위한 콜백 함수
+ const updateProgress = (progress: number) => {
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ progress
+ });
+ };
+
+ // 실제 태그 가져오기 실행
+ const result = await importTagsFromSEDP(packageId, updateProgress);
+
+ // 명시적으로 캐시 무효화
+ revalidateTag(`tags-${packageId}`);
+ revalidateTag(`forms-${packageId}`);
+
+ // 상태 업데이트: 완료
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'completed',
+ endTime: new Date(),
+ result,
+ progress: 100,
+ });
+
+ return result;
+ } catch (error: any) {
+ // 에러 발생 시 상태 업데이트
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred',
+ });
+
+ throw error; // 에러 다시 던지기
+ }
+}
+
+// 서버 메모리에 저장된 작업 상태 접근 함수 (다른 API에서 사용)
+export function getSyncJobStatus(id: string) {
+ return syncJobs.get(id);
+} \ No newline at end of file
diff --git a/app/api/cron/tags/status/route.ts b/app/api/cron/tags/status/route.ts
new file mode 100644
index 00000000..9d288f52
--- /dev/null
+++ b/app/api/cron/tags/status/route.ts
@@ -0,0 +1,46 @@
+// app/api/cron/tags/status/route.ts
+import { NextRequest } from 'next/server';
+import { getSyncJobStatus } from '../start/route';
+
+export async function GET(request: NextRequest) {
+ try {
+ // URL에서 작업 ID 가져오기
+ const searchParams = request.nextUrl.searchParams;
+ const syncId = searchParams.get('id');
+
+ if (!syncId) {
+ return Response.json({
+ success: false,
+ error: 'Missing sync ID parameter'
+ }, { status: 400 });
+ }
+
+ // 작업 상태 조회
+ const jobStatus = getSyncJobStatus(syncId);
+
+ if (!jobStatus) {
+ return Response.json({
+ success: false,
+ error: 'Sync job not found'
+ }, { status: 404 });
+ }
+
+ // 작업 상태 반환
+ return Response.json({
+ success: true,
+ status: jobStatus.status,
+ startTime: jobStatus.startTime,
+ endTime: jobStatus.endTime,
+ progress: jobStatus.progress,
+ result: jobStatus.result,
+ error: jobStatus.error
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Error retrieving tag import status:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to retrieve tag import status'
+ }, { status: 500 });
+ }
+} \ No newline at end of file