mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
API: Add OPTIONS wildcard handler to serve CORS preflight requests #5133
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -10,21 +10,22 @@ import (
|
||||
|
||||
// Echo returns the request and response headers as JSON if debug mode is enabled.
|
||||
//
|
||||
// The supported request methods are:
|
||||
//
|
||||
// - GET
|
||||
// - POST
|
||||
// - PUT
|
||||
// - PATCH
|
||||
// - HEAD
|
||||
// - OPTIONS
|
||||
// - DELETE
|
||||
// - CONNECT
|
||||
// - TRACE
|
||||
//
|
||||
// ANY /api/v1/echo
|
||||
// @Summary returns the request and response headers as JSON if debug mode is enabled
|
||||
// @Id Echo
|
||||
// @Success 200
|
||||
// @Router /api/v1/echo [get]
|
||||
func Echo(router *gin.RouterGroup) {
|
||||
router.Any("/echo", func(c *gin.Context) {
|
||||
methods := []string{
|
||||
http.MethodGet,
|
||||
http.MethodHead,
|
||||
http.MethodPost,
|
||||
http.MethodPut,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
http.MethodConnect,
|
||||
http.MethodTrace,
|
||||
}
|
||||
router.Match(methods, "/echo", func(c *gin.Context) {
|
||||
// Abort if debug mode is disabled.
|
||||
if !get.Config().Debug() {
|
||||
AbortFeatureDisabled(c)
|
||||
|
||||
19
internal/api/options.go
Normal file
19
internal/api/options.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Options returns an empty response to handle CORS preflight requests.
|
||||
//
|
||||
// @Summary returns CORS headers with an empty response body
|
||||
// @Id Options
|
||||
// @Success 204
|
||||
// @Router /api/v1/{any} [options]
|
||||
func Options(router *gin.RouterGroup) {
|
||||
router.OPTIONS("/*any", func(c *gin.Context) {
|
||||
c.Status(http.StatusNoContent)
|
||||
})
|
||||
}
|
||||
@@ -1714,6 +1714,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/echo": {
|
||||
"get": {
|
||||
"summary": "returns the request and response headers as JSON if debug mode is enabled",
|
||||
"operationId": "Echo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/errors": {
|
||||
"get": {
|
||||
"produces": [
|
||||
@@ -5270,6 +5281,17 @@
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/v1/{any}": {
|
||||
"options": {
|
||||
"summary": "returns CORS headers with an empty response body",
|
||||
"operationId": "Options",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{entity}/{uid}/links": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -7560,19 +7582,24 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"drawing": {
|
||||
"type": "number"
|
||||
"type": "number",
|
||||
"format": "float32"
|
||||
},
|
||||
"hentai": {
|
||||
"type": "number"
|
||||
"type": "number",
|
||||
"format": "float32"
|
||||
},
|
||||
"neutral": {
|
||||
"type": "number"
|
||||
"type": "number",
|
||||
"format": "float32"
|
||||
},
|
||||
"porn": {
|
||||
"type": "number"
|
||||
"type": "number",
|
||||
"format": "float32"
|
||||
},
|
||||
"sexy": {
|
||||
"type": "number"
|
||||
"type": "number",
|
||||
"format": "float32"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8223,9 +8250,138 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"time.Duration": {
|
||||
"tensorflow.ColorChannelOrder": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
0
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"UndefinedOrder"
|
||||
]
|
||||
},
|
||||
"tensorflow.Interval": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"end": {
|
||||
"type": "number"
|
||||
},
|
||||
"mean": {
|
||||
"type": "number"
|
||||
},
|
||||
"start": {
|
||||
"type": "number"
|
||||
},
|
||||
"stdDev": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tensorflow.ModelInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"$ref": "#/definitions/tensorflow.PhotoInput"
|
||||
},
|
||||
"output": {
|
||||
"$ref": "#/definitions/tensorflow.ModelOutput"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tensorflow.ModelOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "integer"
|
||||
},
|
||||
"logits": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"outputs": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tensorflow.PhotoInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer"
|
||||
},
|
||||
"inputOrder": {
|
||||
"$ref": "#/definitions/tensorflow.ColorChannelOrder"
|
||||
},
|
||||
"intervals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/tensorflow.Interval"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"resizeOperation": {
|
||||
"$ref": "#/definitions/tensorflow.ResizeOperation"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tensorflow.ResizeOperation": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"UndefinedResizeOperation",
|
||||
"ResizeBreakAspectRatio",
|
||||
"CenterCrop",
|
||||
"Padding"
|
||||
]
|
||||
},
|
||||
"time.Duration": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"enum": [
|
||||
-9223372036854775808,
|
||||
9223372036854775807,
|
||||
1,
|
||||
1000,
|
||||
1000000,
|
||||
1000000000,
|
||||
60000000000,
|
||||
3600000000000,
|
||||
-9223372036854775808,
|
||||
9223372036854775807,
|
||||
1,
|
||||
1000,
|
||||
1000000,
|
||||
1000000000,
|
||||
60000000000,
|
||||
3600000000000,
|
||||
-9223372036854775808,
|
||||
9223372036854775807,
|
||||
1,
|
||||
1000,
|
||||
1000000,
|
||||
1000000000,
|
||||
60000000000,
|
||||
3600000000000,
|
||||
-9223372036854775808,
|
||||
9223372036854775807,
|
||||
1,
|
||||
@@ -8236,6 +8392,30 @@
|
||||
3600000000000
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"minDuration",
|
||||
"maxDuration",
|
||||
"Nanosecond",
|
||||
"Microsecond",
|
||||
"Millisecond",
|
||||
"Second",
|
||||
"Minute",
|
||||
"Hour",
|
||||
"minDuration",
|
||||
"maxDuration",
|
||||
"Nanosecond",
|
||||
"Microsecond",
|
||||
"Millisecond",
|
||||
"Second",
|
||||
"Minute",
|
||||
"Hour",
|
||||
"minDuration",
|
||||
"maxDuration",
|
||||
"Nanosecond",
|
||||
"Microsecond",
|
||||
"Millisecond",
|
||||
"Second",
|
||||
"Minute",
|
||||
"Hour",
|
||||
"minDuration",
|
||||
"maxDuration",
|
||||
"Nanosecond",
|
||||
@@ -8433,7 +8613,8 @@
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
"type": "number",
|
||||
"format": "float64"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8498,9 +8679,15 @@
|
||||
"Service": {
|
||||
"$ref": "#/definitions/vision.Service"
|
||||
},
|
||||
"default": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"disabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/tensorflow.ModelInfo"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -24,11 +24,14 @@ var Api = func(conf *config.Config) gin.HandlerFunc {
|
||||
if origin := conf.CORSOrigin(); origin != "" {
|
||||
c.Header(header.AccessControlAllowOrigin, origin)
|
||||
|
||||
// Add additional information to preflight OPTION requests.
|
||||
// Handle OPTIONS preflight requests by adding CORS headers
|
||||
// and aborting the request with HTTP status code 204.
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.Header(header.AccessControlAllowHeaders, conf.CORSHeaders())
|
||||
c.Header(header.AccessControlAllowMethods, conf.CORSMethods())
|
||||
c.Header(header.AccessControlMaxAge, header.DefaultAccessControlMaxAge)
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,5 +202,6 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
||||
api.Connect(APIv1)
|
||||
api.WebSocket(APIv1)
|
||||
api.GetMetrics(APIv1)
|
||||
api.Options(APIv1)
|
||||
api.Echo(APIv1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user