a18f96912d
interfaces - refactor: split UserRepository into AuthUserRepository and AdminUserRepository capabilities - refactor: split SessionRepository into AuthSessionRepository and repository-owned CLI/admin methods - refactor: replace generic Repository Update/Delete with operation-specific params and ownership predicates - refactor: replace CredentialRepository generic CRUD with passkey-specific methods - refactor: replace FileRepository generic Create/Update/Delete with UploadedFileParams, DirectoryParams, and owned soft-delete - refactor: remove repository fields from WebApp struct; repositories are now composition-time wiring only - feat: add domain error kinds ErrParentNotFound, ErrParentNotDir, ErrDirectoryNotEmpty, ErrInvalidMove - feat: add CredentialTypeAppPasskey constant - feat: add testutil.SetUserAdmin for test fixture setup that bypasses production service ports - test: add architecture test banning GORM Save in repository package - test: add capability interface contract tests ensuring each service receives the minimal interface - test: add blockingStorage helper for concurrent promotion tests - test: add preserved DSN parameter test for sqliteImmediateDSN - docs: update architecture decisions with repository write rules and capability separation - docs: update roadmap to clarify atomic single-use refresh sessions - docs: add -race test target to development docs
100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/dhao2001/mygo/internal/config"
|
|
"github.com/dhao2001/mygo/internal/repository"
|
|
"github.com/dhao2001/mygo/internal/service"
|
|
"github.com/dhao2001/mygo/internal/storage"
|
|
)
|
|
|
|
// WebApp contains application-wide runtime dependencies and metadata.
|
|
type WebApp struct {
|
|
Config *config.Config
|
|
Version string
|
|
|
|
DB *gorm.DB
|
|
AuthService *service.AuthService
|
|
AdminService *service.AdminService
|
|
FileService *service.FileService
|
|
Storage storage.StorageBackend
|
|
}
|
|
|
|
// Bootstrap creates a fully initialized WebApp from config.
|
|
// It opens the database, runs migrations, and wires all repositories and services.
|
|
func Bootstrap(cfg *config.Config) (*WebApp, error) {
|
|
db, err := repository.Open(cfg.Database)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open database: %w", err)
|
|
}
|
|
|
|
if err := repository.AutoMigrate(db); err != nil {
|
|
return nil, fmt.Errorf("migrate database: %w", err)
|
|
}
|
|
|
|
userRepo := repository.NewUserRepository(db)
|
|
sessionRepo := repository.NewSessionRepository(db)
|
|
fileRepo := repository.NewFileRepository(db)
|
|
credentialRepo := repository.NewCredentialRepository(db)
|
|
|
|
jwtSecret := []byte(cfg.JWT.Secret)
|
|
authService := service.NewAuthService(
|
|
userRepo, sessionRepo, credentialRepo,
|
|
jwtSecret,
|
|
cfg.JWT.AccessTTL,
|
|
cfg.JWT.RefreshTTL,
|
|
)
|
|
|
|
store, err := storage.NewLocalStorage(cfg.Storage.Local.Path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("init storage: %w", err)
|
|
}
|
|
|
|
fileService := service.NewFileService(fileRepo, store, cfg.Storage.MaxUploadSize, slog.Default())
|
|
adminService := service.NewAdminService(userRepo)
|
|
|
|
return &WebApp{
|
|
Config: cfg,
|
|
Version: AppVersion,
|
|
DB: db,
|
|
AuthService: authService,
|
|
AdminService: adminService,
|
|
FileService: fileService,
|
|
Storage: store,
|
|
}, nil
|
|
}
|
|
|
|
// NewWebApp creates a WebApp with pre-built dependencies (useful for testing).
|
|
func NewWebApp(cfg *config.Config, db *gorm.DB,
|
|
authService *service.AuthService,
|
|
adminService *service.AdminService,
|
|
fileService *service.FileService,
|
|
store storage.StorageBackend,
|
|
) *WebApp {
|
|
return &WebApp{
|
|
Config: cfg,
|
|
Version: AppVersion,
|
|
DB: db,
|
|
AuthService: authService,
|
|
AdminService: adminService,
|
|
FileService: fileService,
|
|
Storage: store,
|
|
}
|
|
}
|
|
|
|
// Close releases resources held by the application (e.g., database connections).
|
|
func (w *WebApp) Close() error {
|
|
if w.DB == nil {
|
|
return nil
|
|
}
|
|
sqlDB, err := w.DB.DB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return sqlDB.Close()
|
|
}
|