Files
RenewCMS/api/dependecyInjection.go
T

217 lines
9.9 KiB
Go

package api
import (
domainArticle "RenewCMS/internal/domain/article"
domainImage "RenewCMS/internal/domain/image"
domainInvite "RenewCMS/internal/domain/invite"
domainMail "RenewCMS/internal/domain/mail"
domainUpdate "RenewCMS/internal/domain/update"
domainUser "RenewCMS/internal/domain/user"
"RenewCMS/internal/infrastructure/config"
"RenewCMS/internal/infrastructure/hashing"
"RenewCMS/internal/infrastructure/mailer"
"RenewCMS/internal/infrastructure/persistence/models"
persistence "RenewCMS/internal/infrastructure/persistence/repositories"
"RenewCMS/internal/infrastructure/update"
"RenewCMS/internal/infrastructure/useCases"
"crypto/ed25519"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"time"
"github.com/glebarez/sqlite"
"github.com/go-chi/jwtauth/v5"
"gorm.io/gorm"
)
type UseCases struct {
Token *jwtauth.JWTAuth
CreateArticleUseCase *useCases.CreateArticleUseCase
GetArticleUseCase *useCases.GetArticleUseCase
ListArticlesUseCase *useCases.ListArticlesUseCase
UpdateArticleUseCase *useCases.UpdateArticleUseCase
DeleteArticleUseCase *useCases.DeleteArticleUseCase
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
ListMediaUseCase *useCases.ListMediaUseCase
RenameMediaUseCase *useCases.RenameMediaUseCase
DeleteMediaUseCase *useCases.DeleteMediaUseCase
MergeMediaUseCase *useCases.MergeMediaUseCase
GetMediaUsageUseCase *useCases.GetMediaUsageUseCase
GetSettingsUseCase *useCases.GetSettingsUseCase
GetUpdateStatusUseCase *useCases.GetUpdateStatusUseCase
CheckForUpdateUseCase *useCases.CheckForUpdateUseCase
StartUpdateDownloadUseCase *useCases.StartUpdateDownloadUseCase
}
type Repositories struct {
ArticleRepo domainArticle.Repository
UserRepo domainUser.Repository
ImageRepo domainImage.Repository
InviteRepo domainInvite.Repository
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
}
const (
sqliteBusyTimeoutMs = 5000
updateStatusFileName = "update-status.json"
updateJobFileName = "update-job.json"
updateHelperFileName = "update-helper.json"
updatesDirName = "updates"
backupsDirName = "backups"
)
func SQLiteDSN(dbName string) string {
return fmt.Sprintf("%s?_pragma=busy_timeout(%d)", dbName, sqliteBusyTimeoutMs)
}
func getDb(dbName string, uploadDir string) *gorm.DB {
if err := os.MkdirAll(filepath.Dir(dbName), os.ModePerm); err != nil {
panic("Unable to create necessary subdirectories: " + err.Error())
}
db, err := gorm.Open(sqlite.Open(SQLiteDSN(dbName)), &gorm.Config{})
if err != nil {
panic("Unable to open the database: " + err.Error())
}
if err := db.AutoMigrate(&models.Article{}, &models.Image{}, &models.User{}, &models.Invite{}); err != nil {
panic("Failed to migrate database: " + err.Error())
}
if err := persistence.BackfillUserRoles(db); err != nil {
panic("Failed to backfill user roles: " + err.Error())
}
if err := persistence.BackfillImageMetadata(db, uploadDir); err != nil {
panic("Failed to backfill image metadata: " + err.Error())
}
return db
}
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(db *gorm.DB, settings config.Settings) *Repositories {
dataDir := filepath.Dir(settings.DBFile)
return &Repositories{
ArticleRepo: persistence.NewArticleRepository(db),
UserRepo: persistence.NewUserRepository(db),
ImageRepo: persistence.NewImageRepository(db, settings.UploadDir),
InviteRepo: persistence.NewInviteRepository(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.NewFileHelperDetector(filepath.Join(dataDir, updateHelperFileName)),
AssetFetcher: update.NewHTTPAssetFetcher(settings.UpdateFeedURL),
SignatureVerifier: update.NewEd25519SignatureVerifier(releasePublicKey()),
StagedRunner: update.NewExecStagedBinaryRunner(),
DatabaseBackup: update.NewSQLiteDatabaseBackup(db, filepath.Join(dataDir, backupsDirName), time.Now),
}
}
func getUseCases(repos *Repositories, settings config.Settings) *UseCases {
hasher := hashing.NewBcryptHasher()
return &UseCases{
Token: jwtauth.New("HS256", []byte(settings.JWTSecret), nil),
CreateArticleUseCase: useCases.NewCreateArticleUseCase(repos.ArticleRepo),
GetArticleUseCase: useCases.NewGetArticleUseCase(repos.ArticleRepo),
ListArticlesUseCase: useCases.NewListArticlesUseCase(repos.ArticleRepo),
UpdateArticleUseCase: useCases.NewUpdateArticleUseCase(repos.ArticleRepo),
DeleteArticleUseCase: useCases.NewDeleteArticleUseCase(repos.ArticleRepo),
GetUserUseCase: useCases.NewGetUserUseCase(repos.UserRepo),
CreateUserUseCase: useCases.NewCreateUserUseCase(repos.UserRepo, hasher, repos.MailRepo, settings.PublicURL),
AuthenticateUserUseCase: useCases.NewAuthenticateUserUseCase(repos.UserRepo, hasher),
VerifyUserUseCase: useCases.NewVerifyUserUseCase(repos.UserRepo, hasher),
CancelRegistrationUseCase: useCases.NewCancelRegistrationUseCase(repos.UserRepo),
RequestPasswordResetUseCase: useCases.NewRequestPasswordResetUseCase(repos.UserRepo, hasher, repos.MailRepo, settings.PublicURL),
ResetPasswordUseCase: useCases.NewResetPasswordUseCase(repos.UserRepo, hasher),
UpdateUserProfileUseCase: useCases.NewUpdateUserProfileUseCase(repos.UserRepo),
ListUsersUseCase: useCases.NewListUsersUseCase(repos.UserRepo),
CreateInviteUseCase: useCases.NewCreateInviteUseCase(repos.UserRepo, repos.InviteRepo, hasher, repos.MailRepo, settings.PublicURL),
CancelInviteUseCase: useCases.NewCancelInviteUseCase(repos.InviteRepo),
RenewInviteUseCase: useCases.NewRenewInviteUseCase(repos.UserRepo, repos.InviteRepo, hasher, repos.MailRepo, settings.PublicURL),
ListUsersAndInvitesUseCase: useCases.NewListUsersAndInvitesUseCase(repos.UserRepo, repos.InviteRepo),
GetInviteUseCase: useCases.NewGetInviteUseCase(repos.InviteRepo, hasher),
AcceptInviteUseCase: useCases.NewAcceptInviteUseCase(repos.UserRepo, repos.InviteRepo, hasher),
SendMailUseCase: useCases.NewSendMailUseCase(repos.MailRepo),
CreateImageUseCase: useCases.NewCreateImageUseCase(repos.ImageRepo),
ListMediaUseCase: useCases.NewListMediaUseCase(repos.ImageRepo, repos.ArticleRepo, repos.UserRepo),
RenameMediaUseCase: useCases.NewRenameMediaUseCase(repos.ImageRepo),
DeleteMediaUseCase: useCases.NewDeleteMediaUseCase(repos.ImageRepo),
MergeMediaUseCase: useCases.NewMergeMediaUseCase(repos.ImageRepo, repos.ArticleRepo),
GetMediaUsageUseCase: useCases.NewGetMediaUsageUseCase(repos.ImageRepo, repos.ArticleRepo),
GetSettingsUseCase: useCases.NewGetSettingsUseCase(settings.Version),
GetUpdateStatusUseCase: useCases.NewGetUpdateStatusUseCase(repos.UpdateStore, repos.UpdateJobs, repos.UpdateHelper, settings.Version),
CheckForUpdateUseCase: useCases.NewCheckForUpdateUseCase(repos.ReleaseFeed, repos.UpdateStore, settings.Version, time.Now),
StartUpdateDownloadUseCase: startUpdateDownloadUseCase(repos, settings),
}
}
func startUpdateDownloadUseCase(repos *Repositories, settings config.Settings) *useCases.StartUpdateDownloadUseCase {
return useCases.NewStartUpdateDownloadUseCase(useCases.StartUpdateDownloadDependencies{
Store: repos.UpdateStore,
Jobs: repos.UpdateJobs,
Helper: repos.UpdateHelper,
Fetcher: repos.AssetFetcher,
Verifier: repos.SignatureVerifier,
Runner: repos.StagedRunner,
Backup: repos.DatabaseBackup,
Staging: update.NewOSStagingArea(filepath.Join(filepath.Dir(settings.DBFile), updatesDirName)),
Logger: log.Default(),
CurrentVersion: settings.Version,
Platform: domainUpdate.Platform{OS: runtime.GOOS, Arch: runtime.GOARCH},
FeedURL: settings.UpdateFeedURL,
Now: time.Now,
})
}
func InitContainer(settings config.Settings) (*UseCases, func() error) {
db := getDb(settings.DBFile, settings.UploadDir)
repos := getRepositories(db, settings)
return getUseCases(repos, settings), closeDb(db)
}
func closeDb(db *gorm.DB) func() error {
return func() error {
sqlDb, err := db.DB()
if err != nil {
return err
}
return sqlDb.Close()
}
}