feat: refactor watchdog

This commit is contained in:
2026-04-28 17:18:14 +02:00
parent f589ae4faf
commit 0a98ee455f
11 changed files with 261 additions and 178 deletions
+57
View File
@@ -0,0 +1,57 @@
package watchdog
import (
"log/slog"
"orbits-server/internal/server/bootstrap"
"orbits-server/internal/server/database"
"orbits-server/internal/shared/utility"
"os"
"path/filepath"
"gorm.io/gorm"
)
func applyFS(env bootstrap.Environment, db *gorm.DB, fsOrphans []string) {
for _, fp := range fsOrphans {
switch env.WatchdogSyncMode {
case "sync":
f, err := os.Open(fp)
if err != nil {
continue
}
func() {
defer f.Close()
fileData, err := database.BuildFileRecord(
f,
filepath.Base(fp),
env.ContentDirectory,
)
if err != nil {
return
}
database.CreateFile(db, fileData)
os.Rename(fp, fileData.FilePath)
}()
case "strict":
utility.RemoveFile(fp)
case "dry":
slog.Debug("dry mode", "file", fp)
}
}
}
func applyDB(db *gorm.DB, dbOrphans []database.File) {
for _, f := range dbOrphans {
err := database.DeleteFileByID(db, f.ID)
if err != nil {
slog.Error("failed to apply database measures", "error", err)
}
}
}
+57
View File
@@ -0,0 +1,57 @@
package watchdog
import (
"orbits-server/internal/server/bootstrap"
"orbits-server/internal/server/database"
"os"
"path/filepath"
"gorm.io/gorm"
)
func reconcile(fs map[string]struct{}, db map[string]database.File) Result {
r := Result{}
for path := range fs {
if _, ok := db[path]; !ok {
r.FSOrphans = append(r.FSOrphans, path)
}
}
for path, f := range db {
if _, ok := fs[path]; !ok {
r.DBOrphans = append(r.DBOrphans, f)
}
}
return r
}
func scanFS(env bootstrap.Environment) (map[string]struct{}, error) {
fsFiles, err := os.ReadDir(env.ContentDirectory)
if err != nil {
return nil, err
}
res := make(map[string]struct{})
for _, f := range fsFiles {
full := filepath.Join(env.ContentDirectory, f.Name())
res[full] = struct{}{}
}
return res, nil
}
func scanDB(db *gorm.DB) (map[string]database.File, error) {
files, err := database.ListFiles(db)
if err != nil {
return nil, err
}
res := make(map[string]database.File)
for _, f := range files {
res[f.FilePath] = f
}
return res, nil
}
+61
View File
@@ -0,0 +1,61 @@
package watchdog
import (
"log/slog"
"orbits-server/internal/server/bootstrap"
"orbits-server/internal/server/database"
"time"
"gorm.io/gorm"
)
type State struct {
FS map[string]struct{}
DB map[string]database.File
}
type Result struct {
FSOrphans []string
DBOrphans []database.File
}
func Kickoff(env bootstrap.Environment, db *gorm.DB) {
interval := time.Second * time.Duration(env.WatchdogInterval)
ticker := time.NewTicker(interval)
go func() {
defer ticker.Stop()
run(env, db)
for range ticker.C {
run(env, db)
}
}()
}
func run(env bootstrap.Environment, db *gorm.DB) {
slog.Debug("watchdog cycle start")
fsState, err := scanFS(env)
if err != nil {
return
}
dbState, err := scanDB(db)
if err != nil {
return
}
result := reconcile(fsState, dbState)
if len(result.FSOrphans) > 0 {
slog.Info("filesystem orphans detected")
applyFS(env, db, result.FSOrphans)
}
if len(result.DBOrphans) > 0 {
slog.Info("database orphans detected")
applyDB(db, result.DBOrphans)
}
}