mirror of
https://github.com/AlistGo/alist.git
synced 2025-12-19 11:00:06 +08:00
Adds storage class information to file metadata and API responses. This change introduces the ability to store file storage classes in file metadata and display them in API responses. This allows users to view a file's storage tier (e.g., S3 Standard, Glacier), enhancing data management capabilities. Implementation details include: - Introducing the StorageClassProvider interface and the ObjWrapStorageClass structure to uniformly handle and communicate object storage class information. - Updated file metadata structures (e.g., ArchiveObj, FileInfo, RespFile) to include a StorageClass field. - Modified relevant API response functions (e.g., GetFileInfo, GetFileList) to populate and return storage classes. - Integrated functionality for retrieving object storage classes from underlying storage systems (e.g., S3) and wrapping them in lists.
257 lines
5.1 KiB
Go
257 lines
5.1 KiB
Go
package model
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/alist-org/alist/v3/pkg/http_range"
|
|
"github.com/alist-org/alist/v3/pkg/utils"
|
|
"github.com/dlclark/regexp2"
|
|
|
|
mapset "github.com/deckarep/golang-set/v2"
|
|
|
|
"github.com/maruel/natural"
|
|
)
|
|
|
|
type ObjUnwrap interface {
|
|
Unwrap() Obj
|
|
}
|
|
|
|
type StorageClassProvider interface {
|
|
StorageClass() string
|
|
}
|
|
|
|
type Obj interface {
|
|
GetSize() int64
|
|
GetName() string
|
|
ModTime() time.Time
|
|
CreateTime() time.Time
|
|
IsDir() bool
|
|
GetHash() utils.HashInfo
|
|
|
|
// The internal information of the driver.
|
|
// If you want to use it, please understand what it means
|
|
GetID() string
|
|
GetPath() string
|
|
}
|
|
|
|
// FileStreamer ->check FileStream for more comments
|
|
type FileStreamer interface {
|
|
io.Reader
|
|
io.Closer
|
|
Obj
|
|
GetMimetype() string
|
|
//SetReader(io.Reader)
|
|
NeedStore() bool
|
|
IsForceStreamUpload() bool
|
|
GetExist() Obj
|
|
SetExist(Obj)
|
|
//for a non-seekable Stream, RangeRead supports peeking some data, and CacheFullInTempFile still works
|
|
RangeRead(http_range.Range) (io.Reader, error)
|
|
//for a non-seekable Stream, if Read is called, this function won't work
|
|
CacheFullInTempFile() (File, error)
|
|
SetTmpFile(r *os.File)
|
|
GetFile() File
|
|
}
|
|
|
|
type UpdateProgress func(percentage float64)
|
|
|
|
// Reference implementation from OpenListTeam:
|
|
// https://github.com/OpenListTeam/OpenList/blob/a703b736c9346c483bae56905a39bc07bf781cff/internal/model/obj.go#L58
|
|
func UpdateProgressWithRange(inner UpdateProgress, start, end float64) UpdateProgress {
|
|
return func(p float64) {
|
|
if p < 0 {
|
|
p = 0
|
|
}
|
|
if p > 100 {
|
|
p = 100
|
|
}
|
|
scaled := start + (end-start)*(p/100.0)
|
|
inner(scaled)
|
|
}
|
|
}
|
|
|
|
type URL interface {
|
|
URL() string
|
|
}
|
|
|
|
type Thumb interface {
|
|
Thumb() string
|
|
}
|
|
|
|
type SetPath interface {
|
|
SetPath(path string)
|
|
}
|
|
|
|
func SortFiles(objs []Obj, orderBy, orderDirection string) {
|
|
if orderBy == "" {
|
|
return
|
|
}
|
|
sort.Slice(objs, func(i, j int) bool {
|
|
switch orderBy {
|
|
case "name":
|
|
{
|
|
c := natural.Less(objs[i].GetName(), objs[j].GetName())
|
|
if orderDirection == "desc" {
|
|
return !c
|
|
}
|
|
return c
|
|
}
|
|
case "size":
|
|
{
|
|
if orderDirection == "desc" {
|
|
return objs[i].GetSize() >= objs[j].GetSize()
|
|
}
|
|
return objs[i].GetSize() <= objs[j].GetSize()
|
|
}
|
|
case "modified":
|
|
if orderDirection == "desc" {
|
|
return objs[i].ModTime().After(objs[j].ModTime())
|
|
}
|
|
return objs[i].ModTime().Before(objs[j].ModTime())
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
func ExtractFolder(objs []Obj, extractFolder string) {
|
|
if extractFolder == "" {
|
|
return
|
|
}
|
|
front := extractFolder == "front"
|
|
sort.SliceStable(objs, func(i, j int) bool {
|
|
if objs[i].IsDir() || objs[j].IsDir() {
|
|
if !objs[i].IsDir() {
|
|
return !front
|
|
}
|
|
if !objs[j].IsDir() {
|
|
return front
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
func WrapObjName(objs Obj) Obj {
|
|
return &ObjWrapName{Name: utils.MappingName(objs.GetName()), Obj: objs}
|
|
}
|
|
|
|
func WrapObjsName(objs []Obj) {
|
|
for i := 0; i < len(objs); i++ {
|
|
objs[i] = &ObjWrapName{Name: utils.MappingName(objs[i].GetName()), Obj: objs[i]}
|
|
}
|
|
}
|
|
|
|
func WrapObjStorageClass(obj Obj, storageClass string) Obj {
|
|
if storageClass == "" {
|
|
return obj
|
|
}
|
|
return &ObjWrapStorageClass{Obj: obj, storageClass: storageClass}
|
|
}
|
|
|
|
func UnwrapObj(obj Obj) Obj {
|
|
if unwrap, ok := obj.(ObjUnwrap); ok {
|
|
obj = unwrap.Unwrap()
|
|
}
|
|
return obj
|
|
}
|
|
|
|
func GetThumb(obj Obj) (thumb string, ok bool) {
|
|
if obj, ok := obj.(Thumb); ok {
|
|
return obj.Thumb(), true
|
|
}
|
|
if unwrap, ok := obj.(ObjUnwrap); ok {
|
|
return GetThumb(unwrap.Unwrap())
|
|
}
|
|
return thumb, false
|
|
}
|
|
|
|
func GetUrl(obj Obj) (url string, ok bool) {
|
|
if obj, ok := obj.(URL); ok {
|
|
return obj.URL(), true
|
|
}
|
|
if unwrap, ok := obj.(ObjUnwrap); ok {
|
|
return GetUrl(unwrap.Unwrap())
|
|
}
|
|
return url, false
|
|
}
|
|
|
|
func GetStorageClass(obj Obj) (string, bool) {
|
|
if provider, ok := obj.(StorageClassProvider); ok {
|
|
value := provider.StorageClass()
|
|
if value == "" {
|
|
return "", false
|
|
}
|
|
return value, true
|
|
}
|
|
if unwrap, ok := obj.(ObjUnwrap); ok {
|
|
return GetStorageClass(unwrap.Unwrap())
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func GetRawObject(obj Obj) *Object {
|
|
switch v := obj.(type) {
|
|
case *ObjThumbURL:
|
|
return &v.Object
|
|
case *ObjThumb:
|
|
return &v.Object
|
|
case *ObjectURL:
|
|
return &v.Object
|
|
case *Object:
|
|
return v
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Merge
|
|
func NewObjMerge() *ObjMerge {
|
|
return &ObjMerge{
|
|
set: mapset.NewSet[string](),
|
|
}
|
|
}
|
|
|
|
type ObjMerge struct {
|
|
regs []*regexp2.Regexp
|
|
set mapset.Set[string]
|
|
}
|
|
|
|
func (om *ObjMerge) Merge(objs []Obj, objs_ ...Obj) []Obj {
|
|
newObjs := make([]Obj, 0, len(objs)+len(objs_))
|
|
newObjs = om.insertObjs(om.insertObjs(newObjs, objs...), objs_...)
|
|
return newObjs
|
|
}
|
|
|
|
func (om *ObjMerge) insertObjs(objs []Obj, objs_ ...Obj) []Obj {
|
|
for _, obj := range objs_ {
|
|
if om.clickObj(obj) {
|
|
objs = append(objs, obj)
|
|
}
|
|
}
|
|
return objs
|
|
}
|
|
|
|
func (om *ObjMerge) clickObj(obj Obj) bool {
|
|
for _, reg := range om.regs {
|
|
if isMatch, _ := reg.MatchString(obj.GetName()); isMatch {
|
|
return false
|
|
}
|
|
}
|
|
return om.set.Add(obj.GetName())
|
|
}
|
|
|
|
func (om *ObjMerge) InitHideReg(hides string) {
|
|
rs := strings.Split(hides, "\n")
|
|
om.regs = make([]*regexp2.Regexp, 0, len(rs))
|
|
for _, r := range rs {
|
|
om.regs = append(om.regs, regexp2.MustCompile(r, regexp2.None))
|
|
}
|
|
}
|
|
|
|
func (om *ObjMerge) Reset() {
|
|
om.set.Clear()
|
|
}
|