init push
This commit is contained in:
129
cmd/file-lifecycle-backfill/main.go
Normal file
129
cmd/file-lifecycle-backfill/main.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"wucher/internal/config"
|
||||
filemanager "wucher/internal/domain/file_manager"
|
||||
"wucher/internal/repository/mysql"
|
||||
s3repo "wucher/internal/repository/s3"
|
||||
)
|
||||
|
||||
type backfillReport struct {
|
||||
Total int `json:"total"`
|
||||
ActiveFiles int `json:"active_files"`
|
||||
ProbableOrphans int `json:"probable_orphan_files"`
|
||||
MissingS3Key int `json:"missing_s3_key"`
|
||||
InvalidReference int `json:"invalid_references"`
|
||||
Skipped int `json:"skipped"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
cfg, err := config.LoadFromFile(configFilePath())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "load config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
db, err := mysql.Connect(cfg.MySQL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "connect db: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fileRepo := mysql.NewFileManagerFileRepository(db)
|
||||
lifecycleRepo, ok := any(fileRepo).(filemanager.FileLifecycleRepository)
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, "file repository does not support lifecycle operations")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dryRun := true
|
||||
if raw := os.Getenv("BACKFILL_DRY_RUN"); raw != "" && (raw == "false" || raw == "0") {
|
||||
dryRun = false
|
||||
}
|
||||
|
||||
var storage filemanager.ObjectStorage
|
||||
if cfg.FileStorage.Bucket != "" && cfg.FileStorage.Region != "" {
|
||||
objStorage, s3Err := s3repo.NewFileObjectStorage(ctx, cfg.FileStorage)
|
||||
if s3Err == nil {
|
||||
storage = objStorage
|
||||
}
|
||||
}
|
||||
|
||||
rows := make([]filemanager.File, 0)
|
||||
if err := db.WithContext(ctx).Model(&filemanager.File{}).Find(&rows).Error; err != nil {
|
||||
fmt.Fprintf(os.Stderr, "query files: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
report := backfillReport{DryRun: dryRun}
|
||||
now := time.Now().UTC()
|
||||
for i := range rows {
|
||||
row := rows[i]
|
||||
report.Total++
|
||||
if row.DeletedAt != nil {
|
||||
report.Skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
referenced, refErr := lifecycleRepo.IsFileReferenced(ctx, row.ID)
|
||||
if refErr != nil {
|
||||
report.InvalidReference++
|
||||
report.Skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
if storage != nil {
|
||||
if _, headErr := storage.HeadObject(ctx, row.ObjectKey); headErr != nil {
|
||||
report.MissingS3Key++
|
||||
}
|
||||
}
|
||||
|
||||
if referenced {
|
||||
report.ActiveFiles++
|
||||
if !dryRun {
|
||||
_ = lifecycleRepo.UpdateLifecycle(ctx, filemanager.FileLifecycleTransitionParams{
|
||||
ID: row.ID,
|
||||
Status: filemanager.FileLifecycleStatusActive,
|
||||
AttachedAt: &now,
|
||||
ClearOrphanedAt: true,
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
report.ProbableOrphans++
|
||||
if !dryRun {
|
||||
_ = lifecycleRepo.UpdateLifecycle(ctx, filemanager.FileLifecycleTransitionParams{
|
||||
ID: row.ID,
|
||||
Status: filemanager.FileLifecycleStatusOrphanPending,
|
||||
OrphanedAt: &now,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
_ = enc.Encode(report)
|
||||
}
|
||||
|
||||
func configFilePath() string {
|
||||
if v := os.Getenv("CONFIG_FILE"); v != "" {
|
||||
return v
|
||||
}
|
||||
basePath := os.Getenv("CONFIG_BASE_PATH")
|
||||
if basePath == "" {
|
||||
basePath = "/tmp"
|
||||
}
|
||||
filename := os.Getenv("CONFIG_FILENAME")
|
||||
if filename == "" {
|
||||
filename = "config.generated.json"
|
||||
}
|
||||
return basePath + "/" + filename
|
||||
}
|
||||
Reference in New Issue
Block a user