148 lines
4.5 KiB
Python
Raw Normal View History

2025-12-04 10:09:04 +08:00
# api/ecs.py
from typing import List, Dict
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field
from services.ecs_service import (
create_ecs_instances,
get_account_credit,
list_custom_and_shared_images,
list_vpcs_and_vswitches,
)
from utils.validators import (
is_valid_customer_name,
is_valid_resource_name,
is_valid_region,
)
router = APIRouter()
class ImageItem(BaseModel):
ImageId: str = Field(..., description="镜像 ID")
ImageName: str = Field(..., description="镜像名称")
class CreateInstanceRequest(BaseModel):
customer: str
resource_name: str
region: str
access_key: str
secret_key: str
count: int = 1
account_uid: str | None = None
image_id: str
zone_id: str | None = None
vswitch_id: str | None = None
class InstanceResponse(BaseModel):
ip: str
name: str
region: str
instance_id: str
user: str
password: str
created: str
email: str | None = None
ram_user: str
ram_password: str
image_id: str
class CreateInstancesResponse(BaseModel):
instances: list[InstanceResponse]
@router.get(
"/images",
response_model=List[ImageItem],
summary="查询自建镜像和共享给您的镜像列表",
)
def get_images(
access_key: str = Query(..., description="用户的 AccessKey"),
secret_key: str = Query(..., description="用户的 SecretKey"),
ecs_region: str = Query("ap-southeast-1", description="ECS 实例所在区域,仅做展示"),
) -> List[ImageItem]:
"""
返回两类镜像
- owner_alias='self' 用户自建镜像
- owner_alias='others' 共享给用户的镜像
"""
try:
images = list_custom_and_shared_images(
access_key, secret_key, ecs_region)
return images
except Exception as e:
raise HTTPException(status_code=500, detail=f"拉取镜像失败:{e}")
@router.get("/balance")
def balance(
access_key: str = Query(..., description="用户的 AccessKey"),
secret_key: str = Query(..., description="用户的 SecretKey"),
ecs_region: str = Query("ap-southeast-1", description="ECS 实例所在区域,仅做展示"),
) -> Dict[str, float]:
"""
查询账户可用余额AvailableAmount接口名不变仍叫 /balance
"""
try:
# 调用 get_account_credit 方法查询可用余额
available_amount = get_account_credit(
access_key, secret_key, ecs_region)
# 返回查询结果,确保返回 float 类型
return {"available_amount": float(available_amount)}
except RuntimeError as re:
# 捕获具体的运行时错误并返回 400 状态码
raise HTTPException(status_code=400, detail=f"请求失败:{str(re)}")
except Exception as e:
# 捕获所有其他异常并返回 500 状态码
raise HTTPException(status_code=500, detail=f"查询可用余额失败:{str(e)}")
@router.post("/create", response_model=CreateInstancesResponse)
def create_instances(data: CreateInstanceRequest):
if not is_valid_customer_name(data.customer):
raise HTTPException(status_code=400, detail="客户名称不能为空")
if not is_valid_resource_name(data.resource_name):
raise HTTPException(status_code=400, detail="资源账号名称非法")
if not is_valid_region(data.region):
raise HTTPException(status_code=400, detail="区域 ID 格式不合法")
try:
results = create_ecs_instances(
customer=data.customer,
resource_name=data.resource_name,
region=data.region,
access_key=data.access_key,
secret_key=data.secret_key,
count=data.count,
account_uid=data.account_uid,
image_id=data.image_id,
zone_id=data.zone_id,
vswitch_id=data.vswitch_id,
)
return CreateInstancesResponse(instances=results)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/vswitches")
def list_vswitches(
access_key: str = Query(..., description="AccessKey"),
secret_key: str = Query(..., description="SecretKey"),
ecs_region: str = Query(..., description="地域 ID"),
):
"""
拉取指定地域下的 VPC VSwitch 列表供前端选择
"""
try:
data = list_vpcs_and_vswitches(access_key, secret_key, ecs_region)
return data
except Exception as e:
raise HTTPException(status_code=500, detail=f"拉取 VPC/VSwitch 失败:{e}")