diff --git a/backend/Makefile b/backend/Makefile index 10bad81..57a5e48 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -1,4 +1,4 @@ -.PHONY: test build clean lint dev run-docker docs-upd docker +.PHONY: test build clean lint dev run-docker docs-upd docker docker-run test: @@ -29,6 +29,9 @@ docker: swag init -g ./cmd/main.go --parseDependency --parseInternal docker build -t backend . +docker-run: + docker run --rm -p 8080:8080 --env-file .env -v /opt/d3m0k1d.ru/data:/data backend:latest + run-docker: docker build -t backend . docker run --rm -p 8080:8080 --env-file .env backend:latest diff --git a/backend/docs/docs.go b/backend/docs/docs.go index b179ebf..03bf45c 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -412,6 +412,88 @@ const docTemplate = `{ } } } + }, + "/upload": { + "post": { + "description": "Upload static content to the server", + "produces": [ + "application/json" + ], + "tags": [ + "static" + ], + "summary": "Upload static content", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.SuccessResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse" + } + } + } + } + }, + "/upload/{file}": { + "get": { + "description": "Get static content", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "static" + ], + "summary": "Get static content", + "parameters": [ + { + "type": "string", + "description": "File name", + "name": "file", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Static content", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + }, + "404": { + "description": "File not found", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse" + } + } + } + } } }, "definitions": { diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 6f365dc..274c5f4 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -401,6 +401,88 @@ } } } + }, + "/upload": { + "post": { + "description": "Upload static content to the server", + "produces": [ + "application/json" + ], + "tags": [ + "static" + ], + "summary": "Upload static content", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.SuccessResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse" + } + } + } + } + }, + "/upload/{file}": { + "get": { + "description": "Get static content", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "static" + ], + "summary": "Get static content", + "parameters": [ + { + "type": "string", + "description": "File name", + "name": "file", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Static content", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + }, + "404": { + "description": "File not found", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse" + } + } + } + } } }, "definitions": { diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 98d6a4c..21f3420 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -296,6 +296,57 @@ paths: summary: Get user session tags: - auth + /upload: + post: + description: Upload static content to the server + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.SuccessResponse' + "500": + description: Internal server error + schema: + $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse' + summary: Upload static content + tags: + - static + /upload/{file}: + get: + consumes: + - application/json + description: Get static content + parameters: + - description: File name + in: path + name: file + required: true + type: string + produces: + - application/json + responses: + "200": + description: Static content + schema: + allOf: + - $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.SuccessResponse' + - properties: + data: + type: string + type: object + "404": + description: File not found + schema: + $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse' + "500": + description: Internal server error + schema: + $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_d3m0k1d_ru_backend_internal_models.ErrorResponse' + summary: Get static content + tags: + - static securityDefinitions: Bearer: description: Type "Bearer" followed by a space and the JWT token. diff --git a/backend/internal/handlers/registry_handlers.go b/backend/internal/handlers/registry_handlers.go index c4a916a..cb977a5 100644 --- a/backend/internal/handlers/registry_handlers.go +++ b/backend/internal/handlers/registry_handlers.go @@ -11,8 +11,12 @@ import ( func Register(router *gin.Engine, db *sql.DB) { handler_posts := NewPostHandlers(repositories.NewPostRepository(db)) handler_auth := NewAuthHandlers(repositories.NewAuthRepository(db)) + handler_static := NewStaticHandlers() router.GET("/health", func(c *gin.Context) { c.Status(200) }) v1 := router.Group("api/v1") + v1.Static("/uploads", "/data/uploads") + v1.POST("/upload", auth.JWTMiddleware(), auth.RequireAdmin(), handler_static.PostStatic) + v1.GET("/upload/:file", handler_static.GetStatic) v1.GET("/callback/github", handler_auth.CallbackGithub) v1.GET("/auth/github", handler_auth.LoginGithub) v1.GET("/session", auth.JWTMiddleware(), handler_auth.GetSession) diff --git a/backend/internal/handlers/static_handlers.go b/backend/internal/handlers/static_handlers.go new file mode 100644 index 0000000..e2da9bc --- /dev/null +++ b/backend/internal/handlers/static_handlers.go @@ -0,0 +1,59 @@ +package handlers + +import ( + "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/logger" + "gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/models" + + "github.com/gin-gonic/gin" +) + +type StaticHandlers struct { + logger *logger.Logger +} + +func NewStaticHandlers() *StaticHandlers { + return &StaticHandlers{ + logger: logger.New(false), + } +} + +// PostStatic godoc +// @Summary Upload static content +// @Description Upload static content to the server +// @Tags static +// @Produce json +// @Success 200 {object} models.SuccessResponse(data=string) "Static content" +// @Failure 500 {object} models.ErrorResponse "Internal server error" +// @Router /upload [post] +func (h *StaticHandlers) PostStatic(c *gin.Context) { + content, err := c.FormFile("file") + if err != nil { + h.logger.Error("error request: " + err.Error()) + models.Error(c, 500, "Internal server error", err.Error()) + return + } + dst := "/data/upload/" + content.Filename + if err = c.SaveUploadedFile(content, dst); err != nil { + h.logger.Error("error request: " + err.Error()) + models.Error(c, 500, "Internal server error", err.Error()) + return + } + models.Success(c, "Static content saved") +} + +// GetStatic godoc +// @Summary Get static content +// @Description Get static content +// @Tags static +// @Accept json +// @Produce json +// @Param file path string true "File name" +// @Success 200 {object} models.SuccessResponse{data=string} "Static content" +// @Failure 500 {object} models.ErrorResponse "Internal server error" +// @Failure 404 {object} models.ErrorResponse "File not found" +// @Router /upload/{file} [get] +func (h *StaticHandlers) GetStatic(c *gin.Context) { + // TODO: Unsecure handler need to be fixed + c.File("/data/upload/" + c.Param("file")) + +}