feat(12.2-02): add file removal, caption, reorder and protected downloads

- DELETE, PUT and POST reorder under .../{id}/files/{field}, each scoped by one parent query (404 for a foreign file)
- protected download and thumb routes: is_public=false only, nosniff, private no-store, sandbox CSP, inline only for jpeg/png/gif/webp
- the save applies deferred removals, replaces attachOne files and rechecks maxFiles and required
- blobs of deleted files are removed after commit
- swagger2openapi emits binary content for file responses
- admin OpenAPI, TS types, conformance, README and attachments docs
This commit is contained in:
Jakub Zych
2026-10-02 18:11:56 +02:00
parent 044e0450ef
commit e54fd257ee
17 changed files with 2237 additions and 82 deletions

View File

@@ -9,6 +9,7 @@ import (
"encoding/json"
"fmt"
"os"
"strings"
)
func main() {
@@ -322,8 +323,17 @@ func convertResponses(res map[string]any, produces []string) map[string]any {
converted[k] = v
}
if schema != nil {
// A Swagger 2.0 file response is binary under the operation's
// media types; any other schema (an error envelope next to a
// binary success) is JSON.
types := produces
if m, ok := schema.(map[string]any); ok && m["type"] == "file" {
schema = map[string]any{"type": "string", "format": "binary"}
} else {
types = jsonTypes(produces)
}
content := map[string]any{}
for _, ct := range produces {
for _, ct := range types {
content[ct] = map[string]any{"schema": schema}
}
converted["content"] = content
@@ -333,6 +343,21 @@ func convertResponses(res map[string]any, produces []string) map[string]any {
return out
}
// jsonTypes keeps the JSON media types of produces, or application/json
// when there are none.
func jsonTypes(produces []string) []string {
var out []string
for _, ct := range produces {
if strings.Contains(ct, "json") {
out = append(out, ct)
}
}
if len(out) == 0 {
return []string{"application/json"}
}
return out
}
func convertSecurity(sec map[string]any) map[string]any {
out := make(map[string]any, len(sec))
for name, raw := range sec {