Files
2026-07-27 19:31:43 +08:00

164 lines
4.2 KiB
Go

package localfs
import (
"context"
"os"
"path/filepath"
"strings"
"litepan/internal/domain"
"litepan/internal/driver"
)
// Addition 是本地文件系统驱动的配置。
type Addition struct {
RootPath string `json:"root_path" form:"required,full" type:"local_dir" label:"本地根目录"`
}
// Driver 把本地目录作为网盘驱动(路径型 ID)。
type Driver struct {
add Addition
rootReal string
}
var config = driver.Config{
Name: "localfs",
DisplayName: "本机存储",
Description: "适合将 STRM 目录通过 WebDAV 暴露给爆米花等播放器",
CardTags: []string{"本地目录", "容器挂载"},
SortOrder: 100,
AuthLabel: "本地",
CardColor: "#64748b",
CardLogo: "/logos/local.png",
DefaultRoot: "/",
AuthType: driver.AuthNone,
}
func New() driver.Driver { return &Driver{} }
func (d *Driver) Config() driver.Config { return config }
func (d *Driver) GetAddition() any { return &d.add }
func (d *Driver) Init(ctx context.Context) error {
_ = ctx
raw := strings.TrimSpace(d.add.RootPath)
if raw == "" {
raw = "/"
}
abs, err := filepath.Abs(raw)
if err != nil {
return domain.Wrap(domain.CodeValidation, err)
}
info, err := os.Stat(abs)
if err != nil {
return domain.Wrap(domain.CodeValidation, err)
}
if !info.IsDir() {
return domain.Errorf(domain.CodeValidation, "本地路径不是目录")
}
real, err := filepath.EvalSymlinks(abs)
if err != nil {
return domain.Wrap(domain.CodeValidation, err)
}
d.add.RootPath = abs
d.rootReal = filepath.Clean(real)
return nil
}
func (d *Driver) Drop(ctx context.Context) error { return nil }
func (d *Driver) Ping(ctx context.Context) error {
_, err := os.Stat(d.root())
if err != nil {
return domain.Wrap(domain.CodeDriverError, err)
}
return nil
}
func (d *Driver) ExplainConnectionError(technical string, saving bool) string {
prefix := "添加失败"
if saving {
prefix = "保存失败"
}
lower := strings.ToLower(technical)
switch {
case strings.Contains(lower, "no such file") || strings.Contains(technical, "路径不存在"):
return prefix + ":本地路径不存在,请确认目录已挂载且路径正确"
case strings.Contains(lower, "not a directory") || strings.Contains(technical, "不是目录"):
return prefix + ":请填写目录路径,而不是文件路径"
case strings.Contains(lower, "permission") || strings.Contains(technical, "不可读"):
return prefix + ":本地路径不可读,请检查读取权限"
default:
return ""
}
}
func (d *Driver) ListFiles(ctx context.Context, parentID string) ([]domain.FileItem, error) {
_ = ctx
dir, err := d.resolveDir(parentID)
if err != nil {
return nil, err
}
entries, err := os.ReadDir(dir)
if err != nil {
return nil, domain.Wrap(domain.CodeDriverError, err)
}
items := make([]domain.FileItem, 0, len(entries))
for _, e := range entries {
info, err := e.Info()
if err != nil {
continue
}
items = append(items, domain.FileItem{
ID: filepath.Join(dir, e.Name()),
Name: e.Name(),
Size: info.Size(),
IsDir: e.IsDir(),
ModTime: info.ModTime(),
IDKind: domain.IDPath,
})
}
return items, nil
}
func (d *Driver) GetFileInfo(_ context.Context, fileID string) (*domain.FileItem, error) {
target, err := d.resolveEntry(fileID)
if err != nil {
return nil, err
}
info, err := os.Stat(target)
if err != nil {
return nil, domain.Wrap(domain.CodeNotFound, err)
}
return &domain.FileItem{
ID: target,
Name: info.Name(),
Size: info.Size(),
IsDir: info.IsDir(),
ModTime: info.ModTime(),
IDKind: domain.IDPath,
}, nil
}
func (d *Driver) root() string {
if d.add.RootPath == "" {
return "/"
}
return d.add.RootPath
}
func init() { driver.Register(New) }
var (
_ driver.ConnectionErrorExplainer = (*Driver)(nil)
_ driver.InfoGetter = (*Driver)(nil)
_ driver.Downloader = (*Driver)(nil)
_ driver.FolderCreator = (*Driver)(nil)
_ driver.Deleter = (*Driver)(nil)
_ driver.Mover = (*Driver)(nil)
_ driver.Copier = (*Driver)(nil)
_ driver.Renamer = (*Driver)(nil)
_ driver.LocalUploader = (*Driver)(nil)
)