* fix(frontend): apiFetchWithStatus body stream double-read crash
Per user report 2026-05-14: 'Failed to execute text on Response: body stream
already read' при analyze с non-JSON error response.
apiFetchWithStatus делал .json() (consumes stream), потом .text() в catch
(FAILS — stream already consumed). Fetch API: body — ReadableStream,
consumable один раз.
Fix: read text() once → JSON.parse on string. На fail (HTML / non-JSON)
оборачиваем в {detail: rawText}. Никаких double-reads.
Site-finder cad search больше не крашится на error pages.
* feat(infra): auto-apply data/sql/*.sql migrations in deploy pipeline (#150)
Закрывает root cause production 500 на /api/v1/admin/site-finder/weight-profiles
(а также 2 unapplied migrations: #89/#91).
## Why это нужно
После audit обнаружили что:
- backend/alembic/versions/ пустой (Alembic configured но не используется)
- deploy.yml не имеет migration step
- 47+ raw SQL files накопилось без auto-apply
- 3 файла unapplied: 87 (engineering), 89 (drop brin), 90 (weight profiles)
User report: GET weight-profiles → 500 потому что user_weight_profiles
table не существует на prod.
## Changes
### deploy.yml +39 lines
- Path trigger расширен на data/sql/*.sql
- New step 'Apply DB migrations' между compose pull и compose up -d:
- Idempotent CREATE TABLE _schema_migrations
- Loop по data/sql/*.sql sorted → skip applied → psql with ON_ERROR_STOP=on
- Record applied filename в tracking table
- Exit 1 на failure → containers НЕ обновляются (no partial state)
### NN collision fix
- 87_engineering_networks_191fz.sql → 91_engineering_networks_191fz.sql
(collision с 87_on_demand_indexes.sql, мой renumber 85→87 был неудачным)
### CLAUDE.md +32 lines
- Subsection 'Auto-apply' под Migration pattern
- Bootstrap procedure docs
- Idempotency requirement
- Failure recovery
### scripts/bootstrap_schema_migrations.sh +100 lines
- One-time seed _schema_migrations с 48 already-applied files
- 3 файла intentionally NOT seeded: 89/90/91 (auto-apply на first deploy)
- Usage docs + verification query
## Vault
domains/infra/Runbook_Auto_Apply_Migrations.md NEW
infra-MOC.md updated
## Required manual steps ДО merge
1. SSH gendesign (tunnel)
2. POSTGRES creds export
3. bash scripts/bootstrap_schema_migrations.sh — seed 48 already-applied files
4. Verify: SELECT COUNT(*) FROM _schema_migrations → 48
5. После merge → first deploy auto-apply #89/#90/#91 → 500 closes
Closes #150
---------
Co-authored-by: lekss361 <claudestars@proton.me>
100 lines
3.5 KiB
Bash
100 lines
3.5 KiB
Bash
#!/usr/bin/env bash
|
|
# bootstrap_schema_migrations.sh
|
|
# Seed _schema_migrations tracking table with all SQL files already applied
|
|
# to production BEFORE auto-apply was introduced (PR #150).
|
|
#
|
|
# Run ONCE manually on prod via SSH tunnel before the first auto-deploy after
|
|
# merging feat/auto-apply-migrations-150. After that, deploy.yml handles
|
|
# everything automatically.
|
|
#
|
|
# Usage:
|
|
# ssh gendesign # open SSH tunnel (LocalForward 15432 → 5432)
|
|
# export POSTGRES_USER=gendesign
|
|
# export POSTGRES_PASSWORD=<from vault meta/00_credentials.md>
|
|
# export POSTGRES_DB=gendesign
|
|
# bash scripts/bootstrap_schema_migrations.sh
|
|
#
|
|
# The script connects on localhost:15432 (tunnel).
|
|
# Files NOT in this seed list will be applied automatically on next deploy.
|
|
# Currently pending (will be applied by first auto-deploy):
|
|
# 89_drop_dead_brin_rosreestr_deals.sql
|
|
# 90_user_weight_profiles.sql
|
|
# 91_engineering_networks_191fz.sql
|
|
|
|
set -euo pipefail
|
|
|
|
DB_URL="${DB_URL:-postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:15432/${POSTGRES_DB}}"
|
|
|
|
echo "Connecting to: postgresql://${POSTGRES_USER}:***@localhost:15432/${POSTGRES_DB}"
|
|
|
|
psql "$DB_URL" -v ON_ERROR_STOP=on <<'SQL'
|
|
CREATE TABLE IF NOT EXISTS _schema_migrations (
|
|
filename TEXT PRIMARY KEY,
|
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Seed: all NN_*.sql files known to be applied on prod before PR #150.
|
|
-- Excludes 89, 90, 91 — those are pending and will auto-apply on next deploy.
|
|
-- Non-SQL files (*.sh, *.py) are never tracked — only .sql applies.
|
|
INSERT INTO _schema_migrations (filename) VALUES
|
|
('01_schema_rosreestr_deals.sql'),
|
|
('03_materialized_views.sql'),
|
|
('10_schema_domrf.sql'),
|
|
('20_schema_domrf_extras.sql'),
|
|
('31_schema_domrf_full.sql'),
|
|
('31_schema_domrf_raw.sql'),
|
|
('33_schema_domrf_lazy.sql'),
|
|
('35_schema_domrf_stats.sql'),
|
|
('37_cleanup_redundant.sql'),
|
|
('38_relations_fix.sql'),
|
|
('39_territory_aliases.sql'),
|
|
('40_relations.sql'),
|
|
('42_schema_breakdown_widen.sql'),
|
|
('43_anton_import.sql'),
|
|
('45_relations_v2.sql'),
|
|
('50_schema_kn_extensions.sql'),
|
|
('51_schema_kn_extras.sql'),
|
|
('52_schema_prinzip_crm.sql'),
|
|
('53_schema_kn_thumbs.sql'),
|
|
('54_schema_scrape_log.sql'),
|
|
('55_schema_scrape_resume.sql'),
|
|
('56_schema_ekb_districts_geom.sql'),
|
|
('57_kn_objects_district.sql'),
|
|
('58_schema_cad_quarters.sql'),
|
|
('59_backfill_obj_cad_quarter.sql'),
|
|
('60_v_zk_rosreestr_velocity.sql'),
|
|
('63_schema_nspd_runs.sql'),
|
|
('64_v_zk_rosreestr_velocity.sql'),
|
|
('65_partitions_rosreestr_2024_h1.sql'),
|
|
('66_indexes_recommend.sql'),
|
|
('67_refresh_ekb_districts_median.sql'),
|
|
('68_schema_objective.sql'),
|
|
('72_objective_sync_config.sql'),
|
|
('73_complexes_master.sql'),
|
|
('74_dedupe_unified_views.sql'),
|
|
('75_unification_naming.sql'),
|
|
('76_complex_connectivity.sql'),
|
|
('77_nspd_geo_jobs.sql'),
|
|
('78_drop_use_rosreestr2coord.sql'),
|
|
('80_complexes_backfill_nulls.sql'),
|
|
('81_job_settings.sql'),
|
|
('82_osm_poi_ekb.sql'),
|
|
('83_cad_parcels_geom.sql'),
|
|
('84_osm_noise_sources_ekb.sql'),
|
|
('85_pzz_zones_ekb.sql'),
|
|
('86_v_bucket_success_score.sql'),
|
|
('87_on_demand_indexes.sql'),
|
|
('88_nspd_quarter_dumps.sql')
|
|
ON CONFLICT (filename) DO NOTHING;
|
|
|
|
SELECT COUNT(*) AS seeded_count FROM _schema_migrations;
|
|
SQL
|
|
|
|
echo ""
|
|
echo "Bootstrap complete. Pending migrations (will apply on next deploy):"
|
|
echo " 89_drop_dead_brin_rosreestr_deals.sql"
|
|
echo " 90_user_weight_profiles.sql"
|
|
echo " 91_engineering_networks_191fz.sql"
|
|
echo ""
|
|
echo "Verify with:"
|
|
echo " psql \$DB_URL -c \"SELECT filename, applied_at FROM _schema_migrations ORDER BY filename;\""
|