mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
33a1c32cf8
Reorganize internal packages into platform/infra/shared layers and update imports, docs, and seed-count tests to match current system configs.
116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package ingest
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/infra/objectstore"
|
|
)
|
|
|
|
// LocalFileCandidateRequest describes filesystem locations that may host a legacy blob.
|
|
type LocalFileCandidateRequest struct {
|
|
StoredPath string
|
|
RelativePaths []string
|
|
}
|
|
|
|
// ResolveLocalFile returns the first existing regular file among managed candidate paths.
|
|
func ResolveLocalFile(ctx context.Context, req LocalFileCandidateRequest) (string, int64, error) {
|
|
for _, candidate := range buildLocalFileCandidates(ctx, req) {
|
|
info, err := os.Stat(candidate) //nolint:gosec // candidate is resolved from managed legacy metadata
|
|
if err != nil || info.IsDir() {
|
|
continue
|
|
}
|
|
return candidate, info.Size(), nil
|
|
}
|
|
return "", 0, os.ErrNotExist
|
|
}
|
|
|
|
// FromLocalPath ingests a local regular file through the standard upload ingest path.
|
|
func FromLocalPath(ctx context.Context, localPath string, req Request) (Result, error) {
|
|
localPath = strings.TrimSpace(localPath)
|
|
if localPath == "" {
|
|
return Result{}, errors.New("local path is required")
|
|
}
|
|
file, err := os.Open(localPath) //nolint:gosec // localPath is resolved from managed legacy metadata
|
|
if err != nil {
|
|
return Result{}, err
|
|
}
|
|
defer func() { _ = file.Close() }()
|
|
|
|
info, err := file.Stat()
|
|
if err != nil {
|
|
return Result{}, err
|
|
}
|
|
if info.IsDir() {
|
|
return Result{}, errors.New("local path must be a regular file")
|
|
}
|
|
if req.Size <= 0 {
|
|
req.Size = info.Size()
|
|
}
|
|
req.Reader = file
|
|
return Ingest(ctx, req)
|
|
}
|
|
|
|
func buildLocalFileCandidates(ctx context.Context, req LocalFileCandidateRequest) []string {
|
|
seen := make(map[string]struct{})
|
|
const (
|
|
initialCandidatesCap = 8
|
|
relativePathsWeight = 4
|
|
)
|
|
candidates := make([]string, 0, initialCandidatesCap+len(req.RelativePaths)*relativePathsWeight)
|
|
add := func(raw string) {
|
|
value := strings.TrimSpace(raw)
|
|
if value == "" {
|
|
return
|
|
}
|
|
if _, ok := seen[value]; ok {
|
|
return
|
|
}
|
|
seen[value] = struct{}{}
|
|
candidates = append(candidates, value)
|
|
}
|
|
|
|
storedPath := strings.TrimSpace(req.StoredPath)
|
|
add(storedPath)
|
|
if storedPath != "" {
|
|
add(filepath.Clean(storedPath))
|
|
add(strings.ReplaceAll(storedPath, "/data/data/", "/data/"))
|
|
add(strings.ReplaceAll(
|
|
filepath.Clean(storedPath),
|
|
string(filepath.Separator)+string(filepath.Separator),
|
|
string(filepath.Separator),
|
|
))
|
|
}
|
|
for _, relativePath := range req.RelativePaths {
|
|
add(relativePath)
|
|
}
|
|
|
|
for _, root := range localStorageRoots(ctx) {
|
|
if storedPath != "" && !filepath.IsAbs(storedPath) {
|
|
add(filepath.Join(root, storedPath))
|
|
}
|
|
for _, relativePath := range req.RelativePaths {
|
|
add(filepath.Join(root, relativePath))
|
|
}
|
|
}
|
|
return candidates
|
|
}
|
|
|
|
func localStorageRoots(ctx context.Context) []string {
|
|
cfg, err := objectstore.LoadConfig(ctx)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
root := strings.TrimSpace(cfg.Local.Root)
|
|
if root == "" {
|
|
return nil
|
|
}
|
|
return []string{root}
|
|
}
|