package api import ( domainAPIKey "RenewCMS/internal/domain/apikey" domainArticle "RenewCMS/internal/domain/article" domainAudit "RenewCMS/internal/domain/audit" domainAuthor "RenewCMS/internal/domain/author" domainImage "RenewCMS/internal/domain/image" domainInvite "RenewCMS/internal/domain/invite" domainMail "RenewCMS/internal/domain/mail" domainPurge "RenewCMS/internal/domain/purge" domainSite "RenewCMS/internal/domain/site" domainUpdate "RenewCMS/internal/domain/update" domainUser "RenewCMS/internal/domain/user" "RenewCMS/internal/infrastructure/auditing" "RenewCMS/internal/infrastructure/config" "RenewCMS/internal/infrastructure/hashing" "RenewCMS/internal/infrastructure/imaging" "RenewCMS/internal/infrastructure/mailer" "RenewCMS/internal/infrastructure/markdown" "RenewCMS/internal/infrastructure/persistence/database" persistence "RenewCMS/internal/infrastructure/persistence/repositories" "RenewCMS/internal/infrastructure/scheduler" "RenewCMS/internal/infrastructure/storage" "RenewCMS/internal/infrastructure/update" "RenewCMS/internal/infrastructure/usage" "RenewCMS/internal/infrastructure/useCases" "context" "crypto/ed25519" "log" "net/http" "os" "path/filepath" "runtime" "sync" "time" "github.com/go-chi/jwtauth/v5" ) type UseCases struct { apiKeyAuthOnce sync.Once apiKeyAuthMiddleware func(http.Handler) http.Handler Token *jwtauth.JWTAuth PurgeScheduler *scheduler.Runner ActivityCleanupScheduler *scheduler.Runner CreateArticleUseCase *useCases.CreateArticleUseCase GetArticleUseCase *useCases.GetArticleUseCase ListArticlesUseCase *useCases.ListArticlesUseCase UpdateArticleUseCase *useCases.UpdateArticleUseCase DeleteArticleUseCase *useCases.DeleteArticleUseCase ListAuthorsUseCase *useCases.ListAuthorsUseCase CreateAuthorUseCase *useCases.CreateAuthorUseCase CreateMyAuthorUseCase *useCases.CreateMyAuthorUseCase UpdateAuthorUseCase *useCases.UpdateAuthorUseCase DeleteAuthorUseCase *useCases.DeleteAuthorUseCase GetMyAuthorUseCase *useCases.GetMyAuthorUseCase ListSitesUseCase *useCases.ListSitesUseCase CreateSiteUseCase *useCases.CreateSiteUseCase UpdateSiteUseCase *useCases.UpdateSiteUseCase SetDefaultSiteUseCase *useCases.SetDefaultSiteUseCase DeleteSiteUseCase *useCases.DeleteSiteUseCase GetUserUseCase *useCases.GetUserUseCase CreateUserUseCase *useCases.CreateUserUseCase AuthenticateUserUseCase *useCases.AuthenticateUserUseCase VerifyUserUseCase *useCases.VerifyUserUseCase CancelRegistrationUseCase *useCases.CancelRegistrationUseCase RequestPasswordResetUseCase *useCases.RequestPasswordResetUseCase ResetPasswordUseCase *useCases.ResetPasswordUseCase UpdateUserProfileUseCase *useCases.UpdateUserProfileUseCase ListUsersUseCase *useCases.ListUsersUseCase CreateInviteUseCase *useCases.CreateInviteUseCase CancelInviteUseCase *useCases.CancelInviteUseCase RenewInviteUseCase *useCases.RenewInviteUseCase ListUsersAndInvitesUseCase *useCases.ListUsersAndInvitesUseCase GetInviteUseCase *useCases.GetInviteUseCase AcceptInviteUseCase *useCases.AcceptInviteUseCase SendMailUseCase *useCases.SendMailUseCase CreateImageUseCase *useCases.CreateImageUseCase OpenMediaUseCase *useCases.OpenMediaUseCase ListMediaUseCase *useCases.ListMediaUseCase RenameMediaUseCase *useCases.RenameMediaUseCase DeleteMediaUseCase *useCases.DeleteMediaUseCase MergeMediaUseCase *useCases.MergeMediaUseCase GetMediaUsageUseCase *useCases.GetMediaUsageUseCase GetPurgeSettingsUseCase *useCases.GetPurgeSettingsUseCase UpdatePurgeSettingsUseCase *useCases.UpdatePurgeSettingsUseCase PreviewPurgeUseCase *useCases.PreviewPurgeUseCase RunPurgeUseCase *useCases.RunPurgeUseCase GetSettingsUseCase *useCases.GetSettingsUseCase GetUpdateStatusUseCase *useCases.GetUpdateStatusUseCase CheckForUpdateUseCase *useCases.CheckForUpdateUseCase StartUpdateDownloadUseCase *useCases.StartUpdateDownloadUseCase RequestRestartUseCase *useCases.RequestRestartUseCase RecordRunningInfoUseCase *useCases.RecordRunningInfoUseCase CreateAPIKeyUseCase *useCases.CreateAPIKeyUseCase ListAPIKeysUseCase *useCases.ListAPIKeysUseCase RevokeAPIKeyUseCase *useCases.RevokeAPIKeyUseCase AuthenticateAPIKeyUseCase *useCases.AuthenticateAPIKeyUseCase ListActivityUseCase *useCases.ListActivityUseCase GetActivitySummaryUseCase *useCases.GetActivitySummaryUseCase ExportActivityUseCase *useCases.ExportActivityUseCase GetActivityRetentionUseCase *useCases.GetActivityRetentionUseCase UpdateActivityRetentionUseCase *useCases.UpdateActivityRetentionUseCase ListVariantsUseCase *useCases.ListVariantsUseCase CreateVariantUseCase *useCases.CreateVariantUseCase UpdateVariantUseCase *useCases.UpdateVariantUseCase SetVariantEnabledUseCase *useCases.SetVariantEnabledUseCase DeleteVariantUseCase *useCases.DeleteVariantUseCase PurgeVariantUseCase *useCases.PurgeVariantUseCase ServeVariantUseCase *useCases.ServeVariantUseCase ListImageVariantsUseCase *useCases.ListImageVariantsUseCase GenerateImageVariantUseCase *useCases.GenerateImageVariantUseCase GenerateAllImageVariantsUseCase *useCases.GenerateAllImageVariantsUseCase PurgeImageVariantUseCase *useCases.PurgeImageVariantUseCase GenerateVariantsOnUploadUseCase *useCases.GenerateVariantsOnUploadUseCase GetVariantCacheStatsUseCase *useCases.GetVariantCacheStatsUseCase PurgeVariantCacheUseCase *useCases.PurgeVariantCacheUseCase VariantUsageTracker *usage.Tracker } func (c *UseCases) RunSchedulers(ctx context.Context) { var running sync.WaitGroup for _, runner := range []*scheduler.Runner{c.PurgeScheduler, c.ActivityCleanupScheduler} { if runner == nil { continue } running.Add(1) go func() { defer running.Done() runner.Run(ctx) }() } running.Wait() } type Repositories struct { ArticleRepo domainArticle.Repository SiteRepo domainSite.Repository AuthorRepo domainAuthor.Repository UserRepo domainUser.Repository ImageRepo domainImage.Repository VariantRepo domainImage.VariantRepository VariantFileRepo domainImage.VariantFileRepository VariantCacheRepo domainImage.VariantCacheRepository VariantUsageRepo domainImage.VariantUsageRepository VariantRenderer domainImage.VariantRenderer InviteRepo domainInvite.Repository APIKeyRepo domainAPIKey.Repository PurgeSettingsRepo domainPurge.Repository AuditRepo domainAudit.Repository AuditSettingsRepo domainAudit.SettingsRepository MailRepo domainMail.Repository ReleaseFeed domainUpdate.ReleaseFeed UpdateStore domainUpdate.StatusStore UpdateJobs domainUpdate.JobStore UpdateHelper domainUpdate.HelperDetector AssetFetcher domainUpdate.AssetFetcher SignatureVerifier domainUpdate.SignatureVerifier StagedRunner domainUpdate.StagedBinaryRunner DatabaseBackup domainUpdate.DatabaseBackup UpdateResults domainUpdate.ResultStore RestartRequests domainUpdate.RestartRequestStore RunningInfo domainUpdate.RunningInfoStore } const ( updateStatusFileName = "update-status.json" updateJobFileName = "update-job.json" updateHelperFileName = "update-helper.json" updatesDirName = "updates" backupsDirName = "backups" ) func imageStorageConfig(settings config.Settings) storage.Config { if settings.Storage.Driver == config.StorageDriverS3 { s3 := settings.Storage.S3 return storage.Config{ Driver: storage.DriverS3, S3: storage.S3Config{ Bucket: s3.Bucket, Region: s3.Region, Endpoint: s3.Endpoint, AccessKeyID: s3.AccessKeyID, SecretAccessKey: s3.SecretAccessKey, KeyPrefix: s3.KeyPrefix, PathStyle: s3.PathStyle, TLS: s3.TLS, }, } } return storage.Config{Driver: storage.DriverLocal, Location: settings.UploadDir} } func openImageStorage(settings config.Settings) domainImage.Storage { imageStorage, err := storage.Open(imageStorageConfig(settings)) if err != nil { panic("Unable to open the image storage: " + err.Error()) } return imageStorage } func databaseConfig(settings config.Settings) database.Config { switch settings.Database.Driver { case config.DatabaseDriverMySQL: return mysqlDatabaseConfig(settings.Database) case config.DatabaseDriverPostgres: return postgresDatabaseConfig(settings.Database) case "", config.DatabaseDriverSQLite: return database.Config{Driver: database.DriverSQLite, Source: settings.DBFile} default: return database.Config{Driver: settings.Database.Driver} } } func mysqlDatabaseConfig(settings config.DatabaseSettings) database.Config { return database.Config{ Driver: database.DriverMySQL, Source: database.MySQLDSN(database.MySQLConfig{ Host: settings.Host, Port: settings.Port, User: settings.User, Password: settings.Password, Name: settings.Name, TLS: settings.TLS, }), Pool: &database.PoolSettings{ MaxOpenConns: settings.MaxOpenConns, MaxIdleConns: settings.MaxIdleConns, ConnMaxLifetime: settings.ConnMaxLifetime, }, } } func postgresDatabaseConfig(settings config.DatabaseSettings) database.Config { return database.Config{ Driver: database.DriverPostgres, Source: database.PostgresDSN(database.PostgresConfig{ Host: settings.Host, Port: settings.Port, User: settings.User, Password: settings.Password, Name: settings.Name, SSLMode: settings.SSLMode, }), Pool: &database.PoolSettings{ MaxOpenConns: settings.MaxOpenConns, MaxIdleConns: settings.MaxIdleConns, ConnMaxLifetime: settings.ConnMaxLifetime, }, } } func openDatabase(settings config.Settings) *database.Connection { connection, err := database.Connect(databaseConfig(settings)) if err != nil { panic("Unable to open the database: " + err.Error()) } if err := database.Migrate(connection.DB, settings.PublicURL); err != nil { panic("Failed to migrate database: " + err.Error()) } return connection } func releasePublicKey() ed25519.PublicKey { key, err := update.EmbeddedPublicKey() if err != nil { panic("Unable to read the embedded release key: " + err.Error()) } return key } func getRepositories(connection *database.Connection, settings config.Settings) *Repositories { db := connection.DB dataDir := settings.DataDir() variantFiles := persistence.NewVariantFileRepository(db) return &Repositories{ ArticleRepo: persistence.NewArticleRepository(db), SiteRepo: persistence.NewSiteRepository(db), AuthorRepo: persistence.NewAuthorRepository(db), UserRepo: persistence.NewUserRepository(db), ImageRepo: persistence.NewImageRepository(db), VariantRepo: persistence.NewVariantRepository(db), VariantFileRepo: variantFiles, VariantCacheRepo: variantFiles, VariantUsageRepo: persistence.NewVariantUsageRepository(db), VariantRenderer: imaging.NewVariantRenderer(), InviteRepo: persistence.NewInviteRepository(db), APIKeyRepo: persistence.NewAPIKeyRepository(db), PurgeSettingsRepo: persistence.NewPurgeSettingsRepository(db), AuditRepo: persistence.NewAuditEventRepository(db), AuditSettingsRepo: persistence.NewAuditSettingsRepository(db), MailRepo: mailer.NewMailRepository(settings.Smtp), ReleaseFeed: update.NewGiteaReleaseFeed(settings.UpdateFeedURL), UpdateStore: update.NewJSONStatusStore(filepath.Join(dataDir, updateStatusFileName)), UpdateJobs: update.NewJSONJobStore(filepath.Join(dataDir, updateJobFileName)), UpdateHelper: update.NewContainerHelperDetector(update.NewFileHelperDetector(filepath.Join(dataDir, updateHelperFileName)), settings.Container), AssetFetcher: update.NewHTTPAssetFetcher(settings.UpdateFeedURL), SignatureVerifier: update.NewEd25519SignatureVerifier(releasePublicKey()), StagedRunner: update.NewExecStagedBinaryRunner(), DatabaseBackup: connection.NewBackup(filepath.Join(dataDir, backupsDirName), time.Now), UpdateResults: update.NewJSONResultStore(filepath.Join(dataDir, domainUpdate.ResultFileName)), RestartRequests: update.NewJSONRestartRequestStore(filepath.Join(dataDir, domainUpdate.RestartRequestFileName)), RunningInfo: update.NewJSONRunningInfoStore(filepath.Join(dataDir, domainUpdate.RunningInfoFileName)), } } func getUseCases(repos *Repositories, imageStorage domainImage.Storage, settings config.Settings) *UseCases { hasher := hashing.NewBcryptHasher() renderer := markdown.NewConverter() keyGenerator := hashing.NewSHA256KeyGenerator() recorder := auditing.NewRecorder(repos.AuditRepo, time.Now, log.Default()) mergeMedia := useCases.NewMergeMediaUseCase(repos.ImageRepo, repos.ArticleRepo, imageStorage, repos.VariantFileRepo, recorder, repos.SiteRepo, repos.AuthorRepo) runPurge := useCases.NewRunPurgeUseCase(repos.ImageRepo, repos.ArticleRepo, mergeMedia, repos.PurgeSettingsRepo, time.Now, log.Default(), recorder, repos.SiteRepo, repos.AuthorRepo) purgeScheduler := useCases.NewPurgeSchedulerUseCase(repos.PurgeSettingsRepo, runPurge, recorder) activityCleanup := useCases.NewCleanupActivityUseCase(repos.AuditRepo, repos.AuditSettingsRepo, recorder) usageTracker := usage.NewTracker(repos.VariantUsageRepo, time.Now) serveVariant := useCases.NewServeVariantUseCase(repos.VariantRepo, repos.VariantFileRepo, repos.ImageRepo, imageStorage, repos.VariantRenderer, useCases.ServeVariantLimits{MaxSourceBytes: settings.ImageVariantMaxSourceBytes, MaxSourcePixels: settings.ImageVariantMaxSourcePixels, MaxConcurrentRenders: settings.ImageVariantMaxConcurrentRenders}).WithUsageTracker(usageTracker) return &UseCases{ PurgeScheduler: scheduler.NewSystemRunner(purgeScheduler, log.Default()), ActivityCleanupScheduler: scheduler.NewSystemCleanupRunner(activityCleanup, log.Default()), Token: jwtauth.New("HS256", []byte(settings.JWTSecret), nil), CreateArticleUseCase: useCases.NewCreateArticleUseCase(repos.ArticleRepo, renderer, recorder, repos.SiteRepo, repos.AuthorRepo), GetArticleUseCase: useCases.NewGetArticleUseCase(repos.ArticleRepo, renderer), ListArticlesUseCase: useCases.NewListArticlesUseCase(repos.ArticleRepo, renderer, repos.SiteRepo), UpdateArticleUseCase: useCases.NewUpdateArticleUseCase(repos.ArticleRepo, renderer, recorder, repos.SiteRepo, repos.AuthorRepo), DeleteArticleUseCase: useCases.NewDeleteArticleUseCase(repos.ArticleRepo, recorder), ListAuthorsUseCase: useCases.NewListAuthorsUseCase(repos.AuthorRepo, repos.UserRepo), CreateAuthorUseCase: useCases.NewCreateAuthorUseCase(repos.AuthorRepo, repos.UserRepo, repos.ImageRepo, recorder), CreateMyAuthorUseCase: useCases.NewCreateMyAuthorUseCase(repos.AuthorRepo, repos.UserRepo, recorder), UpdateAuthorUseCase: useCases.NewUpdateAuthorUseCase(repos.AuthorRepo, repos.UserRepo, repos.ImageRepo, recorder), DeleteAuthorUseCase: useCases.NewDeleteAuthorUseCase(repos.AuthorRepo, recorder), GetMyAuthorUseCase: useCases.NewGetMyAuthorUseCase(repos.AuthorRepo), ListSitesUseCase: useCases.NewListSitesUseCase(repos.SiteRepo), CreateSiteUseCase: useCases.NewCreateSiteUseCase(repos.SiteRepo, repos.ImageRepo, recorder), UpdateSiteUseCase: useCases.NewUpdateSiteUseCase(repos.SiteRepo, repos.ImageRepo, recorder), SetDefaultSiteUseCase: useCases.NewSetDefaultSiteUseCase(repos.SiteRepo, recorder), DeleteSiteUseCase: useCases.NewDeleteSiteUseCase(repos.SiteRepo, recorder), GetUserUseCase: useCases.NewGetUserUseCase(repos.UserRepo), CreateUserUseCase: useCases.NewCreateUserUseCase(repos.UserRepo, hasher, repos.MailRepo, settings.PublicURL, recorder), AuthenticateUserUseCase: useCases.NewAuthenticateUserUseCase(repos.UserRepo, hasher, recorder), VerifyUserUseCase: useCases.NewVerifyUserUseCase(repos.UserRepo, hasher, recorder), CancelRegistrationUseCase: useCases.NewCancelRegistrationUseCase(repos.UserRepo), RequestPasswordResetUseCase: useCases.NewRequestPasswordResetUseCase(repos.UserRepo, hasher, repos.MailRepo, settings.PublicURL, recorder), ResetPasswordUseCase: useCases.NewResetPasswordUseCase(repos.UserRepo, hasher, repos.APIKeyRepo, time.Now, recorder), UpdateUserProfileUseCase: useCases.NewUpdateUserProfileUseCase(repos.UserRepo, recorder), ListUsersUseCase: useCases.NewListUsersUseCase(repos.UserRepo), CreateInviteUseCase: useCases.NewCreateInviteUseCase(repos.UserRepo, repos.InviteRepo, hasher, repos.MailRepo, settings.PublicURL, recorder), CancelInviteUseCase: useCases.NewCancelInviteUseCase(repos.InviteRepo, recorder), RenewInviteUseCase: useCases.NewRenewInviteUseCase(repos.UserRepo, repos.InviteRepo, hasher, repos.MailRepo, settings.PublicURL, recorder), ListUsersAndInvitesUseCase: useCases.NewListUsersAndInvitesUseCase(repos.UserRepo, repos.InviteRepo), GetInviteUseCase: useCases.NewGetInviteUseCase(repos.InviteRepo, hasher), AcceptInviteUseCase: useCases.NewAcceptInviteUseCase(repos.UserRepo, repos.InviteRepo, hasher, recorder), SendMailUseCase: useCases.NewSendMailUseCase(repos.MailRepo), CreateImageUseCase: useCases.NewCreateImageUseCase(repos.ImageRepo, imageStorage, recorder), OpenMediaUseCase: useCases.NewOpenMediaUseCase(imageStorage), ListMediaUseCase: useCases.NewListMediaUseCase(repos.ImageRepo, repos.ArticleRepo, repos.UserRepo, repos.VariantFileRepo, repos.SiteRepo, repos.AuthorRepo), RenameMediaUseCase: useCases.NewRenameMediaUseCase(repos.ImageRepo, recorder), DeleteMediaUseCase: useCases.NewDeleteMediaUseCase(repos.ImageRepo, imageStorage, repos.VariantFileRepo, recorder, repos.SiteRepo, repos.AuthorRepo), MergeMediaUseCase: mergeMedia, GetMediaUsageUseCase: useCases.NewGetMediaUsageUseCase(repos.ImageRepo, repos.ArticleRepo), GetPurgeSettingsUseCase: useCases.NewGetPurgeSettingsUseCase(repos.PurgeSettingsRepo), UpdatePurgeSettingsUseCase: useCases.NewUpdatePurgeSettingsUseCase(repos.PurgeSettingsRepo, time.Now), PreviewPurgeUseCase: useCases.NewPreviewPurgeUseCase(repos.ImageRepo, repos.ArticleRepo, repos.SiteRepo, repos.AuthorRepo), RunPurgeUseCase: runPurge, GetSettingsUseCase: useCases.NewGetSettingsUseCase(settings.Version), GetUpdateStatusUseCase: useCases.NewGetUpdateStatusUseCase(repos.UpdateStore, repos.UpdateJobs, repos.UpdateHelper, repos.UpdateResults, repos.RestartRequests, settings.Version, settings.Container, time.Now), CheckForUpdateUseCase: useCases.NewCheckForUpdateUseCase(repos.ReleaseFeed, repos.UpdateStore, settings.Version, time.Now, recorder), StartUpdateDownloadUseCase: startUpdateDownloadUseCase(repos, settings, recorder), RequestRestartUseCase: useCases.NewRequestRestartUseCase(repos.UpdateStore, repos.UpdateJobs, repos.UpdateHelper, repos.UpdateResults, repos.RestartRequests, settings.Version, time.Now, recorder), RecordRunningInfoUseCase: useCases.NewRecordRunningInfoUseCase(repos.RunningInfo, settings.Version, os.Getpid(), time.Now), CreateAPIKeyUseCase: useCases.NewCreateAPIKeyUseCase(repos.APIKeyRepo, keyGenerator, time.Now, recorder), ListAPIKeysUseCase: useCases.NewListAPIKeysUseCase(repos.APIKeyRepo, repos.UserRepo, time.Now), RevokeAPIKeyUseCase: useCases.NewRevokeAPIKeyUseCase(repos.APIKeyRepo, repos.UserRepo, time.Now, recorder), AuthenticateAPIKeyUseCase: useCases.NewAuthenticateAPIKeyUseCase(repos.APIKeyRepo, keyGenerator, repos.UserRepo, time.Now), ListActivityUseCase: useCases.NewListActivityUseCase(repos.AuditRepo, repos.UserRepo, repos.ArticleRepo, repos.ImageRepo, repos.APIKeyRepo, time.Now, repos.SiteRepo, repos.AuthorRepo), GetActivitySummaryUseCase: useCases.NewGetActivitySummaryUseCase(repos.AuditRepo, time.Now), ExportActivityUseCase: useCases.NewExportActivityUseCase(repos.AuditRepo, time.Now), GetActivityRetentionUseCase: useCases.NewGetActivityRetentionUseCase(repos.AuditSettingsRepo, repos.AuditRepo, time.Now), UpdateActivityRetentionUseCase: useCases.NewUpdateActivityRetentionUseCase(repos.AuditSettingsRepo, repos.AuditRepo, recorder, time.Now), ListVariantsUseCase: useCases.NewListVariantsUseCase(repos.VariantRepo, repos.VariantFileRepo), CreateVariantUseCase: useCases.NewCreateVariantUseCase(repos.VariantRepo, repos.VariantRenderer, time.Now, recorder), UpdateVariantUseCase: useCases.NewUpdateVariantUseCase(repos.VariantRepo, repos.VariantFileRepo, imageStorage, repos.VariantRenderer, time.Now, recorder), SetVariantEnabledUseCase: useCases.NewSetVariantEnabledUseCase(repos.VariantRepo, repos.VariantFileRepo, time.Now, recorder), DeleteVariantUseCase: useCases.NewDeleteVariantUseCase(repos.VariantRepo, repos.VariantFileRepo, imageStorage, recorder), PurgeVariantUseCase: useCases.NewPurgeVariantUseCase(repos.VariantRepo, repos.VariantFileRepo, imageStorage, recorder), ServeVariantUseCase: serveVariant, ListImageVariantsUseCase: useCases.NewListImageVariantsUseCase(repos.ImageRepo, repos.VariantRepo, repos.VariantFileRepo, repos.VariantRenderer), GenerateImageVariantUseCase: useCases.NewGenerateImageVariantUseCase(repos.ImageRepo, repos.VariantRepo, repos.VariantFileRepo, repos.VariantRenderer, serveVariant), GenerateAllImageVariantsUseCase: useCases.NewGenerateAllImageVariantsUseCase(repos.ImageRepo, repos.VariantRepo, repos.VariantFileRepo, repos.VariantRenderer, serveVariant, recorder), PurgeImageVariantUseCase: useCases.NewPurgeImageVariantUseCase(repos.ImageRepo, repos.VariantRepo, repos.VariantFileRepo, imageStorage, recorder), GenerateVariantsOnUploadUseCase: useCases.NewGenerateVariantsOnUploadUseCase(repos.VariantRepo, serveVariant, log.Default()), GetVariantCacheStatsUseCase: useCases.NewGetVariantCacheStatsUseCase(repos.VariantRepo, repos.VariantFileRepo, repos.VariantCacheRepo, repos.ImageRepo, repos.VariantUsageRepo, usageTracker, time.Now), PurgeVariantCacheUseCase: useCases.NewPurgeVariantCacheUseCase(repos.VariantRepo, repos.ImageRepo, repos.VariantCacheRepo, repos.VariantFileRepo, imageStorage, time.Now, recorder), VariantUsageTracker: usageTracker, } } func startUpdateDownloadUseCase(repos *Repositories, settings config.Settings, recorder domainAudit.Recorder) *useCases.StartUpdateDownloadUseCase { return useCases.NewStartUpdateDownloadUseCase(useCases.StartUpdateDownloadDependencies{ Store: repos.UpdateStore, Jobs: repos.UpdateJobs, Helper: repos.UpdateHelper, Results: repos.UpdateResults, Requests: repos.RestartRequests, Fetcher: repos.AssetFetcher, Verifier: repos.SignatureVerifier, Runner: repos.StagedRunner, Backup: repos.DatabaseBackup, Staging: update.NewOSStagingArea(filepath.Join(settings.DataDir(), updatesDirName)), Logger: log.Default(), CurrentVersion: settings.Version, Platform: domainUpdate.Platform{OS: runtime.GOOS, Arch: runtime.GOARCH}, FeedURL: settings.UpdateFeedURL, Now: time.Now, Recorder: recorder, }) } func backfillImageMetadata(imageRepo domainImage.Repository, imageStorage domainImage.Storage, settings config.Settings) error { run := func(readable domainImage.Storage) error { return useCases.NewBackfillImageMetadataUseCase(imageRepo, readable).Run() } if settings.Storage.Driver == config.StorageDriverS3 { return storage.RunBackfillReportingReads(imageStorage, log.Default(), run) } return run(imageStorage) } func InitContainer(settings config.Settings) (*UseCases, func() error) { imageStorage := openImageStorage(settings) connection := openDatabase(settings) repos := getRepositories(connection, settings) if err := backfillImageMetadata(repos.ImageRepo, imageStorage, settings); err != nil { panic("Failed to backfill image metadata: " + err.Error()) } container := getUseCases(repos, imageStorage, settings) return container, container.runUsageTracker(connection.Close) } func (c *UseCases) runUsageTracker(closeDatabase func() error) func() error { ctx, stop := context.WithCancel(context.Background()) stopped := make(chan struct{}) go func() { defer close(stopped) c.VariantUsageTracker.Run(ctx) }() return func() error { stop() <-stopped return closeDatabase() } } type BrowserUpdateCommands struct { Enable *useCases.EnableBrowserUpdatesUseCase Disable *useCases.DisableBrowserUpdatesUseCase } func InitBrowserUpdateCommands() *BrowserUpdateCommands { units := update.NewSystemdUnits() dependencies := useCases.ManageBrowserUpdatesDependencies{ Probe: update.NewSystemMachineProbe(), Locator: units, Units: units, Manager: update.NewSystemctlControl(domainUpdate.ServiceUnitName), Status: update.NewJSONHelperStatusWriter(), Now: time.Now, } return &BrowserUpdateCommands{ Enable: useCases.NewEnableBrowserUpdatesUseCase(dependencies), Disable: useCases.NewDisableBrowserUpdatesUseCase(dependencies), } } func InitApplyUpdateCommand(options domainUpdate.InstallOptions) *useCases.ApplyRestartRequestUseCase { layout := domainUpdate.HelperLayout{DataDir: options.DataDir, BinaryPath: options.Binary} files := update.NewNoFollowFilesystem() return useCases.NewApplyRestartRequestUseCase(useCases.ApplyRestartRequestDependencies{ Requests: update.NewFSRestartRequestStore(files, layout.RestartRequestPath()), Results: update.NewFSResultStore(files, layout.ResultPath()), Running: update.NewJSONRunningInfoStore(filepath.Join(options.DataDir, domainUpdate.RunningInfoFileName)), Files: files, Service: update.NewSystemctlControl(options.Service), Health: update.NewHTTPHealthChecker(options.Port), Verifier: update.NewEd25519SignatureVerifier(releasePublicKey()), Clock: update.NewSystemClock(), Logger: log.Default(), Layout: layout, Platform: domainUpdate.Platform{OS: runtime.GOOS, Arch: runtime.GOARCH}, }) }