package database import ( "eden-server/internal/utility" "errors" "fmt" "log/slog" "os" "path/filepath" "gorm.io/gorm" ) func filesystemGather(env utility.Environment) (map[string]struct{}, error) { fsFiles, err := os.ReadDir(env.ContentDirectory) if err != nil { slog.Error("failed to read the content directory on the filesystem", "error", err) return nil, err } // generate a set of filesystem contents fsSet := make(map[string]struct{}) // cool name for the files that are (now) marked for annihilation for _, f := range fsFiles { // absolute path creation fullPath := filepath.Join(env.ContentDirectory, f.Name()) fsSet[fullPath] = struct{}{} } return fsSet, nil } func databaseGather(db *gorm.DB) (map[string]File, error) { dbFiles, err := GetFiles(db) if err != nil { slog.Error("failed to retrieve the files indexed from the database", "error", err) return nil, err } // generate a set of Database contents dbSet := make(map[string]File) // cool name for the files that are going to be deregistered from the database for _, f := range dbFiles { dbSet[f.FilePath] = f } return dbSet, nil } func watchdog(env utility.Environment, db *gorm.DB) { slog.Info("performing the watchdog cycle") fsSet, err := filesystemGather(env) if err != nil { return } dbSet, err := databaseGather(db) if err != nil { return } // FS -> DB // check for orphaned filesystem files var fsOrphans []string for path := range fsSet { if _, exists := dbSet[path]; !exists { fsOrphans = append(fsOrphans, path) } } if len(fsOrphans) > 0 { slog.Info("filesystem orphans detected, engaging flow") //filepath is stored in the slice, the filename or file object, see above for _, fp := range fsOrphans { // this switch is guarded by the environment.go its check making sure its one of the three // the following logic is used to actually perform the sync modes, removal, enrollment or ignore switch env.WatchdogSyncMode { case "sync": readerStream, err := os.Open(fp) if err != nil { } defer readerStream.Close() fileData, err := BuildFileRecord(readerStream, filepath.Base(fp), env.ContentDirectory) if err != nil { slog.Error("failed to enroll local file into the database", "error", err) } fmt.Println(fileData) if err := RegisterFile(db, fileData); err != nil { if errors.Is(err, gorm.ErrDuplicatedKey) { slog.Debug("discarding file since its a duplicate", "error", err) } else { slog.Error("failed to insert filedata to the database", "error", err) } } if err := os.Rename(fp, fileData.FilePath); err != nil { slog.Error("failed to move the locally inserted file", "error", err) } case "strict": err := utility.RemoveFile(fp) if err != nil { slog.Warn("failed to remove local file from the filesystem", "error", err) } case "dry": slog.Debug("dry mode enabled, not purging", "filepath", fp) } } } // DB -> FS // check stale database records var dbdbOrphans []File for path, f := range dbSet { if _, exists := fsSet[path]; !exists { dbdbOrphans = append(dbdbOrphans, f) } } if len(dbdbOrphans) > 0 { slog.Info("database orphans detected, engaging flow") for _, f := range dbdbOrphans { DeregisterFile(db, f) } } }