2023-12-01 23:47:00 +00:00
|
|
|
package files
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"os"
|
2024-09-16 21:01:16 +00:00
|
|
|
"path/filepath"
|
2023-12-01 23:47:00 +00:00
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gtsteffaniak/filebrowser/settings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Index struct {
|
|
|
|
Root string
|
2024-11-21 00:15:30 +00:00
|
|
|
Directories map[string]*FileInfo
|
2023-12-01 23:47:00 +00:00
|
|
|
NumDirs int
|
|
|
|
NumFiles int
|
|
|
|
inProgress bool
|
|
|
|
LastIndexed time.Time
|
|
|
|
mu sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
rootPath string = "/srv"
|
|
|
|
indexes []*Index
|
|
|
|
indexesMutex sync.RWMutex
|
|
|
|
)
|
|
|
|
|
|
|
|
func InitializeIndex(intervalMinutes uint32, schedule bool) {
|
|
|
|
if schedule {
|
|
|
|
go indexingScheduler(intervalMinutes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func indexingScheduler(intervalMinutes uint32) {
|
|
|
|
if settings.Config.Server.Root != "" {
|
|
|
|
rootPath = settings.Config.Server.Root
|
|
|
|
}
|
|
|
|
si := GetIndex(rootPath)
|
|
|
|
for {
|
|
|
|
startTime := time.Now()
|
|
|
|
// Set the indexing flag to indicate that indexing is in progress
|
|
|
|
si.resetCount()
|
|
|
|
// Perform the indexing operation
|
2024-11-21 00:15:30 +00:00
|
|
|
err := si.indexFiles("/")
|
2023-12-01 23:47:00 +00:00
|
|
|
// Reset the indexing flag to indicate that indexing has finished
|
|
|
|
si.inProgress = false
|
|
|
|
// Update the LastIndexed time
|
|
|
|
si.LastIndexed = time.Now()
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error during indexing: %v", err)
|
|
|
|
}
|
|
|
|
if si.NumFiles+si.NumDirs > 0 {
|
|
|
|
timeIndexedInSeconds := int(time.Since(startTime).Seconds())
|
|
|
|
log.Println("Successfully indexed files.")
|
|
|
|
log.Printf("Time spent indexing: %v seconds\n", timeIndexedInSeconds)
|
|
|
|
log.Printf("Files found: %v\n", si.NumFiles)
|
|
|
|
log.Printf("Directories found: %v\n", si.NumDirs)
|
|
|
|
}
|
|
|
|
// Sleep for the specified interval
|
|
|
|
time.Sleep(time.Duration(intervalMinutes) * time.Minute)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define a function to recursively index files and directories
|
2024-11-21 00:15:30 +00:00
|
|
|
func (si *Index) indexFiles(adjustedPath string) error {
|
|
|
|
realPath := strings.TrimRight(si.Root, "/") + adjustedPath
|
2024-10-07 22:44:53 +00:00
|
|
|
|
|
|
|
// Open the directory
|
2024-11-21 00:15:30 +00:00
|
|
|
dir, err := os.Open(realPath)
|
2023-12-01 23:47:00 +00:00
|
|
|
if err != nil {
|
2024-11-21 00:15:30 +00:00
|
|
|
si.RemoveDirectory(adjustedPath) // Remove if it can't be opened
|
2024-10-07 22:44:53 +00:00
|
|
|
return err
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
defer dir.Close()
|
|
|
|
|
2023-12-01 23:47:00 +00:00
|
|
|
dirInfo, err := dir.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-11-21 00:15:30 +00:00
|
|
|
// Skip directories that haven't been modified since the last index
|
2024-10-07 22:44:53 +00:00
|
|
|
if dirInfo.ModTime().Before(si.LastIndexed) {
|
2023-12-01 23:47:00 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
// Read directory contents
|
2023-12-01 23:47:00 +00:00
|
|
|
files, err := dir.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
var totalSize int64
|
|
|
|
var numDirs, numFiles int
|
2024-11-21 00:15:30 +00:00
|
|
|
fileInfos := []ReducedItem{}
|
|
|
|
dirInfos := map[string]*FileInfo{}
|
|
|
|
combinedPath := adjustedPath + "/"
|
|
|
|
if adjustedPath == "/" {
|
|
|
|
combinedPath = "/"
|
|
|
|
}
|
2023-12-01 23:47:00 +00:00
|
|
|
|
2024-11-21 00:15:30 +00:00
|
|
|
// Process each file and directory in the current directory
|
2024-10-07 22:44:53 +00:00
|
|
|
for _, file := range files {
|
2024-11-21 00:15:30 +00:00
|
|
|
itemInfo := &FileInfo{
|
2024-10-07 22:44:53 +00:00
|
|
|
ModTime: file.ModTime(),
|
|
|
|
}
|
2024-11-21 00:15:30 +00:00
|
|
|
if file.IsDir() {
|
|
|
|
itemInfo.Name = file.Name()
|
|
|
|
itemInfo.Path = combinedPath + file.Name()
|
|
|
|
// Recursively index the subdirectory
|
|
|
|
err := si.indexFiles(itemInfo.Path)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Failed to index directory %s: %v", itemInfo.Path, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Fetch the metadata for the subdirectory after indexing
|
|
|
|
subDirInfo, exists := si.GetMetadataInfo(itemInfo.Path, true)
|
|
|
|
if exists {
|
|
|
|
itemInfo.Size = subDirInfo.Size
|
|
|
|
totalSize += subDirInfo.Size // Add subdirectory size to the total
|
|
|
|
}
|
|
|
|
dirInfos[itemInfo.Name] = itemInfo
|
2024-10-07 22:44:53 +00:00
|
|
|
numDirs++
|
|
|
|
} else {
|
2024-11-21 00:15:30 +00:00
|
|
|
itemInfo := &ReducedItem{
|
|
|
|
Name: file.Name(),
|
|
|
|
ModTime: file.ModTime(),
|
|
|
|
Size: file.Size(),
|
|
|
|
Mode: file.Mode(),
|
|
|
|
}
|
|
|
|
_ = itemInfo.detectType(combinedPath+file.Name(), true, false, false)
|
|
|
|
fileInfos = append(fileInfos, *itemInfo)
|
|
|
|
totalSize += itemInfo.Size
|
2024-10-07 22:44:53 +00:00
|
|
|
numFiles++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create FileInfo for the current directory
|
|
|
|
dirFileInfo := &FileInfo{
|
2024-11-21 00:15:30 +00:00
|
|
|
Path: adjustedPath,
|
|
|
|
Files: fileInfos,
|
|
|
|
Dirs: dirInfos,
|
|
|
|
Size: totalSize,
|
|
|
|
ModTime: dirInfo.ModTime(),
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
|
2024-11-21 00:15:30 +00:00
|
|
|
// Update the current directory metadata in the index
|
|
|
|
si.UpdateMetadata(dirFileInfo)
|
2024-10-07 22:44:53 +00:00
|
|
|
si.NumDirs += numDirs
|
|
|
|
si.NumFiles += numFiles
|
2024-11-21 00:15:30 +00:00
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
return nil
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
|
|
|
|
2024-11-21 00:15:30 +00:00
|
|
|
func (si *Index) makeIndexPath(subPath string) string {
|
|
|
|
if strings.HasPrefix(subPath, "./") {
|
|
|
|
subPath = strings.TrimPrefix(subPath, ".")
|
2024-10-07 22:44:53 +00:00
|
|
|
}
|
2024-11-21 00:15:30 +00:00
|
|
|
if strings.HasPrefix(subPath, ".") || si.Root == subPath {
|
2023-12-01 23:47:00 +00:00
|
|
|
return "/"
|
|
|
|
}
|
2024-09-16 21:01:16 +00:00
|
|
|
// clean path
|
|
|
|
subPath = strings.TrimSuffix(subPath, "/")
|
|
|
|
// remove index prefix
|
|
|
|
adjustedPath := strings.TrimPrefix(subPath, si.Root)
|
|
|
|
// remove trailing slash
|
2023-12-01 23:47:00 +00:00
|
|
|
adjustedPath = strings.TrimSuffix(adjustedPath, "/")
|
2024-10-07 22:44:53 +00:00
|
|
|
if !strings.HasPrefix(adjustedPath, "/") {
|
|
|
|
adjustedPath = "/" + adjustedPath
|
|
|
|
}
|
2023-12-01 23:47:00 +00:00
|
|
|
return adjustedPath
|
|
|
|
}
|
2024-11-21 00:15:30 +00:00
|
|
|
|
|
|
|
//func getParentPath(path string) string {
|
|
|
|
// // Trim trailing slash for consistency
|
|
|
|
// path = strings.TrimSuffix(path, "/")
|
|
|
|
// if path == "" || path == "/" {
|
|
|
|
// return "" // Root has no parent
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// lastSlash := strings.LastIndex(path, "/")
|
|
|
|
// if lastSlash == -1 {
|
|
|
|
// return "/" // Parent of a top-level directory
|
|
|
|
// }
|
|
|
|
// return path[:lastSlash]
|
|
|
|
//}
|
|
|
|
|
|
|
|
func (si *Index) recursiveUpdateDirSizes(parentDir string, childInfo *FileInfo, previousSize int64) {
|
|
|
|
childDirName := filepath.Base(childInfo.Path)
|
|
|
|
if parentDir == childDirName {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
dir, exists := si.GetMetadataInfo(parentDir, true)
|
|
|
|
if !exists {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
dir.Dirs[childDirName] = childInfo
|
|
|
|
newSize := dir.Size - previousSize + childInfo.Size
|
|
|
|
dir.Size += newSize
|
|
|
|
si.UpdateMetadata(dir)
|
|
|
|
dir, _ = si.GetMetadataInfo(parentDir, true)
|
|
|
|
si.recursiveUpdateDirSizes(filepath.Dir(parentDir), dir, newSize)
|
|
|
|
}
|