// Package app is the self-hosted, Postgres-backed side of Spread: subscribers, // users, sessions, the admin CMS, and the customer dashboard. It is imported // only by the standalone binary (main.go), never by the Vercel function, so // the public demo stays dependency-light. package app import ( "context" _ "embed" "fmt" "github.com/jackc/pgx/v5/pgxpool" ) //go:embed schema.sql var schemaSQL string // App holds the database pool and shared configuration for all handlers. type App struct { pool *pgxpool.Pool secureCookie bool baseURL string mailer Mailer } // Config configures the application. Only DSN is required; the rest have sane // defaults so the CLI helpers can open with a bare Config. type Config struct { DSN string SecureCookie bool BaseURL string Mailer Mailer } // Open connects to Postgres, verifies the connection, and applies the schema. func Open(ctx context.Context, cfg Config) (*App, error) { pool, err := pgxpool.New(ctx, cfg.DSN) if err != nil { return nil, fmt.Errorf("connect: %w", err) } if err := pool.Ping(ctx); err != nil { pool.Close() return nil, fmt.Errorf("ping: %w", err) } if _, err := pool.Exec(ctx, schemaSQL); err != nil { pool.Close() return nil, fmt.Errorf("apply schema: %w", err) } if cfg.Mailer == nil { cfg.Mailer = LogMailer{} } if cfg.BaseURL == "" { cfg.BaseURL = "http://localhost:8137" } return &App{pool: pool, secureCookie: cfg.SecureCookie, baseURL: cfg.BaseURL, mailer: cfg.Mailer}, nil } func (a *App) Close() { if a.pool != nil { a.pool.Close() } }