from __future__ import annotations import hashlib import json from pathlib import Path from fastapi import HTTPException from fastapi.responses import FileResponse, Response BUSINESS_LEDGER_DEFAULT_SOURCE_KEY = "business_ledger_default" def sync_default_business_ledger_source(cur, incoming_files_dir: Path, served_dir: Path) -> None: cur.execute("SELECT to_regclass('public.integration_binary_sources') IS NOT NULL AS table_exists") row = cur.fetchone() table_exists = bool(row["table_exists"]) if row is not None else False if not table_exists: return business_dashboard_dir = incoming_files_dir / "사업관리대장" business_ledger_served_dir = served_dir / "ledger" candidates = [ business_ledger_served_dir / "사업관리대장-1.xlsx", business_dashboard_dir / "사업관리대장-1.xlsx", business_dashboard_dir / "사업관리 대장-1.xlsx", business_dashboard_dir / "사업관리대장.xlsx", business_dashboard_dir / "사업관리 대장.xlsx", ] source_path = next((candidate for candidate in candidates if candidate.exists()), None) if source_path is None: return content = source_path.read_bytes() content_sha256 = hashlib.sha256(content).hexdigest() meta_json = { "byte_size": len(content), "source_path": str(source_path), "synced_from": "startup", } cur.execute( """ INSERT INTO integration_binary_sources ( source_key, source_name, filename, mime_type, content, content_sha256, meta_json, imported_at ) VALUES (%s, %s, %s, %s, %s, %s, %s::jsonb, NOW()) ON CONFLICT (source_key) DO UPDATE SET source_name = EXCLUDED.source_name, filename = EXCLUDED.filename, mime_type = EXCLUDED.mime_type, content = EXCLUDED.content, content_sha256 = EXCLUDED.content_sha256, meta_json = EXCLUDED.meta_json, imported_at = NOW() WHERE integration_binary_sources.content_sha256 IS DISTINCT FROM EXCLUDED.content_sha256 OR integration_binary_sources.filename IS DISTINCT FROM EXCLUDED.filename OR integration_binary_sources.mime_type IS DISTINCT FROM EXCLUDED.mime_type OR integration_binary_sources.meta_json IS DISTINCT FROM EXCLUDED.meta_json """, ( BUSINESS_LEDGER_DEFAULT_SOURCE_KEY, "사업관리대장 기본 원본", source_path.name, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", content, content_sha256, json.dumps(meta_json, ensure_ascii=False), ), ) def build_business_ledger_default_response(cur) -> Response: cur.execute( """ SELECT filename, mime_type, content FROM integration_binary_sources WHERE source_key = %s ORDER BY imported_at DESC LIMIT 1 """, (BUSINESS_LEDGER_DEFAULT_SOURCE_KEY,), ) row = cur.fetchone() if not row: raise HTTPException(status_code=404, detail="Business ledger default source not found.") filename = str(row["filename"] or "사업관리대장-1.xlsx") headers = { "Content-Disposition": 'inline; filename="business-ledger-default.xlsx"', "X-Source-Filename": "business-ledger-default.xlsx", "X-Original-Filename": filename, "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0", "Pragma": "no-cache", } return Response( content=bytes(row["content"]), media_type=str( row["mime_type"] or "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ), headers=headers, ) def build_ledger_index_response(ledger_index_path: Path) -> FileResponse: if not ledger_index_path.exists(): raise HTTPException(status_code=404, detail="Business ledger integration file not found.") response = FileResponse(ledger_index_path) response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" response.headers["Pragma"] = "no-cache" return response