Files
vconnect-api/app/api/tunnel.py
2025-12-08 21:35:55 +09:00

128 lines
4.2 KiB
Python

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
)