- 新端点 /ai/v1/audio/speech(xai.grok-tts)、/rerank(cohere.rerank-v4)、/moderations(OCI Guardrails) - Responses 放行 code_interpreter 与远程 mcp 工具,web_search/x_search 解除仅非流式限制 - 模型能力映射扩展:TEXT_RERANK→RERANK、TEXT_TO_AUDIO→TTS - AI 网关文档独立 docs/ai-gateway.md,字段兼容矩阵只列支持项;README 精简引用 - swagger 修缺:135 处响应注解具体化,RawMessage/联合类型统一渲染 AnyJSON,overrides 迁至 docs/.swaggo - CHANGELOG 0.4.0,版本段不再记日期;DASH_VERSION v0.4.0
500 lines
14 KiB
Go
500 lines
14 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
// @Summary 实例形状列表
|
|
// @Tags 租户配置
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.ComputeShape
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/shapes [get]
|
|
func (h *ociConfigHandler) shapes(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
shapes, err := h.svc.Shapes(c.Request.Context(), id, c.Query("region"), c.Query("availabilityDomain"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, shapes)
|
|
}
|
|
|
|
// @Summary 镜像列表
|
|
// @Tags 租户配置
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.Image
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/images [get]
|
|
func (h *ociConfigHandler) images(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
images, err := h.svc.Images(c.Request.Context(), id, oci.ImagesQuery{
|
|
Region: c.Query("region"),
|
|
OperatingSystem: c.Query("operatingSystem"),
|
|
Shape: c.Query("shape"),
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, images)
|
|
}
|
|
|
|
// @Summary 镜像详情
|
|
// @Tags 租户配置
|
|
// @Param id path int true "配置 ID"
|
|
// @Param imageId path string true "imageId"
|
|
// @Success 200 {object} oci.Image
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/images/{imageId} [get]
|
|
func (h *ociConfigHandler) getImage(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
image, err := h.svc.Image(c.Request.Context(), id, c.Query("region"), c.Param("imageId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, image)
|
|
}
|
|
|
|
// ---- VCN ----
|
|
|
|
type createVCNRequest struct {
|
|
Region string `json:"region"`
|
|
CompartmentID string `json:"compartmentId"` // 为空时建在租户根
|
|
DisplayName string `json:"displayName" binding:"required"`
|
|
CidrBlock string `json:"cidrBlock" binding:"required"`
|
|
DnsLabel string `json:"dnsLabel"`
|
|
EnableIPv6 *bool `json:"enableIpv6"` // 缺省默认启用
|
|
}
|
|
|
|
type renameRequest struct {
|
|
Region string `json:"region"`
|
|
DisplayName string `json:"displayName" binding:"required"`
|
|
}
|
|
|
|
// @Summary VCN 列表
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.VCN
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vcns [get]
|
|
func (h *ociConfigHandler) listVCNs(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
vcns, err := h.svc.VCNs(c.Request.Context(), id, c.Query("region"), c.Query("compartmentId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, vcns)
|
|
}
|
|
|
|
// @Summary 创建 VCN
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param body body createVCNRequest true "请求体"
|
|
// @Success 201 {object} oci.VCN
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vcns [post]
|
|
func (h *ociConfigHandler) createVCN(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req createVCNRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
vcn, err := h.svc.CreateVCN(c.Request.Context(), id, oci.CreateVCNInput{
|
|
Region: req.Region,
|
|
CompartmentID: req.CompartmentID,
|
|
DisplayName: req.DisplayName,
|
|
CidrBlock: req.CidrBlock,
|
|
DnsLabel: req.DnsLabel,
|
|
EnableIPv6: req.EnableIPv6 == nil || *req.EnableIPv6,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, vcn)
|
|
}
|
|
|
|
// @Summary VCN 详情
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param vcnId path string true "vcnId"
|
|
// @Success 200 {object} oci.VCN
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vcns/{vcnId} [get]
|
|
func (h *ociConfigHandler) getVCN(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
vcn, err := h.svc.VCN(c.Request.Context(), id, c.Query("region"), c.Param("vcnId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, vcn)
|
|
}
|
|
|
|
// @Summary 更新 VCN
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param vcnId path string true "vcnId"
|
|
// @Param body body renameRequest true "请求体"
|
|
// @Success 200 {object} oci.VCN
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vcns/{vcnId} [put]
|
|
func (h *ociConfigHandler) updateVCN(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req renameRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
vcn, err := h.svc.UpdateVCN(c.Request.Context(), id, req.Region, c.Param("vcnId"), req.DisplayName)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, vcn)
|
|
}
|
|
|
|
// @Summary 删除 VCN
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param vcnId path string true "vcnId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vcns/{vcnId} [delete]
|
|
func (h *ociConfigHandler) deleteVCN(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DeleteVCN(c.Request.Context(), id, c.Query("region"), c.Param("vcnId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
type enableIPv6Request struct {
|
|
Region string `json:"region"`
|
|
}
|
|
|
|
// @Summary VCN 启用 IPv6
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param vcnId path string true "vcnId"
|
|
// @Param body body enableIPv6Request true "请求体"
|
|
// @Success 200 {object} ipv6StepsResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vcns/{vcnId}/enable-ipv6 [post]
|
|
func (h *ociConfigHandler) enableVCNIPv6(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req enableIPv6Request
|
|
if err := c.ShouldBindJSON(&req); err != nil && c.Request.ContentLength > 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
steps, err := h.svc.EnableVCNIPv6(c.Request.Context(), id, req.Region, c.Param("vcnId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"steps": steps})
|
|
}
|
|
|
|
// ---- Subnet ----
|
|
|
|
type createSubnetRequest struct {
|
|
Region string `json:"region"`
|
|
VcnID string `json:"vcnId" binding:"required"`
|
|
DisplayName string `json:"displayName" binding:"required"`
|
|
CidrBlock string `json:"cidrBlock" binding:"required"`
|
|
DnsLabel string `json:"dnsLabel"`
|
|
Ipv6CidrBlock string `json:"ipv6CidrBlock"`
|
|
ProhibitPublicIP bool `json:"prohibitPublicIp"`
|
|
}
|
|
|
|
// @Summary 子网列表
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.Subnet
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/subnets [get]
|
|
func (h *ociConfigHandler) listSubnets(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
subnets, err := h.svc.Subnets(c.Request.Context(), id, c.Query("region"), c.Query("vcnId"), c.Query("compartmentId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, subnets)
|
|
}
|
|
|
|
// @Summary 创建子网
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param body body createSubnetRequest true "请求体"
|
|
// @Success 201 {object} oci.Subnet
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/subnets [post]
|
|
func (h *ociConfigHandler) createSubnet(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req createSubnetRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
subnet, err := h.svc.CreateSubnet(c.Request.Context(), id, oci.CreateSubnetInput{
|
|
Region: req.Region,
|
|
VcnID: req.VcnID,
|
|
DisplayName: req.DisplayName,
|
|
CidrBlock: req.CidrBlock,
|
|
DnsLabel: req.DnsLabel,
|
|
Ipv6CidrBlock: req.Ipv6CidrBlock,
|
|
ProhibitPublicIP: req.ProhibitPublicIP,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, subnet)
|
|
}
|
|
|
|
// @Summary 子网详情
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param subnetId path string true "subnetId"
|
|
// @Success 200 {object} oci.Subnet
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/subnets/{subnetId} [get]
|
|
func (h *ociConfigHandler) getSubnet(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
subnet, err := h.svc.Subnet(c.Request.Context(), id, c.Query("region"), c.Param("subnetId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, subnet)
|
|
}
|
|
|
|
// @Summary 更新子网
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param subnetId path string true "subnetId"
|
|
// @Param body body renameRequest true "请求体"
|
|
// @Success 200 {object} oci.Subnet
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/subnets/{subnetId} [put]
|
|
func (h *ociConfigHandler) updateSubnet(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req renameRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
subnet, err := h.svc.UpdateSubnet(c.Request.Context(), id, req.Region, c.Param("subnetId"), req.DisplayName)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, subnet)
|
|
}
|
|
|
|
// @Summary 删除子网
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param subnetId path string true "subnetId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/subnets/{subnetId} [delete]
|
|
func (h *ociConfigHandler) deleteSubnet(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DeleteSubnet(c.Request.Context(), id, c.Query("region"), c.Param("subnetId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// ---- Security List ----
|
|
|
|
type createSecurityListRequest struct {
|
|
Region string `json:"region"`
|
|
VcnID string `json:"vcnId" binding:"required"`
|
|
DisplayName string `json:"displayName" binding:"required"`
|
|
IngressRules []oci.SecurityRule `json:"ingressRules"`
|
|
EgressRules []oci.SecurityRule `json:"egressRules"`
|
|
}
|
|
|
|
type updateSecurityListRequest struct {
|
|
Region string `json:"region"`
|
|
DisplayName string `json:"displayName"`
|
|
IngressRules *[]oci.SecurityRule `json:"ingressRules"`
|
|
EgressRules *[]oci.SecurityRule `json:"egressRules"`
|
|
}
|
|
|
|
// @Summary 安全列表清单
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.SecurityList
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/security-lists [get]
|
|
func (h *ociConfigHandler) listSecurityLists(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
lists, err := h.svc.SecurityLists(c.Request.Context(), id, c.Query("region"), c.Query("vcnId"), c.Query("compartmentId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, lists)
|
|
}
|
|
|
|
// @Summary 创建安全列表
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param body body createSecurityListRequest true "请求体"
|
|
// @Success 201 {object} oci.SecurityList
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/security-lists [post]
|
|
func (h *ociConfigHandler) createSecurityList(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req createSecurityListRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
list, err := h.svc.CreateSecurityList(c.Request.Context(), id, oci.CreateSecurityListInput{
|
|
Region: req.Region,
|
|
VcnID: req.VcnID,
|
|
DisplayName: req.DisplayName,
|
|
IngressRules: req.IngressRules,
|
|
EgressRules: req.EgressRules,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, list)
|
|
}
|
|
|
|
// @Summary 安全列表详情
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param securityListId path string true "securityListId"
|
|
// @Success 200 {object} oci.SecurityList
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/security-lists/{securityListId} [get]
|
|
func (h *ociConfigHandler) getSecurityList(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
list, err := h.svc.SecurityList(c.Request.Context(), id, c.Query("region"), c.Param("securityListId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, list)
|
|
}
|
|
|
|
// @Summary 更新安全列表
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param securityListId path string true "securityListId"
|
|
// @Param body body updateSecurityListRequest true "请求体"
|
|
// @Success 200 {object} oci.SecurityList
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/security-lists/{securityListId} [put]
|
|
func (h *ociConfigHandler) updateSecurityList(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req updateSecurityListRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
list, err := h.svc.UpdateSecurityList(c.Request.Context(), id, c.Param("securityListId"), oci.UpdateSecurityListInput{
|
|
Region: req.Region,
|
|
DisplayName: req.DisplayName,
|
|
IngressRules: req.IngressRules,
|
|
EgressRules: req.EgressRules,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, list)
|
|
}
|
|
|
|
// @Summary 删除安全列表
|
|
// @Tags 网络
|
|
// @Param id path int true "配置 ID"
|
|
// @Param securityListId path string true "securityListId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/security-lists/{securityListId} [delete]
|
|
func (h *ociConfigHandler) deleteSecurityList(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DeleteSecurityList(c.Request.Context(), id, c.Query("region"), c.Param("securityListId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|