-- Set auth_app password from env. -- Applied by .forgejo/workflows/deploy.yml after auth DB migrations: -- psql -v pw="$AUTH_DB_PASSWORD" < ops/db-bootstrap/set_auth_app_password.sql -- Источник переменной: AUTH_DB_PASSWORD из /opt/gendesign/backend/.env.runtime (chmod 600, -- вне git). Зеркало паттерна ops/db-bootstrap/set_tradein_fdw_password.sql и -- tradein-mvp/ops/db-bootstrap/set_gendesign_reader_password.sql. -- -- Idempotent: ALTER если роль существует, NOTICE и продолжает если нет (миграция -- data/sql/auth/002_auth_app_role.sql могла ещё не примениться на первом деплое). -- Пароль НИКОГДА не хранится в этом файле или в git — только имя переменной. -- -- Format %L экранирует пароль как SQL string literal — безопасно даже с кавычками. -- -- psql variable substitution (:'pw') НЕ интерполируется внутри dollar-quoted блока ($$...$$) -- — это правило psql, не bug. Поэтому password передаём в DO через сессионный GUC -- (set_config), который psql интерполирует ВНЕ dollar quote, и читаем внутри через -- current_setting(). По той же причине файл подаётся через stdin, а НЕ через `psql -c`. -- Reference incident: deploy 2026-05-24 (post-merge PR #503) упал на -- "syntax error at or near ':'" именно на этом. -- -- ⚠️ `set_config(name, value, is_local) -> text` ВОЗВРАЩАЕТ установленное значение. Без -- `\o /dev/null` psql напечатал бы пароль на stdout → leak в Forgejo Actions deploy logs -- (retained, visible всем с repo read access). Поэтому оба set_config обёрнуты в -- `\o /dev/null` / `\o` — глушится только их вывод, NOTICE из DO block (сигнал -- идемпотентности) остаётся видимым. -- -- Rollback path: НЕ revert этого файла (вернёт сломанный :'pw' внутри $$). Корректный -- rollback — unset AUTH_DB_PASSWORD в /opt/gendesign/backend/.env.runtime на VPS, deploy.yml -- тогда пропустит этот шаг полностью (роль останется без пароля = логин по паролю невозможен). \o /dev/null SELECT set_config('app.auth_pw', :'pw', false); \o DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'auth_app') THEN EXECUTE format('ALTER ROLE auth_app WITH PASSWORD %L', current_setting('app.auth_pw')); RAISE NOTICE 'auth_app password set'; ELSE RAISE NOTICE 'auth_app role missing — migration data/sql/auth/002_auth_app_role.sql not applied yet'; END IF; END $$; -- Clear GUC after use (defense-in-depth — не оставляем password в session state даже на -- short connection). Same \o trick — set_config return value is empty string here, но лишний -- row в stdout всё равно не нужен. \o /dev/null SELECT set_config('app.auth_pw', '', false); \o