Files
mygo/internal/app/webapp.go
T
ld 1dfccf513a Add structured logging and centralized error handling
- Initialize slog in the serve command with terminal/file support
- Introduce `AppError` with HTTP status for unified service-layer errors
- Replace ad-hoc `api.Error` calls with `api.RespondError`
- Wrap internal errors with `model.NewInternalError` and add reference
  IDs
- Add `RequestID` middleware and switch router from `gin.Default` to
  `gin.New`
2026-07-04 16:24:22 +08:00

111 lines
2.9 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
UserRepo repository.UserRepository
SessionRepo repository.SessionRepository
FileRepo repository.FileRepository
CredentialRepo repository.CredentialRepository
AuthService *service.AuthService
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())
return &WebApp{
Config: cfg,
Version: AppVersion,
DB: db,
UserRepo: userRepo,
SessionRepo: sessionRepo,
FileRepo: fileRepo,
CredentialRepo: credentialRepo,
AuthService: authService,
FileService: fileService,
Storage: store,
}, nil
}
// NewWebApp creates a WebApp with pre-built dependencies (useful for testing).
func NewWebApp(cfg *config.Config, db *gorm.DB,
userRepo repository.UserRepository,
sessionRepo repository.SessionRepository,
fileRepo repository.FileRepository,
credentialRepo repository.CredentialRepository,
authService *service.AuthService,
fileService *service.FileService,
store storage.StorageBackend,
) *WebApp {
return &WebApp{
Config: cfg,
Version: AppVersion,
DB: db,
UserRepo: userRepo,
SessionRepo: sessionRepo,
FileRepo: fileRepo,
CredentialRepo: credentialRepo,
AuthService: authService,
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()
}