// File Reader API 클라이언트 import { apiClient } from "./client"; export type FileType = "csv" | "tsv" | "txt" | "xlsx" | "xls"; export type SourceMode = "upload" | "watch"; export type SaveMode = "INSERT" | "UPSERT" | "REPLACE"; export type ProcessedAction = "archive" | "delete" | "mark"; export interface FileReaderMapping { id?: number; config_id?: number; from_column: string; from_column_type: "header" | "index"; to_column: string; to_data_type: string; default_value?: string | null; transform_expr?: string | null; is_required: boolean; mapping_order: number; } export interface FileReaderConfig { id?: number; name: string; company_code: string; file_type: FileType; source_mode: SourceMode; host_path?: string | null; file_pattern?: string; processed_action?: ProcessedAction; archive_subdir?: string; has_header: boolean; delimiter?: string; encoding?: string; sheet_name?: string | null; skip_rows?: number; target_db_id?: number | null; target_schema?: string; target_table?: string; save_mode: SaveMode; conflict_keys?: string | null; cron_schedule?: string | null; is_active: string; description?: string | null; last_run_at?: string | null; last_run_result?: string | null; last_run_message?: string | null; last_processed_count?: number; mappings?: FileReaderMapping[]; } export interface FileReaderHistory { id: number; config_id: number; file_path: string; file_size: number; file_mtime: string; started_at: string; finished_at: string | null; status: "success" | "failure" | "partial" | "skipped"; rows_total: number; rows_inserted: number; rows_failed: number; error_message: string | null; } export interface PreviewResult { headers: string[]; totalRows: number; sampleRows: Record[]; } const BASE = "/api/file-reader"; export const FileReaderAPI = { async list(companyCode?: string): Promise { const url = companyCode ? `${BASE}/configs?company_code=${encodeURIComponent(companyCode)}` : `${BASE}/configs`; const res = await apiClient.get<{ success: boolean; items: FileReaderConfig[] }>(url); return res.data.items || []; }, async get(id: number): Promise<{ config: FileReaderConfig; mappings: FileReaderMapping[] }> { const res = await apiClient.get<{ success: boolean; config: FileReaderConfig; mappings: FileReaderMapping[] }>( `${BASE}/configs/${id}` ); return { config: res.data.config, mappings: res.data.mappings || [] }; }, async create(payload: Partial): Promise { const res = await apiClient.post<{ success: boolean; id: number }>(`${BASE}/configs`, payload); return res.data.id; }, async update(id: number, payload: Partial): Promise { await apiClient.put(`${BASE}/configs/${id}`, payload); }, async remove(id: number): Promise { await apiClient.delete(`${BASE}/configs/${id}`); }, async preview(id: number, file: File): Promise { const fd = new FormData(); fd.append("file", file); const res = await apiClient.post<{ success: boolean } & PreviewResult>( `${BASE}/configs/${id}/preview`, fd ); return { headers: res.data.headers, totalRows: res.data.totalRows, sampleRows: res.data.sampleRows, }; }, async runUpload(id: number, file: File): Promise<{ status: string; rowsTotal: number; rowsInserted: number; rowsFailed: number; errorMessage: string | null; }> { const fd = new FormData(); fd.append("file", file); const res = await apiClient.post(`${BASE}/configs/${id}/run-upload`, fd); return res.data; }, async runWatch(id: number): Promise<{ scanned: number; processed: number; skipped: number; failed: number }> { const res = await apiClient.post(`${BASE}/configs/${id}/run-watch`, {}); return res.data; }, async history(id: number, limit = 50): Promise { const res = await apiClient.get<{ success: boolean; items: FileReaderHistory[] }>( `${BASE}/configs/${id}/history?limit=${limit}` ); return res.data.items || []; }, async allowedRoots(): Promise { const res = await apiClient.get<{ success: boolean; roots: string[] }>(`${BASE}/allowed-roots`); return res.data.roots || []; }, };