package firebase import ( "context" "fmt" fb "firebase.google.com/go/v4" "firebase.google.com/go/v4/auth" "code.nochebuena.dev/go/health" "code.nochebuena.dev/go/launcher" "code.nochebuena.dev/go/logz" ) // Provider is the minimal interface for consumers that only need the firebase App. type Provider interface { App() *fb.App } // Component adds lifecycle management and health check to Provider. type Component interface { launcher.Component health.Checkable Provider } // Config holds Firebase configuration. type Config struct { // ProjectID is the Google Cloud Project ID. ProjectID string `env:"FIREBASE_PROJECT_ID,required"` } type firebaseComponent struct { cfg Config logger logz.Logger app *fb.App } // New returns a firebase Component. Call lc.Append(fb) to manage its lifecycle. func New(logger logz.Logger, cfg Config) Component { return &firebaseComponent{cfg: cfg, logger: logger} } func (f *firebaseComponent) OnInit() error { if f.cfg.ProjectID == "" { return fmt.Errorf("firebase: ProjectID is required") } f.logger.Info("firebase: initializing app", "project_id", f.cfg.ProjectID) app, err := fb.NewApp(context.Background(), &fb.Config{ ProjectID: f.cfg.ProjectID, }) if err != nil { f.logger.Error("firebase: failed to create app", err) return fmt.Errorf("firebase: create app: %w", err) } f.app = app return nil } func (f *firebaseComponent) OnStart() error { f.logger.Info("firebase: app ready") return nil } func (f *firebaseComponent) OnStop() error { f.logger.Info("firebase: shutting down") return nil } func (f *firebaseComponent) App() *fb.App { return f.app } func (f *firebaseComponent) HealthCheck(ctx context.Context) error { if f.app == nil { return fmt.Errorf("firebase: app not initialized") } authClient, err := f.app.Auth(ctx) if err != nil { return fmt.Errorf("firebase: get auth client: %w", err) } _, err = authClient.GetUser(ctx, "health-probe-non-existent") if err != nil { if auth.IsUserNotFound(err) { return nil // expected: probe of non-existent UID succeeded } return err } return nil } func (f *firebaseComponent) Name() string { return "firebase" } func (f *firebaseComponent) Priority() health.Level { return health.LevelCritical }