| 1 | module main |
| 2 | |
| 3 | import veb |
| 4 | |
| 5 | // reconnect_db drops the current DB handle and opens a fresh connection. |
| 6 | // Used to recover from stale connections (e.g. an idle Postgres connection |
| 7 | // dropped by NAT or by PG's idle_session_timeout). v's db.pg has no built-in |
| 8 | // reconnect, so without this the process keeps using a dead handle until restart. |
| 9 | pub fn (mut app App) reconnect_db() ! { |
| 10 | app.db.close() or {} |
| 11 | app.db = connect_db(app.config)! |
| 12 | } |
| 13 | |
| 14 | // get_users_count_with_reconnect retries the user count query once after |
| 15 | // reconnecting on failure. This is the recovery path for the specific bug |
| 16 | // where a dead DB handle made get_users_count() silently return 0 and the |
| 17 | // site rendered the "welcome / register" page until systemd restart. |
| 18 | pub fn (mut app App) get_users_count_with_reconnect() !int { |
| 19 | if count := app.get_users_count() { |
| 20 | return count |
| 21 | } else { |
| 22 | app.warn('db query failed, attempting reconnect: ${err}') |
| 23 | app.reconnect_db() or { return error('db unavailable; reconnect failed: ${err}') } |
| 24 | return app.get_users_count()! |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | // db_error renders a 503 response describing a database failure. |
| 29 | // We render an explicit page rather than letting callers fall back to a |
| 30 | // misleading default (e.g. redirecting to /register on a swallowed error). |
| 31 | pub fn (mut ctx Context) db_error(err IError) veb.Result { |
| 32 | ctx.res.set_status(.service_unavailable) |
| 33 | body := '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Gitly — database unavailable</title></head><body style="font-family:sans-serif;max-width:640px;margin:4em auto;padding:0 1em;"><h1>Database unavailable</h1><p>Gitly could not reach its database. This is usually transient — please try again in a moment.</p><pre style="background:#f4f4f4;padding:1em;overflow:auto;white-space:pre-wrap;">${err}</pre></body></html>' |
| 34 | return ctx.html(body) |
| 35 | } |
| 36 | |
| 37 | fn sql_table(name string) string { |
| 38 | return '"' + name.to_lower().replace('"', '""') + '"' |
| 39 | } |
| 40 | |
| 41 | fn sql_literal(value string) string { |
| 42 | return "'" + value.replace("'", "''") + "'" |
| 43 | } |
| 44 | |
| 45 | fn sql_like_pattern(value string) string { |
| 46 | return sql_literal('%' + value + '%') |
| 47 | } |
| 48 | |