from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from app.database import get_db from app.schemas.tunnel import ( TunnelCreateRequest, TunnelCreateResponse, TunnelStatusResponse, TunnelCloseResponse, TunnelInfo ) from app.schemas.auth import CurrentUser from app.api.auth import get_current_user from app.services.ssh_tunnel_service import ssh_tunnel_manager from app.services.proxmox_service import proxmox_service from app.models.vm import VMAccess from app.utils.exceptions import NotFoundError, BadRequestError router = APIRouter() @router.post("/create", response_model=TunnelCreateResponse) async def create_tunnel( request: TunnelCreateRequest, current_user: CurrentUser = Depends(get_current_user), db: Session = Depends(get_db) ): """ SSH 터널 생성 VM에 접속하기 위한 SSH Local Port Forwarding 터널을 생성합니다. 클라이언트는 반환된 local_port로 RDP 연결을 시도해야 합니다. """ # VM IP 주소 확인 # 1. 요청에 IP가 포함되어 있으면 사용 (Guest Agent 없이도 가능) ip_address = request.vm_ip # 2. 없으면 Guest Agent로 조회 if not ip_address: try: ip_address = await proxmox_service.get_vm_ip(request.node, request.vm_id) if ip_address: print(f"✅ Guest Agent에서 IP 조회 성공: {ip_address}") except Exception as e: print(f"⚠️ Guest Agent IP 조회 실패: {str(e)}") ip_address = None # 3. Guest Agent 실패 시 VMAccess 테이블의 static_ip 사용 if not ip_address: vm_access = db.query(VMAccess).filter( VMAccess.user_id == current_user.id, VMAccess.vm_id == request.vm_id, VMAccess.node == request.node ).first() if vm_access and vm_access.static_ip: ip_address = vm_access.static_ip print(f"✅ Static IP 사용: {ip_address}") else: raise BadRequestError( "VM의 IP 주소를 확인할 수 없습니다. " "관리자 패널에서 고정 IP를 설정하거나 QEMU Guest Agent를 설치하세요." ) try: # SSH 터널 생성 tunnel_info = await ssh_tunnel_manager.create_tunnel( user_id=current_user.id, vm_id=request.vm_id, remote_host=ip_address, remote_port=3389 # RDP 포트 ) return TunnelCreateResponse( success=True, message="SSH 터널이 생성되었습니다", session_id=tunnel_info["session_id"], tunnel_info=TunnelInfo( session_id=tunnel_info["session_id"], local_port=tunnel_info["local_port"], remote_host=tunnel_info["remote_host"], remote_port=tunnel_info["remote_port"], vm_id=request.vm_id, is_active=True, created_at=None # 자동 설정 ) ) except Exception as e: return TunnelCreateResponse( success=False, message=f"터널 생성 실패: {str(e)}", session_id="", tunnel_info=None ) @router.get("/{session_id}/status", response_model=TunnelStatusResponse) async def get_tunnel_status( session_id: str, current_user: CurrentUser = Depends(get_current_user) ): """ 터널 상태 조회 활성 터널의 상태를 확인합니다. """ status = await ssh_tunnel_manager.get_tunnel_status(session_id) if not status: raise NotFoundError("터널을 찾을 수 없습니다") return TunnelStatusResponse(**status) @router.delete("/{session_id}", response_model=TunnelCloseResponse) async def close_tunnel( session_id: str, current_user: CurrentUser = Depends(get_current_user) ): """ 터널 종료 SSH 터널을 종료하고 리소스를 정리합니다. """ success = await ssh_tunnel_manager.close_tunnel(session_id) return TunnelCloseResponse( success=success, message="터널이 종료되었습니다" if success else "터널 종료에 실패했습니다", session_id=session_id )