92 lines
2.2 KiB
Go
92 lines
2.2 KiB
Go
|
|
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 }
|