mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-14 02:46:37 +08:00
fix(zone): register domain delete/update APIs and drop edit UI
补全 Zone 与 Zone 域名的 update/delete 路由与业务逻辑,修复删除域名 404;前端域名列表移除编辑入口,仅保留添加与删除。
This commit is contained in:
+254
@@ -10920,6 +10920,51 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/d/zones/{id}/delete": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "删除 Zone",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/d/zones/{id}/domains": {
|
"/api/v1/d/zones/{id}/domains": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -10995,6 +11040,140 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/d/zones/{id}/domains/{domainId}/delete": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "删除 Zone 域名",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "域名 ID",
|
||||||
|
"name": "domainId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/d/zones/{id}/domains/{domainId}/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "更新 Zone 域名",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "域名 ID",
|
||||||
|
"name": "domainId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "域名参数",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/zone.DomainInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.ZoneDomain"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "Conflict",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/d/zones/{id}/overview": {
|
"/api/v1/d/zones/{id}/overview": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -11046,6 +11225,81 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/d/zones/{id}/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "更新 Zone",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Zone 参数",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/zone.Input"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.Zone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "Conflict",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/oauth/callback": {
|
"/api/v1/oauth/callback": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "接收前端传回的 state 和 code,完成 OAuth/OIDC 认证并建立会话。支持登录(login)和账号绑定(bind)两种场景。",
|
"description": "接收前端传回的 state 和 code,完成 OAuth/OIDC 认证并建立会话。支持登录(login)和账号绑定(bind)两种场景。",
|
||||||
|
|||||||
@@ -10913,6 +10913,51 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/d/zones/{id}/delete": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "删除 Zone",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/d/zones/{id}/domains": {
|
"/api/v1/d/zones/{id}/domains": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -10988,6 +11033,140 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/d/zones/{id}/domains/{domainId}/delete": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "删除 Zone 域名",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "域名 ID",
|
||||||
|
"name": "domainId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/d/zones/{id}/domains/{domainId}/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "更新 Zone 域名",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "域名 ID",
|
||||||
|
"name": "domainId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "域名参数",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/zone.DomainInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.ZoneDomain"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "Conflict",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/d/zones/{id}/overview": {
|
"/api/v1/d/zones/{id}/overview": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -11039,6 +11218,81 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/d/zones/{id}/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"SessionCookie": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openflare-zone"
|
||||||
|
],
|
||||||
|
"summary": "更新 Zone",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Zone ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Zone 参数",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/zone.Input"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/model.Zone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "Conflict",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/oauth/callback": {
|
"/api/v1/oauth/callback": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "接收前端传回的 state 和 code,完成 OAuth/OIDC 认证并建立会话。支持登录(login)和账号绑定(bind)两种场景。",
|
"description": "接收前端传回的 state 和 code,完成 OAuth/OIDC 认证并建立会话。支持登录(login)和账号绑定(bind)两种场景。",
|
||||||
|
|||||||
@@ -10546,6 +10546,34 @@ paths:
|
|||||||
summary: 创建 Zone
|
summary: 创建 Zone
|
||||||
tags:
|
tags:
|
||||||
- openflare-zone
|
- openflare-zone
|
||||||
|
/api/v1/d/zones/{id}/delete:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: Zone ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
security:
|
||||||
|
- SessionCookie: []
|
||||||
|
summary: 删除 Zone
|
||||||
|
tags:
|
||||||
|
- openflare-zone
|
||||||
/api/v1/d/zones/{id}/domains:
|
/api/v1/d/zones/{id}/domains:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -10591,6 +10619,89 @@ paths:
|
|||||||
summary: 创建 Zone 域名
|
summary: 创建 Zone 域名
|
||||||
tags:
|
tags:
|
||||||
- openflare-zone
|
- openflare-zone
|
||||||
|
/api/v1/d/zones/{id}/domains/{domainId}/delete:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: Zone ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: 域名 ID
|
||||||
|
in: path
|
||||||
|
name: domainId
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
security:
|
||||||
|
- SessionCookie: []
|
||||||
|
summary: 删除 Zone 域名
|
||||||
|
tags:
|
||||||
|
- openflare-zone
|
||||||
|
/api/v1/d/zones/{id}/domains/{domainId}/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- description: Zone ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: 域名 ID
|
||||||
|
in: path
|
||||||
|
name: domainId
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: 域名参数
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/zone.DomainInput'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Any'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.ZoneDomain'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"409":
|
||||||
|
description: Conflict
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
security:
|
||||||
|
- SessionCookie: []
|
||||||
|
summary: 更新 Zone 域名
|
||||||
|
tags:
|
||||||
|
- openflare-zone
|
||||||
/api/v1/d/zones/{id}/overview:
|
/api/v1/d/zones/{id}/overview:
|
||||||
get:
|
get:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -10620,6 +10731,51 @@ paths:
|
|||||||
summary: 获取 Zone 概览
|
summary: 获取 Zone 概览
|
||||||
tags:
|
tags:
|
||||||
- openflare-zone
|
- openflare-zone
|
||||||
|
/api/v1/d/zones/{id}/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- description: Zone ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Zone 参数
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/zone.Input'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Any'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/model.Zone'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
"409":
|
||||||
|
description: Conflict
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Any'
|
||||||
|
security:
|
||||||
|
- SessionCookie: []
|
||||||
|
summary: 更新 Zone
|
||||||
|
tags:
|
||||||
|
- openflare-zone
|
||||||
/api/v1/oauth/{source}/authorize:
|
/api/v1/oauth/{source}/authorize:
|
||||||
get:
|
get:
|
||||||
description: 根据指定认证源名称发起 OAuth 授权,支持 purpose 参数用于区分登录和账号绑定场景。认证源必须已启用。
|
description: 根据指定认证源名称发起 OAuth 授权,支持 purpose 参数用于区分登录和账号绑定场景。认证源必须已启用。
|
||||||
|
|||||||
@@ -1,26 +1,152 @@
|
|||||||
'use client'
|
'use client';
|
||||||
|
|
||||||
import {useEffect} from 'react'
|
import {useEffect} from 'react';
|
||||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
import {zodResolver} from '@hookform/resolvers/zod'
|
import {zodResolver} from '@hookform/resolvers/zod';
|
||||||
import {useForm} from 'react-hook-form'
|
import {useForm} from 'react-hook-form';
|
||||||
import {Loader2} from 'lucide-react'
|
import {Loader2} from 'lucide-react';
|
||||||
import {toast} from 'sonner'
|
import {toast} from 'sonner';
|
||||||
import {z} from 'zod'
|
import {z} from 'zod';
|
||||||
import {Button} from '@/components/ui/button'
|
|
||||||
import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle} from '@/components/ui/dialog'
|
|
||||||
import {Input} from '@/components/ui/input'
|
|
||||||
import {Label} from '@/components/ui/label'
|
|
||||||
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select'
|
|
||||||
import {Textarea} from '@/components/ui/textarea'
|
|
||||||
import {TlsCertificateService, ZoneDomainService, type ZoneDomainItem} from '@/lib/services/openflare'
|
|
||||||
|
|
||||||
const schema = z.object({domain: z.string().trim().min(1, '请输入完整域名').refine((value) => !value.includes('*.'), 'Zone 域名不支持通配符'), cert_id: z.string(), remark: z.string().max(255)})
|
import {Button} from '@/components/ui/button';
|
||||||
type Values = z.infer<typeof schema>
|
import {
|
||||||
export function ZoneDomainDialog({open, onOpenChange, zoneId, domain, onSaved}: {open: boolean; onOpenChange(open: boolean): void; zoneId: number; domain?: ZoneDomainItem | null; onSaved(): Promise<unknown> | void}) {
|
Dialog,
|
||||||
const form = useForm<Values>({resolver: zodResolver(schema), defaultValues: {domain: '', cert_id: '', remark: ''}}); const queryClient = useQueryClient()
|
DialogContent,
|
||||||
const certificatesQuery = useQuery({queryKey: ['openflare', 'tls-certificates'], queryFn: () => TlsCertificateService.list(), enabled: open})
|
DialogDescription,
|
||||||
useEffect(() => { if (open) form.reset({domain: domain?.domain ?? '', cert_id: domain?.cert_id?.toString() ?? '', remark: domain?.remark ?? ''}) }, [domain, form, open])
|
DialogFooter,
|
||||||
const mutation = useMutation({mutationFn: (values: Values) => {const payload = {domain: values.domain.toLowerCase(), cert_id: values.cert_id ? Number(values.cert_id) : null, remark: values.remark.trim()}; return domain ? ZoneDomainService.update(zoneId, domain.id, payload) : ZoneDomainService.create(zoneId, payload)}, onSuccess: async () => {toast.success(domain ? '域名已更新' : '域名已添加'); await Promise.all([onSaved(), queryClient.invalidateQueries({queryKey: ['openflare', 'zones']})]); onOpenChange(false)}, onError: (error) => toast.error(error instanceof Error ? error.message : '保存失败')})
|
DialogHeader,
|
||||||
return <Dialog open={open} onOpenChange={onOpenChange}><DialogContent><DialogHeader><DialogTitle>{domain ? '编辑 Zone 域名' : '添加 Zone 域名'}</DialogTitle><DialogDescription>填写明确 FQDN;通配符仅可存在于所选证书的 SAN 中。</DialogDescription></DialogHeader><form id="zone-domain-editor" className="space-y-4" onSubmit={form.handleSubmit((values) => mutation.mutate(values))}><div className="space-y-1.5"><Label htmlFor="fqdn">完整域名</Label><Input id="fqdn" placeholder="api.arctel.de" {...form.register('domain')} />{form.formState.errors.domain && <p className="text-xs text-destructive">{form.formState.errors.domain.message}</p>}</div><div className="space-y-1.5"><Label>证书</Label><Select value={form.watch('cert_id') || '__none'} onValueChange={(value) => form.setValue('cert_id', value === '__none' ? '' : value)}><SelectTrigger><SelectValue placeholder="不绑定证书" /></SelectTrigger><SelectContent><SelectItem value="__none">不绑定证书</SelectItem>{(certificatesQuery.data ?? []).map((certificate) => <SelectItem key={certificate.id} value={String(certificate.id)}>{certificate.name} · {certificate.primary_domain}</SelectItem>)}</SelectContent></Select></div><div className="space-y-1.5"><Label htmlFor="domain-remark">备注</Label><Textarea id="domain-remark" rows={2} {...form.register('remark')} /></div></form><DialogFooter><Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button><Button type="submit" form="zone-domain-editor" disabled={mutation.isPending}>{mutation.isPending && <Loader2 className="mr-1 size-4 animate-spin" />}{domain ? '保存修改' : '添加域名'}</Button></DialogFooter></DialogContent></Dialog>
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import {Input} from '@/components/ui/input';
|
||||||
|
import {Label} from '@/components/ui/label';
|
||||||
|
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select';
|
||||||
|
import {Textarea} from '@/components/ui/textarea';
|
||||||
|
import {TlsCertificateService, ZoneDomainService} from '@/lib/services/openflare';
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
domain: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, '请输入完整域名')
|
||||||
|
.refine((value) => !value.includes('*.'), 'Zone 域名不支持通配符'),
|
||||||
|
cert_id: z.string(),
|
||||||
|
remark: z.string().max(255),
|
||||||
|
});
|
||||||
|
|
||||||
|
type Values = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
export function ZoneDomainDialog({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
zoneId,
|
||||||
|
onSaved,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange(open: boolean): void;
|
||||||
|
zoneId: number;
|
||||||
|
onSaved(): Promise<unknown> | void;
|
||||||
|
}) {
|
||||||
|
const form = useForm<Values>({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {domain: '', cert_id: '', remark: ''},
|
||||||
|
});
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const certificatesQuery = useQuery({
|
||||||
|
queryKey: ['openflare', 'tls-certificates'],
|
||||||
|
queryFn: () => TlsCertificateService.list(),
|
||||||
|
enabled: open,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
form.reset({domain: '', cert_id: '', remark: ''});
|
||||||
|
}
|
||||||
|
}, [form, open]);
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: (values: Values) =>
|
||||||
|
ZoneDomainService.create(zoneId, {
|
||||||
|
domain: values.domain.toLowerCase(),
|
||||||
|
cert_id: values.cert_id ? Number(values.cert_id) : null,
|
||||||
|
remark: values.remark.trim(),
|
||||||
|
}),
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success('域名已添加');
|
||||||
|
await Promise.all([
|
||||||
|
onSaved(),
|
||||||
|
queryClient.invalidateQueries({queryKey: ['openflare', 'zones']}),
|
||||||
|
]);
|
||||||
|
onOpenChange(false);
|
||||||
|
},
|
||||||
|
onError: (error) =>
|
||||||
|
toast.error(error instanceof Error ? error.message : '保存失败'),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>添加 Zone 域名</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
填写明确 FQDN;通配符仅可存在于所选证书的 SAN 中。
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<form
|
||||||
|
id="zone-domain-create"
|
||||||
|
className="space-y-4"
|
||||||
|
onSubmit={form.handleSubmit((values) => mutation.mutate(values))}
|
||||||
|
>
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label htmlFor="fqdn">完整域名</Label>
|
||||||
|
<Input id="fqdn" placeholder="api.arctel.de" {...form.register('domain')} />
|
||||||
|
{form.formState.errors.domain ? (
|
||||||
|
<p className="text-xs text-destructive">
|
||||||
|
{form.formState.errors.domain.message}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label>证书</Label>
|
||||||
|
<Select
|
||||||
|
value={form.watch('cert_id') || '__none'}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
form.setValue('cert_id', value === '__none' ? '' : value)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="不绑定证书" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="__none">不绑定证书</SelectItem>
|
||||||
|
{(certificatesQuery.data ?? []).map((certificate) => (
|
||||||
|
<SelectItem key={certificate.id} value={String(certificate.id)}>
|
||||||
|
{certificate.name} · {certificate.primary_domain}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label htmlFor="domain-remark">备注</Label>
|
||||||
|
<Textarea id="domain-remark" rows={2} {...form.register('remark')} />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
form="zone-domain-create"
|
||||||
|
disabled={mutation.isPending}
|
||||||
|
>
|
||||||
|
{mutation.isPending ? (
|
||||||
|
<Loader2 className="mr-1 size-4 animate-spin" />
|
||||||
|
) : null}
|
||||||
|
添加域名
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import {useMemo, useState} from 'react';
|
import {useMemo, useState} from 'react';
|
||||||
import {useMutation} from '@tanstack/react-query';
|
import {useMutation} from '@tanstack/react-query';
|
||||||
import {Eye, Pencil, Plus, Trash2} from 'lucide-react';
|
import {Eye, Plus, Trash2} from 'lucide-react';
|
||||||
import {toast} from 'sonner';
|
import {toast} from 'sonner';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -45,7 +45,7 @@ export function ZoneDomainsTable({
|
|||||||
routesLoading?: boolean;
|
routesLoading?: boolean;
|
||||||
onChanged(): Promise<unknown> | void;
|
onChanged(): Promise<unknown> | void;
|
||||||
}) {
|
}) {
|
||||||
const [editing, setEditing] = useState<ZoneDomainItem | null | undefined>(undefined);
|
const [createOpen, setCreateOpen] = useState(false);
|
||||||
const [deleting, setDeleting] = useState<ZoneDomainItem | null>(null);
|
const [deleting, setDeleting] = useState<ZoneDomainItem | null>(null);
|
||||||
|
|
||||||
const remove = useMutation({
|
const remove = useMutation({
|
||||||
@@ -74,7 +74,7 @@ export function ZoneDomainsTable({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-7 text-xs"
|
className="h-7 text-xs"
|
||||||
onClick={() => setEditing(null)}
|
onClick={() => setCreateOpen(true)}
|
||||||
>
|
>
|
||||||
<Plus className="mr-1 size-3.5" />
|
<Plus className="mr-1 size-3.5" />
|
||||||
添加域名
|
添加域名
|
||||||
@@ -101,7 +101,7 @@ export function ZoneDomainsTable({
|
|||||||
<TableHead className="h-8 whitespace-nowrap py-2 min-w-[120px]">
|
<TableHead className="h-8 whitespace-nowrap py-2 min-w-[120px]">
|
||||||
备注
|
备注
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="sticky right-0 z-10 h-8 w-[110px] bg-background py-2 text-center">
|
<TableHead className="sticky right-0 z-10 h-8 w-[90px] bg-background py-2 text-center">
|
||||||
操作
|
操作
|
||||||
</TableHead>
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -190,22 +190,6 @@ export function ZoneDomainsTable({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-6 w-6 text-muted-foreground hover:text-foreground"
|
|
||||||
onClick={() => setEditing(domain)}
|
|
||||||
>
|
|
||||||
<Pencil className="size-3" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="top" className="text-xs">
|
|
||||||
编辑域名
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -233,14 +217,9 @@ export function ZoneDomainsTable({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<ZoneDomainDialog
|
<ZoneDomainDialog
|
||||||
open={editing !== undefined}
|
open={createOpen}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={setCreateOpen}
|
||||||
if (!open) {
|
|
||||||
setEditing(undefined);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
zoneId={zoneId}
|
zoneId={zoneId}
|
||||||
domain={editing ?? null}
|
|
||||||
onSaved={onChanged}
|
onSaved={onChanged}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,12 @@ func BindJSON(c *gin.Context, dst any) bool {
|
|||||||
|
|
||||||
// IDParam parses :id from the URL path.
|
// IDParam parses :id from the URL path.
|
||||||
func IDParam(c *gin.Context) (uint, bool) {
|
func IDParam(c *gin.Context) (uint, bool) {
|
||||||
raw := c.Param("id")
|
return NamedIDParam(c, "id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedIDParam parses a named path parameter as a positive uint ID.
|
||||||
|
func NamedIDParam(c *gin.Context, name string) (uint, bool) {
|
||||||
|
raw := c.Param(name)
|
||||||
if raw == "" {
|
if raw == "" {
|
||||||
response.AbortBadRequest(c, errInvalidID)
|
response.AbortBadRequest(c, errInvalidID)
|
||||||
return 0, false
|
return 0, false
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ const (
|
|||||||
errDomainNotFound = "域名不存在"
|
errDomainNotFound = "域名不存在"
|
||||||
errDomainExists = "域名已存在"
|
errDomainExists = "域名已存在"
|
||||||
errCertificateNotFound = "所选证书不存在"
|
errCertificateNotFound = "所选证书不存在"
|
||||||
|
errDomainBoundToRoute = "域名已绑定反代路由,请先解除绑定"
|
||||||
|
errZoneHasDomains = "Zone 下仍有域名,请先删除全部域名"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -222,6 +222,34 @@ func UpdateDomain(ctx context.Context, zoneID, id uint, input DomainInput) (*mod
|
|||||||
return &item, nil
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteDomain removes a Zone domain that is not bound to a proxy route.
|
||||||
|
func DeleteDomain(ctx context.Context, zoneID, id uint) error {
|
||||||
|
var item model.ZoneDomain
|
||||||
|
if err := db.DB(ctx).Where("id = ? AND zone_id = ?", id, zoneID).First(&item).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if item.ProxyRouteID != nil {
|
||||||
|
return errors.New(errDomainBoundToRoute)
|
||||||
|
}
|
||||||
|
return db.DB(ctx).Delete(&item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a Zone that has no remaining domains.
|
||||||
|
func Delete(ctx context.Context, id uint) error {
|
||||||
|
var zone model.Zone
|
||||||
|
if err := db.DB(ctx).First(&zone, id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var count int64
|
||||||
|
if err := db.DB(ctx).Model(&model.ZoneDomain{}).Where("zone_id = ?", id).Count(&count).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
return errors.New(errZoneHasDomains)
|
||||||
|
}
|
||||||
|
return db.DB(ctx).Delete(&zone).Error
|
||||||
|
}
|
||||||
|
|
||||||
func isUnique(err error) bool {
|
func isUnique(err error) bool {
|
||||||
return errors.Is(err, gorm.ErrDuplicatedKey) || strings.Contains(strings.ToLower(err.Error()), "unique constraint")
|
return errors.Is(err, gorm.ErrDuplicatedKey) || strings.Contains(strings.ToLower(err.Error()), "unique constraint")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,24 @@ func TestCreateZoneDomainRejectsWildcard(t *testing.T) {
|
|||||||
require.EqualError(t, err, errDomainWildcardUnsupported)
|
require.EqualError(t, err, errDomainWildcardUnsupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteDomainRejectsBoundRoute(t *testing.T) {
|
||||||
|
ctx := setupZoneDB(t)
|
||||||
|
zone, err := Create(ctx, Input{Domain: "example.com"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
item, err := CreateDomain(ctx, zone.ID, DomainInput{Domain: "api.example.com"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
routeID := uint(9)
|
||||||
|
item.ProxyRouteID = &routeID
|
||||||
|
require.NoError(t, db.DB(ctx).Save(item).Error)
|
||||||
|
|
||||||
|
err = DeleteDomain(ctx, zone.ID, item.ID)
|
||||||
|
require.EqualError(t, err, errDomainBoundToRoute)
|
||||||
|
|
||||||
|
item.ProxyRouteID = nil
|
||||||
|
require.NoError(t, db.DB(ctx).Save(item).Error)
|
||||||
|
require.NoError(t, DeleteDomain(ctx, zone.ID, item.ID))
|
||||||
|
}
|
||||||
|
|
||||||
func TestLegacyImportUsesEffectiveTLDPlusOne(t *testing.T) {
|
func TestLegacyImportUsesEffectiveTLDPlusOne(t *testing.T) {
|
||||||
root, err := zoneRoot("api.example.co.uk")
|
root, err := zoneRoot("api.example.co.uk")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -87,6 +87,56 @@ func GetOverviewHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, response.OK(item))
|
c.JSON(http.StatusOK, response.OK(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateHandler updates a Zone.
|
||||||
|
// @Summary 更新 Zone
|
||||||
|
// @Tags openflare-zone
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security SessionCookie
|
||||||
|
// @Param id path int true "Zone ID"
|
||||||
|
// @Param body body zone.Input true "Zone 参数"
|
||||||
|
// @Success 200 {object} response.Any{data=model.Zone}
|
||||||
|
// @Failure 400 {object} response.Any
|
||||||
|
// @Failure 404 {object} response.Any
|
||||||
|
// @Failure 409 {object} response.Any
|
||||||
|
// @Router /api/v1/d/zones/{id}/update [post]
|
||||||
|
func UpdateHandler(c *gin.Context) {
|
||||||
|
id, ok := apiutil.IDParam(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var input Input
|
||||||
|
if !apiutil.BindJSON(c, &input) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item, err := Update(c.Request.Context(), id, input)
|
||||||
|
if abort(c, err, errZoneNotFound) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, response.OK(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteHandler deletes a Zone with no remaining domains.
|
||||||
|
// @Summary 删除 Zone
|
||||||
|
// @Tags openflare-zone
|
||||||
|
// @Produce json
|
||||||
|
// @Security SessionCookie
|
||||||
|
// @Param id path int true "Zone ID"
|
||||||
|
// @Success 200 {object} response.Any
|
||||||
|
// @Failure 400 {object} response.Any
|
||||||
|
// @Failure 404 {object} response.Any
|
||||||
|
// @Router /api/v1/d/zones/{id}/delete [post]
|
||||||
|
func DeleteHandler(c *gin.Context) {
|
||||||
|
id, ok := apiutil.IDParam(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := Delete(c.Request.Context(), id); abort(c, err, errZoneNotFound) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, response.OKNil())
|
||||||
|
}
|
||||||
|
|
||||||
// CreateDomainHandler creates an explicit FQDN under a Zone.
|
// CreateDomainHandler creates an explicit FQDN under a Zone.
|
||||||
// @Summary 创建 Zone 域名
|
// @Summary 创建 Zone 域名
|
||||||
// @Tags openflare-zone
|
// @Tags openflare-zone
|
||||||
@@ -115,3 +165,63 @@ func CreateDomainHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, response.OK(item))
|
c.JSON(http.StatusOK, response.OK(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateDomainHandler updates a Zone domain.
|
||||||
|
// @Summary 更新 Zone 域名
|
||||||
|
// @Tags openflare-zone
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security SessionCookie
|
||||||
|
// @Param id path int true "Zone ID"
|
||||||
|
// @Param domainId path int true "域名 ID"
|
||||||
|
// @Param body body zone.DomainInput true "域名参数"
|
||||||
|
// @Success 200 {object} response.Any{data=model.ZoneDomain}
|
||||||
|
// @Failure 400 {object} response.Any
|
||||||
|
// @Failure 404 {object} response.Any
|
||||||
|
// @Failure 409 {object} response.Any
|
||||||
|
// @Router /api/v1/d/zones/{id}/domains/{domainId}/update [post]
|
||||||
|
func UpdateDomainHandler(c *gin.Context) {
|
||||||
|
zoneID, ok := apiutil.IDParam(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
domainID, ok := apiutil.NamedIDParam(c, "domainId")
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var input DomainInput
|
||||||
|
if !apiutil.BindJSON(c, &input) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item, err := UpdateDomain(c.Request.Context(), zoneID, domainID, input)
|
||||||
|
if abort(c, err, errDomainNotFound) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, response.OK(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDomainHandler deletes a Zone domain not bound to a proxy route.
|
||||||
|
// @Summary 删除 Zone 域名
|
||||||
|
// @Tags openflare-zone
|
||||||
|
// @Produce json
|
||||||
|
// @Security SessionCookie
|
||||||
|
// @Param id path int true "Zone ID"
|
||||||
|
// @Param domainId path int true "域名 ID"
|
||||||
|
// @Success 200 {object} response.Any
|
||||||
|
// @Failure 400 {object} response.Any
|
||||||
|
// @Failure 404 {object} response.Any
|
||||||
|
// @Router /api/v1/d/zones/{id}/domains/{domainId}/delete [post]
|
||||||
|
func DeleteDomainHandler(c *gin.Context) {
|
||||||
|
zoneID, ok := apiutil.IDParam(c)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
domainID, ok := apiutil.NamedIDParam(c, "domainId")
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := DeleteDomain(c.Request.Context(), zoneID, domainID); abort(c, err, errDomainNotFound) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, response.OKNil())
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,5 +15,9 @@ func registerZoneRoutes(apiGroup *gin.RouterGroup) {
|
|||||||
apiutil.RegisterCollection(zoneGroup, "GET", zone.ListHandler)
|
apiutil.RegisterCollection(zoneGroup, "GET", zone.ListHandler)
|
||||||
apiutil.RegisterCollection(zoneGroup, "POST", zone.CreateHandler)
|
apiutil.RegisterCollection(zoneGroup, "POST", zone.CreateHandler)
|
||||||
zoneGroup.GET("/:id/overview", zone.GetOverviewHandler)
|
zoneGroup.GET("/:id/overview", zone.GetOverviewHandler)
|
||||||
|
zoneGroup.POST("/:id/update", zone.UpdateHandler)
|
||||||
|
zoneGroup.POST("/:id/delete", zone.DeleteHandler)
|
||||||
zoneGroup.POST("/:id/domains", zone.CreateDomainHandler)
|
zoneGroup.POST("/:id/domains", zone.CreateDomainHandler)
|
||||||
|
zoneGroup.POST("/:id/domains/:domainId/update", zone.UpdateDomainHandler)
|
||||||
|
zoneGroup.POST("/:id/domains/:domainId/delete", zone.DeleteDomainHandler)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user