package main import ( "context" "encoding/json" "fmt" "log/slog" "net/http" "time" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) // newPool creates a pgx pool. When readOnly (the default in parallel mode), every // connection is forced into read-only transactions so the Go service can never // mutate the production inventory_db it shares with the Python service. func newPool(ctx context.Context, dsn string, readOnly bool) (*pgxpool.Pool, error) { cfg, err := pgxpool.ParseConfig(dsn) if err != nil { return nil, fmt.Errorf("parse DATABASE_URL: %w", err) } cfg.MaxConns = 10 cfg.MaxConnIdleTime = 5 * time.Minute if readOnly { cfg.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error { _, err := conn.Exec(ctx, "SET default_transaction_read_only = on") return err } } return pgxpool.NewWithConfig(ctx, cfg) } func queryRowsAsMaps(ctx context.Context, pool *pgxpool.Pool, sql string, args ...any) ([]map[string]any, error) { rows, err := pool.Query(ctx, sql, args...) if err != nil { return nil, err } out, err := pgx.CollectRows(rows, pgx.RowToMap) if err != nil { return nil, err } if out == nil { out = []map[string]any{} } return out, nil } // pyISO mirrors Python datetime.isoformat() for a UTC value (matches FastAPI's // jsonable_encoder). Note the inventory-service stores naive datetimes (no tz), // so isoformat has no offset — we format without one. func pyISO(t time.Time) string { t = t.UTC() if t.Nanosecond() == 0 { return t.Format("2006-01-02T15:04:05") } return t.Format("2006-01-02T15:04:05") + fmt.Sprintf(".%06d", t.Nanosecond()/1000) } func formatTimes(rows []map[string]any, keys ...string) { for _, m := range rows { for _, k := range keys { if t, ok := m[k].(time.Time); ok { m[k] = pyISO(t) } } } } func toStr(v any) string { if s, ok := v.(string); ok { return s } return "" } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(v); err != nil { slog.Error("json encode failed", "err", err) } }