package handler import ( "html/template" "path" "strconv" "time" "git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils" "github.com/docker/go-units" "github.com/valyala/fasthttp" "go.uber.org/zap" "golang.org/x/exp/slices" ) const ( dateFormat = "02-01-2006 15:04" attrOID, attrCreated, attrFileName, attrSize = "OID", "Created", "FileName", "Size" ) type ( BrowsePageData struct { BucketName, Prefix string Objects []ResponseObject } ResponseObject struct { OID string Created string FileName string Size string } ) func parseTimestamp(tstamp string) (time.Time, error) { millis, err := strconv.ParseInt(tstamp, 10, 64) if err != nil { return time.Time{}, err } return time.UnixMilli(millis), nil } func NewResponseObject(nodes map[string]string) ResponseObject { return ResponseObject{ OID: nodes[attrOID], Created: nodes[attrCreated], FileName: nodes[attrFileName], Size: nodes[attrSize], } } func formatTimestamp(strdate string) string { date, err := parseTimestamp(strdate) if err != nil || date.IsZero() { return "" } return date.Format(dateFormat) } func formatSize(strsize string) string { size, err := strconv.ParseFloat(strsize, 64) if err != nil { return "" } return units.HumanSize(size) } func (h *Handler) browseObjects(c *fasthttp.RequestCtx, bucketName, prefix string) { var log = h.log.With(zap.String("bucket", bucketName)) ctx := utils.GetContextFromRequest(c) nodes, err := h.listObjects(ctx, bucketName, prefix) if err != nil { logAndSendBucketError(c, log, err) return } respObjects := make([]ResponseObject, len(nodes)) for i, node := range nodes { respObjects[i] = NewResponseObject(node) } slices.SortFunc(respObjects, func(a, b ResponseObject) int { aIsDir := a.Size == "" bIsDir := b.Size == "" // prefix objects go first if aIsDir && !bIsDir { return -1 } else if !aIsDir && bIsDir { return 1 } if a.FileName < b.FileName { return -1 } return 1 }) templatePath := h.config.IndexPageTemplatePath() tmpl, err := template.New(path.Base(templatePath)).Funcs(template.FuncMap{ "formatTimestamp": formatTimestamp, "formatSize": formatSize, }).ParseFiles(templatePath) if err != nil { logAndSendBucketError(c, log, err) return } if err = tmpl.Execute(c, &BrowsePageData{ BucketName: bucketName, Prefix: prefix, Objects: respObjects, }); err != nil { logAndSendBucketError(c, log, err) return } }