package core import ( "context" "errors" "fmt" "log" "path/filepath" "github.com/syonad/clonepack/internal/clone/rpm" "github.com/syonad/clonepack/internal/store" ) var ErrCloneAlreadyRunning = errors.New("a clone job is already running for this repo") type CloneService struct { repoStore store.RepoStore jobStore store.CloneJobStore dataDir string } func NewCloneService(repoStore store.RepoStore, jobStore store.CloneJobStore, dataDir string) *CloneService { return &CloneService{repoStore: repoStore, jobStore: jobStore, dataDir: dataDir} } func (s *CloneService) StartClone(ctx context.Context, repoID int64) (int64, error) { repo, err := s.repoStore.GetRepo(ctx, repoID) if err != nil { return 0, err } if repo.Type != "rpm" { return 0, fmt.Errorf("clone is only supported for rpm repositories") } running, err := s.jobStore.HasRunningCloneJob(ctx, repoID) if err != nil { return 0, err } if running { return 0, ErrCloneAlreadyRunning } jobID, err := s.jobStore.CreateCloneJob(ctx, repoID) if err != nil { return 0, err } go s.runClone(repoID, jobID, repo.SourceURL, repo.Type) return jobID, nil } func (s *CloneService) GetLatestCloneJob(ctx context.Context, repoID int64) (*store.CloneJob, error) { return s.jobStore.GetLatestCloneJob(ctx, repoID) } func (s *CloneService) runClone(repoID, jobID int64, sourceURL, repoType string) { ctx := context.Background() if err := s.jobStore.MarkCloneJobStarted(ctx, jobID); err != nil { log.Printf("clone job %d: failed to mark started: %v", jobID, err) return } destDir := filepath.Join(s.dataDir, "repos", fmt.Sprintf("%d", repoID), repoType) cloner := rpm.New(sourceURL, destDir) cloner.OnProgress = func(p rpm.Progress) { log.Printf("clone job %d: %s (%d bytes)", jobID, p.File, p.BytesDone) } if err := cloner.Clone(ctx); err != nil { errMsg := err.Error() _ = s.jobStore.MarkCloneJobFinished(ctx, jobID, store.CloneJobFailed, &errMsg) log.Printf("clone job %d: failed: %v", jobID, err) return } if err := rpm.RegenerateMetadata(destDir); err != nil { log.Printf("clone job %d: metadata regeneration failed: %v", jobID, err) } _ = s.jobStore.MarkCloneJobFinished(ctx, jobID, store.CloneJobCompleted, nil) log.Printf("clone job %d: completed", jobID) }