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-10-07 22:44:53 +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
|
|
|
|
err := si.indexFiles(si.Root)
|
|
|
|
// 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
|
|
|
|
func (si *Index) indexFiles(path string) error {
|
2024-10-07 22:44:53 +00:00
|
|
|
// Ensure path is cleaned and normalized
|
2024-09-17 01:30:55 +00:00
|
|
|
adjustedPath := si.makeIndexPath(path, true)
|
2024-10-07 22:44:53 +00:00
|
|
|
|
|
|
|
// Open the directory
|
2023-12-01 23:47:00 +00:00
|
|
|
dir, err := os.Open(path)
|
|
|
|
if err != nil {
|
2024-10-07 22:44:53 +00:00
|
|
|
// If the directory can't be opened (e.g., deleted), remove it from the index
|
2023-12-01 23:47:00 +00:00
|
|
|
si.RemoveDirectory(adjustedPath)
|
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-10-07 22:44:53 +00:00
|
|
|
// Check if the directory is already up-to-date
|
|
|
|
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
|
|
|
// Recursively process files and directories
|
|
|
|
fileInfos := []*FileInfo{}
|
|
|
|
var totalSize int64
|
|
|
|
var numDirs, numFiles int
|
2023-12-01 23:47:00 +00:00
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
for _, file := range files {
|
|
|
|
parentInfo := &FileInfo{
|
|
|
|
Name: file.Name(),
|
|
|
|
Size: file.Size(),
|
|
|
|
ModTime: file.ModTime(),
|
|
|
|
IsDir: file.IsDir(),
|
2023-12-20 20:44:25 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
childInfo, err := si.InsertInfo(path, parentInfo)
|
|
|
|
if err != nil {
|
|
|
|
// Log error, but continue processing other files
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accumulate directory size and items
|
|
|
|
totalSize += childInfo.Size
|
|
|
|
if childInfo.IsDir {
|
|
|
|
numDirs++
|
|
|
|
} else {
|
|
|
|
numFiles++
|
|
|
|
}
|
|
|
|
_ = childInfo.detectType(path, true, false, false)
|
|
|
|
fileInfos = append(fileInfos, childInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create FileInfo for the current directory
|
|
|
|
dirFileInfo := &FileInfo{
|
|
|
|
Items: fileInfos,
|
|
|
|
Name: filepath.Base(path),
|
|
|
|
Size: totalSize,
|
|
|
|
ModTime: dirInfo.ModTime(),
|
|
|
|
CacheTime: time.Now(),
|
|
|
|
IsDir: true,
|
|
|
|
NumDirs: numDirs,
|
|
|
|
NumFiles: numFiles,
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
|
|
|
|
// Add directory to index
|
|
|
|
si.mu.Lock()
|
|
|
|
si.Directories[adjustedPath] = *dirFileInfo
|
|
|
|
si.NumDirs += numDirs
|
|
|
|
si.NumFiles += numFiles
|
|
|
|
si.mu.Unlock()
|
|
|
|
return nil
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
|
|
|
|
2024-10-07 22:44:53 +00:00
|
|
|
// InsertInfo function to handle adding a file or directory into the index
|
|
|
|
func (si *Index) InsertInfo(parentPath string, file *FileInfo) (*FileInfo, error) {
|
|
|
|
filePath := filepath.Join(parentPath, file.Name)
|
|
|
|
|
|
|
|
// Check if it's a directory and recursively index it
|
|
|
|
if file.IsDir {
|
|
|
|
// Recursively index directory
|
|
|
|
err := si.indexFiles(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
|
|
|
|
// Return directory info from the index
|
|
|
|
adjustedPath := si.makeIndexPath(filePath, true)
|
|
|
|
si.mu.RLock()
|
|
|
|
dirInfo := si.Directories[adjustedPath]
|
|
|
|
si.mu.RUnlock()
|
|
|
|
return &dirInfo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create FileInfo for regular files
|
|
|
|
fileInfo := &FileInfo{
|
|
|
|
Path: filePath,
|
|
|
|
Name: file.Name,
|
|
|
|
Size: file.Size,
|
|
|
|
ModTime: file.ModTime,
|
|
|
|
IsDir: false,
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
|
|
|
|
return fileInfo, nil
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
|
|
|
|
2024-09-16 21:01:16 +00:00
|
|
|
func (si *Index) makeIndexPath(subPath string, isDir bool) string {
|
|
|
|
if 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-09-16 21:01:16 +00:00
|
|
|
// add leading slash for root of index
|
2023-12-01 23:47:00 +00:00
|
|
|
if adjustedPath == "" {
|
|
|
|
adjustedPath = "/"
|
2024-09-16 21:01:16 +00:00
|
|
|
} else if !isDir {
|
|
|
|
adjustedPath = filepath.Dir(adjustedPath)
|
2023-12-01 23:47:00 +00:00
|
|
|
}
|
2024-10-07 22:44:53 +00:00
|
|
|
if !strings.HasPrefix(adjustedPath, "/") {
|
|
|
|
adjustedPath = "/" + adjustedPath
|
|
|
|
}
|
2023-12-01 23:47:00 +00:00
|
|
|
return adjustedPath
|
|
|
|
}
|