feat: file upload imlemented
All checks were successful
Build and Deploy Go App / build (push) Successful in 9m4s
Build and Deploy Go App / deploy (push) Successful in 41s

This commit is contained in:
Iron_Felix 2025-12-06 04:13:27 +03:00
parent 7623adf2a7
commit 184868b142
7 changed files with 355 additions and 8 deletions

View file

@ -527,6 +527,42 @@ paths:
description: Internal server error
security:
- XsrfAuthHeader: []
/media/upload:
post:
summary: 'Upload an image (PNG, JPEG, or WebP)'
description: |
Uploads a single image file. Supported formats: **PNG**, **JPEG/JPG**, **WebP**.
requestBody:
required: true
content:
encoding:
image:
contentType: 'image/png, image/jpeg, image/webp'
multipart/form-data:
schema:
image:
type: string
format: binary
description: 'Image file (PNG, JPEG, or WebP)'
responses:
'200':
description: Image uploaded successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Image'
'400':
description: 'Bad request — e.g., invalid/malformed image, empty file'
content:
application/json:
schema:
type: string
'415':
description: |
Unsupported Media Type — e.g., request `Content-Type` is not `multipart/form-data`,
or the `image` part has an unsupported `Content-Type` (not image/png, image/jpeg, or image/webp)
'500':
description: Internal server error
components:
parameters:
cursor:

View file

