/* * Copyright 2026 Oleg Borodin */ package handler import ( "io" "net/http" "mstore/app/fileoper" "mstore/app/router" "mstore/pkg/terms" ) const zeroContentLength = "0" // GetProperty godoc // // @Summary Get file info // @Description Get file info // @Tags file // @Param filepath path string true "File path" // @Success 200 // @Failure 404,405 // @Router /v3/file/{filepath} [HEAD] // @Header 200 {int64} Content-Size "File size" // @Header 200 {string} Content-Digest "File digest" // @Header 200 {string} Content-Name "File name" // @Header 200 {string} Content-Collection "File collection" func (hand *Handler) FileInfo(rctx *router.Context) { filepath, _ := rctx.GetSubpath("filepath") params := &fileoper.FileInfoParams{ Filepath: filepath, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadFiles, params.Filepath) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, res, err := hand.fiop.FileInfo(ctx, operatorID, params) if err != nil { hand.logg.Errorf("FileInfo error: %v", err) rctx.SetStatus(code) return } rctx.SetHeader("Content-Collection", res.ContentCollection) rctx.SetHeader("Content-Name", res.ContentName) rctx.SetHeader("Content-Type", res.ContentType) rctx.SetHeader("Content-Size", res.ContentSize) rctx.SetHeader("Content-Digest", res.ContentDigest) rctx.SetHeader("Content-CreatedAt", res.ContentCreatedAt) rctx.SetHeader("Content-CreatedBy", string(res.ContentCreatedBy)) rctx.SetHeader("Content-UpdatedAt", res.ContentUpdatedAt) rctx.SetHeader("Content-UpdatedBy", string(res.ContentUpdatedBy)) rctx.SetHeader("Content-Length", zeroContentLength) rctx.SetStatus(code) } // GetProperty godoc // // @Summary Store file // @Description Store file // @Tags file // @Param Content-Size header int64 true "File size" // @Param Content-Type header string true "File type" // @Param filepath path string true "File path" // @Success 200 // @Failure 404,405 // @Router /v3/file/{filepath} [PUT] func (hand *Handler) PutFile(rctx *router.Context) { contentSize := rctx.GetHeader("Content-Size") contentType := rctx.GetHeader("Content-Type") filepath, _ := rctx.GetSubpath("filepath") params := &fileoper.PutFileParams{ Filepath: filepath, ContentType: contentType, ContentSize: contentSize, Source: rctx.Request.Body, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteFiles, params.Filepath) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, _, err := hand.fiop.PutFile(ctx, operatorID, params) if err != nil { hand.logg.Errorf("PutFile error: %v", err) rctx.SetStatus(code) return } rctx.SetStatus(code) } // GetProperty godoc // // @Summary Get file // @Description Get file // @Produce application/octet-stream // @Produce application/vnd.cncf.helm.chart.content.v1.tar+gzip // @Tags file // @Param filepath path string true "File path" // @Success 200 // @Failure 404,405 // @Router /v3/file/{filepath} [GET] func (hand *Handler) GetFile(rctx *router.Context) { filepath, _ := rctx.GetSubpath("filepath") params := &fileoper.GetFileParams{ Filepath: filepath, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadFiles, params.Filepath) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, res, err := hand.fiop.GetFile(ctx, operatorID, params) if err != nil { hand.logg.Errorf("PutFile error: %v", err) rctx.SetStatus(code) return } rctx.SetHeader("Content-Type", res.ContentType) rctx.SetHeader("Content-Size", res.ContentSize) rctx.SetHeader("Content-Digest", res.ContentDigest) rctx.SetHeader("Content-Length", res.ContentSize) rctx.SetHeader("Content-CreatedAt", res.ContentCreatedAt) rctx.SetHeader("Content-CreatedBy", string(res.ContentCreatedBy)) rctx.SetHeader("Content-UpdatedAt", res.ContentUpdatedAt) rctx.SetHeader("Content-UpdatedBy", string(res.ContentUpdatedBy)) rctx.SetStatus(code) if res.Source != nil { defer res.Source.Close() _, err = io.Copy(rctx.Writer, res.Source) if err != nil { hand.logg.Errorf("GetFile error: %v", err) return } } } // GetProperty godoc // // @Summary Delete file // @Description Delete file // @Tags file // @Param filepath path string true "File path" // @Success 200 // @Failure 404,405 // @Router /v3/file/{filepath} [DELETE] func (hand *Handler) DeleteFile(rctx *router.Context) { filepath, _ := rctx.GetSubpath("filepath") params := &fileoper.DeleteFileParams{ Filepath: filepath, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteFiles, params.Filepath) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, _, err := hand.fiop.DeleteFile(ctx, operatorID, params) if err != nil { hand.logg.Errorf("DeleteFIle error: %v", err) } rctx.SetStatus(code) } // GetProperty godoc // // @Summary List files // @Description List files // @Tags file // @Param path path string true "File path" // @Success 200 {array} descr.File // @Failure 404,405 // @Router /v3/files/{path} [GET] // @Produce application/json func (hand *Handler) ListFiles(rctx *router.Context) { filepath, _ := rctx.GetSubpath("filepath") if filepath == "" { filepath = "/" } params := &fileoper.ListFilesParams{ Filepath: filepath, } err := rctx.BindQuery(params) if err != nil { hand.logg.Errorf("ListFiles binding error: %v", err) rctx.SetStatus(http.StatusInternalServerError) return } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadFiles, params.Filepath) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, res, err := hand.fiop.ListFiles(ctx, operatorID, params) if err != nil { hand.logg.Errorf("ListFiles error: %v", err) rctx.SetStatus(code) return } rctx.SendJSON(code, res.Files) } func (hand *Handler) CheckFiles(rctx *router.Context) { filepath, _ := rctx.GetSubpath("path") if filepath == "" { filepath = "/" } params := &fileoper.CheckFilesParams{ Path: filepath, } err := rctx.BindQuery(params) if err != nil { hand.logg.Errorf("CheckFiles binding error: %v", err) rctx.SetStatus(http.StatusInternalServerError) return } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteFiles, params.Path) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, res, err := hand.fiop.CheckFiles(ctx, operatorID, params) if err != nil { hand.logg.Errorf("CheckFiles error: %v", err) rctx.SetStatus(code) return } rctx.SendJSON(code, res.Files) } // GetProperty godoc // // @Summary List collections // @Description List collections // @Tags file // @Param path path string true "Collection path" // @Success 200 {array} string // @Failure 404,405 // @Router /v3/collections/{path} [GET] // @Produce application/json func (hand *Handler) ListCollections(rctx *router.Context) { cpath, _ := rctx.GetSubpath("path") if cpath == "" { cpath = "/" } params := &fileoper.ListCollectionsParams{ Path: cpath, } err := rctx.BindQuery(params) if err != nil { hand.logg.Errorf("ListCollections binding error: %v", err) rctx.SetStatus(http.StatusInternalServerError) return } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadFiles, params.Path) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, res, err := hand.fiop.ListCollections(ctx, operatorID, params) if err != nil { hand.logg.Errorf("ListCollections error: %v", err) rctx.SetStatus(code) return } rctx.SendJSON(code, res.Collections) } // GetProperty godoc // // @Summary Delete collections // @Description Delete collections // @Tags file // @Param path path string true "Collection path" // @Success 200 // @Failure 404,405 // @Router /v3/collection/{path} [DELETE] func (hand *Handler) DeleteCollection(rctx *router.Context) { cpath, _ := rctx.GetSubpath("path") if cpath == "" { cpath = "/" } params := &fileoper.DeleteColletionParams{ Path: cpath, } err := rctx.BindQuery(params) if err != nil { hand.logg.Errorf("DeleteColletion binding error: %v", err) rctx.SetStatus(http.StatusInternalServerError) return } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadFiles, params.Path) if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() code, res, err := hand.fiop.DeleteColletion(ctx, operatorID, params) if err != nil { hand.logg.Errorf("DeleteColletion error: %v", err) rctx.SetStatus(code) return } rctx.SendJSON(code, res.Files) }