213f57b52d
Media Engine Control Panel — 6탭 웹 대시보드 [백엔드] FastAPI (dashboard/backend/) - server.py: 포트 8080, CORS, React SPA 서빙 - api_overview.py: KPI 카드 + 파이프라인 상태 + 활동 로그 - api_content.py: 칸반 보드 + 승인/거부 + 수동 트리거 - api_analytics.py: 방문자 추이 + 플랫폼/코너별 성과 - api_novels.py: 소설 목록/생성/에피소드 관리 - api_settings.py: engine.json CRUD - api_connections.py: AI 서비스 연결 관리 + 키 저장 - api_tools.py: 기능별 AI 도구 선택 - api_cost.py: 구독 현황 + API 사용량 추적 - api_logs.py: 시스템 로그 필터/검색 [프론트엔드] React + Vite + Tailwind + Recharts (dashboard/frontend/) - Overview: KPI 카드 + 파이프라인 + 코너별 바차트 + 활동 로그 - Content: 4열 칸반 보드 + 상세 모달 + 승인/거부 - Analytics: LineChart 방문자 추이 + 플랫폼별 성과 - Novel: 소설 목록 + 에피소드 테이블 + 새 소설 생성 폼 - Settings: 5개 서브탭 (AI연결/도구선택/배포채널/품질/비용관리) - Logs: 필터/검색 시스템 로그 뷰어 [디자인] CNN 다크+골드 테마 - 배경 #0a0a0d + 액센트 #c8a84e - 모바일 반응형 (Tailscale 외부 접속 대응) [실행] - dashboard/start.bat 더블클릭 → http://localhost:8080 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
79 lines
2.9 KiB
Python
79 lines
2.9 KiB
Python
"""
|
|
dashboard/backend/server.py
|
|
미디어 엔진 컨트롤 패널 — FastAPI 메인 서버
|
|
|
|
실행: uvicorn dashboard.backend.server:app --port 8080
|
|
또는: python -m uvicorn dashboard.backend.server:app --port 8080 --reload
|
|
"""
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.responses import FileResponse
|
|
|
|
from dashboard.backend import (
|
|
api_overview,
|
|
api_content,
|
|
api_analytics,
|
|
api_novels,
|
|
api_settings,
|
|
api_connections,
|
|
api_tools,
|
|
api_cost,
|
|
api_logs,
|
|
)
|
|
|
|
app = FastAPI(title="The 4th Path — Control Panel", version="1.0.0")
|
|
|
|
# ── CORS ──────────────────────────────────────────────────────────────────────
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[
|
|
"http://localhost:5173",
|
|
"http://localhost:8080",
|
|
"http://127.0.0.1:5173",
|
|
"http://127.0.0.1:8080",
|
|
],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# ── API 라우터 등록 ────────────────────────────────────────────────────────────
|
|
app.include_router(api_overview.router, prefix="/api")
|
|
app.include_router(api_content.router, prefix="/api")
|
|
app.include_router(api_analytics.router, prefix="/api")
|
|
app.include_router(api_novels.router, prefix="/api")
|
|
app.include_router(api_settings.router, prefix="/api")
|
|
app.include_router(api_connections.router, prefix="/api")
|
|
app.include_router(api_tools.router, prefix="/api")
|
|
app.include_router(api_cost.router, prefix="/api")
|
|
app.include_router(api_logs.router, prefix="/api")
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
return {"status": "ok", "service": "The 4th Path Control Panel"}
|
|
|
|
|
|
# ── 정적 파일 서빙 (프론트엔드 빌드 결과) — API 라우터보다 나중에 등록 ──────────
|
|
FRONTEND_DIST = Path(__file__).parent.parent / "frontend" / "dist"
|
|
|
|
if FRONTEND_DIST.exists():
|
|
assets_dir = FRONTEND_DIST / "assets"
|
|
if assets_dir.exists():
|
|
app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")
|
|
|
|
@app.get("/", include_in_schema=False)
|
|
@app.get("/{full_path:path}", include_in_schema=False)
|
|
async def serve_spa(full_path: str = ""):
|
|
# API 경로는 위 라우터가 처리 — 여기는 SPA 라우팅용
|
|
if full_path.startswith("api/"):
|
|
from fastapi.responses import JSONResponse
|
|
return JSONResponse({"detail": "Not Found"}, status_code=404)
|
|
index = FRONTEND_DIST / "index.html"
|
|
if index.exists():
|
|
return FileResponse(str(index))
|
|
return {"status": "frontend not built — run: npm run build"}
|