mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 05:31:44 +08:00
145 lines
2.8 KiB
Go
145 lines
2.8 KiB
Go
package storage
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/shirou/gopsutil/v4/disk"
|
|
|
|
pkgio "github.com/acepanel/panel/pkg/io"
|
|
)
|
|
|
|
type Local struct {
|
|
basePath string
|
|
}
|
|
|
|
func NewLocal(basePath string) (Storage, error) {
|
|
if basePath == "" {
|
|
return nil, errors.New("base path is empty")
|
|
}
|
|
return &Local{
|
|
basePath: basePath,
|
|
}, nil
|
|
}
|
|
|
|
// Delete 删除文件
|
|
func (l *Local) Delete(files ...string) error {
|
|
for _, file := range files {
|
|
fullPath := l.fullPath(file)
|
|
if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Exists 检查文件是否存在
|
|
func (l *Local) Exists(file string) bool {
|
|
fullPath := l.fullPath(file)
|
|
_, err := os.Stat(fullPath)
|
|
return !os.IsNotExist(err)
|
|
}
|
|
|
|
// LastModified 获取文件最后修改时间
|
|
func (l *Local) LastModified(file string) (time.Time, error) {
|
|
fullPath := l.fullPath(file)
|
|
info, err := os.Stat(fullPath)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return info.ModTime(), nil
|
|
}
|
|
|
|
// List 列出目录下的所有文件
|
|
func (l *Local) List(path string) ([]string, error) {
|
|
fullPath := l.fullPath(path)
|
|
entries, err := os.ReadDir(fullPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var files []string
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
files = append(files, entry.Name())
|
|
}
|
|
}
|
|
return files, nil
|
|
}
|
|
|
|
// Put 写入文件内容
|
|
func (l *Local) Put(file string, content io.Reader) error {
|
|
fullPath := l.fullPath(file)
|
|
|
|
// 确保目录存在
|
|
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 预检查空间
|
|
if err := l.preCheckPath(filepath.Dir(fullPath)); err != nil {
|
|
return fmt.Errorf("pre check path failed: %w", err)
|
|
}
|
|
|
|
f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func(f *os.File) { _ = f.Close() }(f)
|
|
|
|
_, err = io.Copy(f, content)
|
|
return err
|
|
}
|
|
|
|
// Size 获取文件大小
|
|
func (l *Local) Size(file string) (int64, error) {
|
|
fullPath := l.fullPath(file)
|
|
info, err := os.Stat(fullPath)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return info.Size(), nil
|
|
}
|
|
|
|
func (l *Local) fullPath(path string) string {
|
|
path = strings.TrimPrefix(path, "/")
|
|
if path == "" {
|
|
return l.basePath
|
|
}
|
|
if filepath.IsAbs(path) {
|
|
return path
|
|
}
|
|
return filepath.Join(l.basePath, path)
|
|
}
|
|
|
|
func (l *Local) preCheckPath(path string) error {
|
|
size, err := pkgio.SizeX(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files, err := pkgio.CountX(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
usage, err := disk.Usage(l.basePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if uint64(size) > usage.Free {
|
|
return errors.New("insufficient backup directory space")
|
|
}
|
|
if uint64(files) > usage.InodesFree {
|
|
return errors.New("insufficient backup directory inode")
|
|
}
|
|
|
|
return nil
|
|
}
|