"""IDP Golden Path 템플릿 — 서비스 스캐폴딩 자동화""" from __future__ import annotations import json, logging from datetime import datetime from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user from database import get_db from models import User, IDPTemplate logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/idp/template", tags=["IDP 템플릿"]) BUILTIN_TEMPLATES = [ { "name": "fastapi-service", "description": "FastAPI Python 마이크로서비스 (Docker + Jenkinsfile + .env)", "language": "python", "framework": "fastapi", "variables": ["SERVICE_NAME", "PORT", "DB_URL"], "files": ["main.py", "requirements.txt", "Dockerfile", "Jenkinsfile", ".env.example"], }, { "name": "spring-boot-service", "description": "Spring Boot Java 서비스 (Maven + Jenkinsfile)", "language": "java", "framework": "spring-boot", "variables": ["SERVICE_NAME", "PORT", "DB_URL"], "files": ["pom.xml", "src/", "Dockerfile", "Jenkinsfile"], }, { "name": "react-spa", "description": "React 18 + TypeScript + Vite SPA", "language": "typescript", "framework": "react", "variables": ["APP_NAME", "API_BASE_URL"], "files": ["package.json", "vite.config.ts", "src/App.tsx", "Jenkinsfile"], }, { "name": "csap-compliant", "description": "공공기관 CSAP 준수 보안 설정 패키지", "language": "config", "framework": "security", "variables": ["SYSTEM_NAME", "ZONE"], "files": ["nginx.conf", "ufw-rules.sh", "audit-policy.yaml"], }, ] class TemplateCreate(BaseModel): name: str; description: str = ""; language: str = ""; framework: str = "" variables: list = []; files: list = []; content_json: str = "{}" class ScaffoldRequest(BaseModel): template_id: int; service_name: str variables: dict = {}; create_gitea_repo: bool = False @router.get("") async def list_templates(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): builtins = [{"id": f"builtin-{i}", **t, "is_builtin": True} for i, t in enumerate(BUILTIN_TEMPLATES)] rows = await db.execute(select(IDPTemplate).order_by(IDPTemplate.name)) custom = [{"id": t.id, "name": t.name, "description": t.description, "language": t.language, "is_builtin": False} for t in rows.scalars().all()] return builtins + custom @router.post("", status_code=201) async def create_template(body: TemplateCreate, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): t = IDPTemplate( name=body.name, description=body.description, language=body.language, framework=body.framework, variables=json.dumps(body.variables), files=json.dumps(body.files), content_json=body.content_json, created_by=user.id, created_at=datetime.utcnow() ) db.add(t); await db.commit(); await db.refresh(t) return {"id": t.id} @router.get("/{template_id}") async def get_template(template_id, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): if str(template_id).startswith("builtin-"): idx = int(str(template_id).replace("builtin-", "")) if idx < len(BUILTIN_TEMPLATES): return {**BUILTIN_TEMPLATES[idx], "id": template_id, "is_builtin": True} raise HTTPException(404) row = await db.execute(select(IDPTemplate).where(IDPTemplate.id == int(template_id))) t = row.scalar_one_or_none() if not t: raise HTTPException(404) return {"id": t.id, "name": t.name, "description": t.description, "language": t.language, "variables": json.loads(t.variables or "[]"), "files": json.loads(t.files or "[]")} @router.post("/{template_id}/scaffold") async def scaffold(template_id, body: ScaffoldRequest, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): """템플릿으로 새 서비스 스캐폴딩 — 변수 치환 + 선택적 Gitea repo 생성.""" scaffold_result = { "service_name": body.service_name, "template_id": str(template_id), "generated_files": [], "gitea_repo": None, "jenkins_job": None, } # 내장 템플릿 기반 파일 목록 생성 tpl_data = None if str(template_id).startswith("builtin-"): idx = int(str(template_id).replace("builtin-", "")) if idx < len(BUILTIN_TEMPLATES): tpl_data = BUILTIN_TEMPLATES[idx] if tpl_data: scaffold_result["generated_files"] = [ f.replace("SERVICE_NAME", body.service_name) for f in tpl_data.get("files", []) ] if body.create_gitea_repo: scaffold_result["gitea_repo"] = f"http://localhost:9003/zio/{body.service_name}" scaffold_result["jenkins_job"] = body.service_name return scaffold_result @router.get("/{template_id}/preview") async def preview(template_id, service_name: str = "my-service", user: User = Depends(get_current_user)): if str(template_id).startswith("builtin-"): idx = int(str(template_id).replace("builtin-", "")) if idx < len(BUILTIN_TEMPLATES): tpl = BUILTIN_TEMPLATES[idx] return {"template": tpl["name"], "service_name": service_name, "files": tpl.get("files", []), "required_vars": tpl.get("variables", [])} return {"preview": "커스텀 템플릿 미리보기"}