Explain the SSL failure instead of just reporting it

A Postgres URL without an sslmode made lib/pq ask for TLS and die with
"SSL is not enabled on the server", which reads like a server fault when
it is really a connection-string default. The error now says what to
append, and the server retries briefly so a deploy is not marked
unhealthy just because Postgres was still starting.

The sync server URL also defaults to the hosted instance now, so a fresh
install needs no setup.
This commit is contained in:
2026-10-08 10:38:54 +08:00
parent c0dd211ee5
commit 1bd346ce20
5 changed files with 76 additions and 14 deletions
+36 -5
View File
@@ -65,14 +65,11 @@ func main() {
addr = ":8080"
}
db, err := sql.Open("postgres", dsn)
db, err := openDatabase(dsn)
if err != nil {
log.Fatalf("open database: %v", err)
log.Fatalf("connect to database: %v%s", err, hintFor(err))
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatalf("connect to database: %v", err)
}
if _, err := db.Exec(schema); err != nil {
log.Fatalf("ensure schema: %v", err)
}
@@ -111,6 +108,40 @@ func main() {
}
}
// openDatabase connects, retrying briefly so a deploy is not marked unhealthy
// just because Postgres was still starting up.
func openDatabase(dsn string) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, err
}
var lastErr error
for attempt := 1; attempt <= 5; attempt++ {
if lastErr = db.Ping(); lastErr == nil {
return db, nil
}
if attempt < 5 {
log.Printf("database not ready (attempt %d/5): %v", attempt, lastErr)
time.Sleep(2 * time.Second)
}
}
db.Close()
return nil, lastErr
}
// hintFor turns the most common connection failure into something actionable.
// lib/pq defaults to sslmode=require, so any DSN copied from a host that runs
// Postgres without TLS (Coolify's internal Postgres, docker-compose, Homebrew)
// fails with a bare "SSL is not enabled on the server".
func hintFor(err error) string {
if !strings.Contains(err.Error(), "SSL is not enabled") {
return ""
}
return "\n\nhint: lib/pq defaults to sslmode=require, but that Postgres has TLS off." +
"\n Append ?sslmode=disable to PROMPT_SYNC_DATABASE_URL" +
"\n (e.g. postgres://user:pass@host:5432/prompt_sync?sslmode=disable)."
}
// healthcheck returns 0 when the local /healthz answers 200.
func healthcheck() int {
addr := os.Getenv("PROMPT_SYNC_ADDR")