@ -7,7 +7,10 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
@ -181,6 +184,9 @@ type UserTitleStatus string
// Cursor defines model for cursor.
type Cursor = string
// PostMediaUploadMultipartBody defines parameters for PostMediaUpload.
type PostMediaUploadMultipartBody = interface{}
// GetTitlesParams defines parameters for GetTitles.
type GetTitlesParams struct {
Cursor *Cursor `form:"cursor,omitempty" json:"cursor,omitempty"`
@ -271,6 +277,9 @@ type UpdateUserTitleJSONBody struct {
Status *UserTitleStatus `json:"status,omitempty"`
}
// PostMediaUploadMultipartRequestBody defines body for PostMediaUpload for multipart/form-data ContentType.
type PostMediaUploadMultipartRequestBody = PostMediaUploadMultipartBody
// UpdateUserJSONRequestBody defines body for UpdateUser for application/json ContentType.
type UpdateUserJSONRequestBody UpdateUserJSONBody
@ -282,6 +291,9 @@ type UpdateUserTitleJSONRequestBody UpdateUserTitleJSONBody
// ServerInterface represents all server handlers.
type ServerInterface interface {
// Upload an image (PNG, JPEG, or WebP)
// (POST /media/upload)
PostMediaUpload(c *gin.Context)
// Get titles
// (GET /titles)
GetTitles(c *gin.Context, params GetTitlesParams)
@ -323,6 +335,19 @@ type ServerInterfaceWrapper struct {
type MiddlewareFunc func(c *gin.Context)
// PostMediaUpload operation middleware
func (siw *ServerInterfaceWrapper) PostMediaUpload(c *gin.Context) {
for _, middleware := range siw.HandlerMiddlewares {
middleware(c)
if c.IsAborted() {
return
}
}
siw.Handler.PostMediaUpload(c)
}
// GetTitles operation middleware
func (siw *ServerInterfaceWrapper) GetTitles(c *gin.Context) {
@ -854,6 +879,7 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options
ErrorHandler: errorHandler,
}
router.POST(options.BaseURL+"/media/upload", wrapper.PostMediaUpload)
router.GET(options.BaseURL+"/titles", wrapper.GetTitles)
router.GET(options.BaseURL+"/titles/:title_id", wrapper.GetTitle)
router.GET(options.BaseURL+"/users/", wrapper.GetUsers)
@ -866,6 +892,49 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options
router.PATCH(options.BaseURL+"/users/:user_id/titles/:title_id", wrapper.UpdateUserTitle)
}
type PostMediaUploadRequestObject struct {
Body io.Reader
MultipartBody *multipart.Reader
}
type PostMediaUploadResponseObject interface {
VisitPostMediaUploadResponse(w http.ResponseWriter) error
}
type PostMediaUpload200JSONResponse Image
func (response PostMediaUpload200JSONResponse) VisitPostMediaUploadResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
type PostMediaUpload400JSONResponse string
func (response PostMediaUpload400JSONResponse) VisitPostMediaUploadResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type PostMediaUpload415Response struct {
}
func (response PostMediaUpload415Response) VisitPostMediaUploadResponse(w http.ResponseWriter) error {
w.WriteHeader(415)
return nil
}
type PostMediaUpload500Response struct {
}
func (response PostMediaUpload500Response) VisitPostMediaUploadResponse(w http.ResponseWriter) error {
w.WriteHeader(500)
return nil
}
type GetTitlesRequestObject struct {
Params GetTitlesParams
}
@ -1403,6 +1472,9 @@ func (response UpdateUserTitle500Response) VisitUpdateUserTitleResponse(w http.R
// StrictServerInterface represents all server handlers.
type StrictServerInterface interface {
// Upload an image (PNG, JPEG, or WebP)
// (POST /media/upload)
PostMediaUpload(ctx context.Context, request PostMediaUploadRequestObject) (PostMediaUploadResponseObject, error)
// Get titles
// (GET /titles)
GetTitles(ctx context.Context, request GetTitlesRequestObject) (GetTitlesResponseObject, error)
@ -1447,6 +1519,43 @@ type strictHandler struct {
middlewares []StrictMiddlewareFunc
}
// PostMediaUpload operation middleware
func (sh *strictHandler) PostMediaUpload(ctx *gin.Context) {
var request PostMediaUploadRequestObject
if strings.HasPrefix(ctx.GetHeader("Content-Type"), "encoding") {
request.Body = ctx.Request.Body
}
if strings.HasPrefix(ctx.GetHeader("Content-Type"), "multipart/form-data") {
if reader, err := ctx.Request.MultipartReader(); err == nil {
request.MultipartBody = reader
} else {
ctx.Error(err)
return
}
}
handler := func(ctx *gin.Context, request interface{}) (interface{}, error) {
return sh.ssi.PostMediaUpload(ctx, request.(PostMediaUploadRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "PostMediaUpload")
}
response, err := handler(ctx, request)
if err != nil {
ctx.Error(err)
ctx.Status(http.StatusInternalServerError)
} else if validResponse, ok := response.(PostMediaUploadResponseObject); ok {
if err := validResponse.VisitPostMediaUploadResponse(ctx.Writer); err != nil {
ctx.Error(err)
}
} else if response != nil {
ctx.Error(fmt.Errorf("unexpected response type: %T", response))
}
}
// GetTitles operation middleware
func (sh *strictHandler) GetTitles(ctx *gin.Context, params GetTitlesParams) {
var request GetTitlesRequestObject

View file

@ -19,7 +19,9 @@ paths:
$ref: "./paths/users-id-titles.yaml"
/users/{user_id}/titles/{title_id}:
$ref: "./paths/users-id-titles-id.yaml"
/media/upload:
$ref: "./paths/media_upload.yaml"
components:
parameters:
$ref: "./parameters/_index.yaml"

View file

@ -0,0 +1,37 @@
post:
summary: Upload an image (PNG, JPEG, or WebP)
description: |
Uploads a single image file. Supported formats: **PNG**, **JPEG/JPG**, **WebP**.
requestBody:
required: true
content:
multipart/form-data:
schema:
image:
type: string
format: binary
description: Image file (PNG, JPEG, or WebP)
encoding:
image:
contentType: image/png, image/jpeg, image/webp
responses:
'200':
description: Image uploaded successfully
content:
application/json:
schema:
$ref: "../schemas/Image.yaml"
'400':
description: Bad request — e.g., invalid/malformed image, empty file
content:
application/json:
schema:
type: string
'415':
description: |
Unsupported Media Type — e.g., request `Content-Type` is not `multipart/form-data`,
or the `image` part has an unsupported `Content-Type` (not image/png, image/jpeg, or image/webp)
'500':
description: Internal server error