Skip to content

feat(web): add /health endpoint and post-deploy smoke check - #118

Open
mhunter02 wants to merge 2 commits into
midt-bg:mainfrom
mhunter02:feat/health-endpoint-smoke-check
Open

feat(web): add /health endpoint and post-deploy smoke check#118
mhunter02 wants to merge 2 commits into
midt-bg:mainfrom
mhunter02:feat/health-endpoint-smoke-check

Conversation

@mhunter02

Copy link
Copy Markdown
Contributor

Summary

Adds a production readiness probe and automated post-deploy verification — standard deployment-patterns practice for a Cloudflare Worker + D1 app.

  • GET /health — JSON { ok, ts, db } with a cheap D1 SELECT 1 ping; returns 503 when DB is unreachable; Cache-Control: no-store
  • Deploy smoke check — curls /health after the web worker deploy (retries, optional Cloudflare Access service-token headers)
  • Tests — Vitest coverage for pingDb and response shaping
  • Docsdocs/deploy.md note on uptime probing and Access bypass for automation

Closes #117

Test plan

  • pnpm --filter @sigma/web test -- app/lib/health.test.ts
  • pnpm typecheck
  • After merge: confirm deploy workflow smoke step passes on staging (requires CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET if Access is enabled)
  • curl https://<staging-worker>/health returns {"ok":true,"db":"ok",...}

Follow-up backlog (from repo audit)

Priority Issue idea Skill
1 Wire web-vitals attribution → GA4 in root.tsx + CSP connect-src web-vitals-monitor
2 eslint-plugin-jsx-a11y + axe Vitest on key routes a11y-architect
3 pnpm build in CI to catch SSR build regressions before deploy deployment-patterns

Made with Cursor

Expose a cheap D1 readiness probe for uptime monitors and verify it
automatically after each web worker deploy. Closes midt-bg#117.

Co-authored-by: Cursor <cursoragent@cursor.com>
@nedda76

nedda76 commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Преглед на кода

Полезен и добре оформен PR — readiness probe + smoke check след deploy са стандартна практика за Worker + D1. Разделянето на pingDb (D1 ping) и buildHealthResponse (оформяне на отговора) е чисто и лесно за тестване, а unit тестовете покриват и двата клона (200/503). 👍

Какво работи добре

  • /health е resource route (само loader, без default export), затова коренният loader в root.tsx (който прави свой D1 read през getCoverageMeta) не се изпълнява за този път. Това е важно: при недостъпна база /health връща чисто 503 с { ok:false, db:"error" }, вместо да отиде в error boundary-то на root-а. (Същият модел като sitemap-ите.) Само да се пази инвариантът — добави ли се някога default export, маршрутът става UI route и коренният loader ще се включи.
  • Smoke check-ът е стабилен--retry/--retry-connrefused поемат забавянето след deploy, а jq -e '.ok == true and .db == "ok"' е истинската проверка. Service-token headers идват от secrets и не изтичат в лога (echo показва само тялото на отговора).
  • no-store, 503 при грешка и минималното тяло ({ ok, ts, db }, без детайли за грешката) са правилни.

Бележки (без блокери)

1. Наблюдаемост. Бих логнала грешката в catch на pingDb — сега се поглъща тихо и реален D1 проблем се вижда само като 503, но не и в логовете. Един console.error(...) ще го изведе в wrangler tail, което помага при инцидент.

2. Цена/повърхност (в контекста на #64). /health прави D1 read на всяка заявка — без кеш и без аутентикация. SELECT 1 е евтин, но е публичен endpoint, който докосва базата при всяко извикване. Струва си да се потвърди, че rate limiter-ът (от #64) го покрива (или че това е приемливо), за да не става лост за изчерпване на read-овете.

3. Сценарият „недостъпна база“. Тест планът покрива щастливия случай; пътят 503/db:"error" е тестван на unit ниво, но бих проверила и на staging, че реално паднала база наистина връща 503 JSON, а не HTML error страница.

4. Козметично. В SELECT 1 AS ok алиасът ok не се ползва (резултатът се игнорира) — SELECT 1 е достатъчно.

Резюме

Чиста, добре тествана функционалност с правилен дизайн (resource route + разделена логика). Бележките са дребни — логване при грешка, потвърждение за rate-limit покритието и проверка на пътя при недостъпна база. Иначе изглежда готов за merge. 🙏

Log D1 ping failures for wrangler tail, add HEALTH_RATE_LIMITER
(60/60s) per midt-bg#64 pattern, and drop unused SELECT alias.

Co-authored-by: Cursor <cursoragent@cursor.com>
@mhunter02

Copy link
Copy Markdown
Contributor Author

Благодаря за прегледа, @nedda76 — адресирах бележките в последния commit:

1. НаблюдаемостpingDb вече логва structured console.error с event: health_db_ping_failed при D1 грешка (видимо в wrangler tail), без да изтичат детайли в HTTP отговора.

2. Rate limit (#64)/health не беше покрит; добавих HEALTH_RATE_LIMITER (namespace_id: 1004, 60/60s на IP) по същия модел като search/agg limiters. Smoke check-ът (≤5 заявки от един IP) остава под лимита.

3. Недостъпна база — resource-route инвариантът е запазен (без default export → root loader не се пълни). Пътят 503/db:"error" е unit-тестван; staging verification при реално паднала база остава в test plan-а след merge.

4. КозметичноSELECT 1 AS okSELECT 1.

pnpm --filter @sigma/web test (87) и pnpm typecheck минават. Готов за re-review. 🙏

@lyubomir-bozhinov lyubomir-bozhinov added enhancement Нова функционалност или предложение web Област: web infra Област: infra labels Jun 24, 2026
@lyubomir-bozhinov

Copy link
Copy Markdown
Collaborator

От моя страна (accuracy/security) — чисто. Три неща над вече казаното, за да не повтарям прегледа на @nedda76:

Покритието на rate-limit, което Неда повдигна (#64), е затворено коректно. Новият HEALTH_RATE_LIMITER е с уникален namespace_id „1004" (1001–1004, без тихо сливане на bucket-и), 60/min/IP, а wrangler-render.mjs вече го изисква при deploy → ако binding-ът липсва, deploy-ът пада рано. Така /health не става лост за изчерпване на read-овете. SELECT 1 е constant-cost (не сканира редове), което е и DoW-safe.

Smoke-стъпката е истинска gate, не само 200. curl --fail --retry 5 --retry-connrefused + jq -e '.ok == true and .db == "ok"' означава, че при база надолу (503) или ok:false целият workflow пада, а не само при мрежова грешка. Retry-ите поемат propagation закъснението, CF Access service token-ът е обработен, а base URL-ът е правилно разделен (prod vs preview). Добре направено.

Едно за координация (не блокер за този PR): apps/web/workers/rate-limit.ts се променя и в #80 (failClosed рефакторът). Семантично са съвместими — default-ите пазят 5-аргументното извикване тук — но е кандидат за текстов конфликт, така че който мерджне втори, ще иска кратък rebase.

Иначе и от мен изглежда готов. 🙏

@cefothe

cefothe commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Ревю на PR #118feat(web): add /health endpoint and post-deploy smoke check

Обхват: +243 / −1 в 11 файла · CI: ⚠️ никога не е изпълнявано (виж Бележка 1)

Заключение: коментар (без блокиране) — обща оценка 9.2/10

Чиста, добре ограничена промяна за production readiness: GET /health endpoint с евтин D1 ping (SELECT 1), отделен rate limiter, който вярно повтаря съществуващия шаблон на search/csv/agg, smoke стъпка след deploy и документация. Кодът е идиоматичен за репото, а тестовете са прецизни. Единственото реално условие е, че CI не се е изпълнявало — гейтът „всички тестове минават“ засега може да се потвърди само локално.

Фаза 0 — Сканиране за критична сигурност: ЧИСТО

  • Тайни: няма твърдо кодирани. CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET се четат от GitHub Environment secrets и се подават само като curl headers — коректна обработка.
  • URL-и: https://sigma.midt.bg (собствен production домейн) и ${SIGMA_WEB_NAME}.cf-midt.workers.dev (собствен staging). И двата са first-party; не са въведени външни endpoint-и.
  • Зависимости: няма нови npm пакети. HEALTH_RATE_LIMITER е Cloudflare binding (инфраструктура), с namespace_id: 1004уникален спрямо съществуващите 1001/1002/1003, и е добавен към REQUIRED_RATE_LIMITERS в wrangler-render.mjs, така че липсващ binding проваля deploy render-а. Добре.
  • Изтичане на информация: тялото при 503 връща само {ok:false, db:"error"}; реалната D1 грешка се логва само от страната на сървъра (структуриран JSON), никога не се връща на клиента. Коректна позиция за публична probe.

Оценки по измерения

Измерение Оценка Бележки
Сигурност 1.0/1.0 Минимално излагане; прилагат се security headers; rate-limited; fail-open
Тестове 2.7/3.0 Прецизни, добре покрити — но непотвърдени в CI (Бележка 1)
Качество на кода 2.0/2.0 Вярно копие на search/csv/agg limiter-а; последователно
Документация 2.0/2.0 docs/deploy.md описва probe, rate limit и Access bypass
Производителност 2.0/2.0 SELECT 1, no-store (без замърсяване на edge cache), с лимит
Архитектура 9.5/10 Чисто разделение: чист health.ts + тънък loader + middleware limiter

Проверка на коректността (проверено, не само прочетено)

  • Limiter-ът отговаря на установения шаблон. Извикването с 5 аргумента rateLimitRequest(req, limiter, isProd, body, name) е идентично по форма на limiter-ите за search/csv/aggregation (параметърът name управлява degrade логването). Не е разминаване — коректно е.
  • Няма кеширане на остарял health. /health задава Cache-Control: no-store; worker-ът кешира само при наличие на s-maxage=\d, затова health винаги е X-Edge-Cache: BYPASS и никога не се put-ва в edge cache.
  • Базата за сигурност все пак се прилага. Loader-ът връща чист Response, но той минава през hardenResponseapplySecurityHeaders(baseSecurityHeaders()), така че health JSON-ът получава пълната база от headers, въпреки че сам не ги задава.
  • Fail-open поведението е тествано. Липсващ binding и хвърлящ limiter и двата резултират в null (без блокиране) — потвърдено от тестовете.

Бележки

  1. ⚠️ CI не се е изпълнявало — да се реши преди merge. gh run list показва и двете CI изпълнения като action_required (продължителност 0s), а mergeStateStatus: UNSTABLE. CI workflow-ът (ci.yml, който се задейства при pull_request) чака одобрение от maintainer (типично за по-нов контрибутор). Тоест pnpm test / typecheck са само заявени локално — не са потвърдени зелени в CI. Maintainer трябва да одобри и пусне workflow-а и да потвърди, че минава, преди merge. Това е единственото нещо между PR-а и чисто одобрение.

  2. Smoke след deploy е ship-and-alert (без rollback). Smoke стъпката се изпълнява след pnpm run deploy; провалена /health probe проваля job-а, но worker-ът вече е на живо — същият съзнателен компромис като D1 gate-а в feat(etl): pipeline reconciliation gate (#97) #119. Приемливо, но си струва съзнателно отбелязване, тъй като няма автоматичен rollback.

  3. Production URL твърдо кодиран в deploy.yml (base="https://sigma.midt.bg"), докато staging се извежда от ${SIGMA_WEB_NAME}. Дребна асиметрия — обмислете workflow променлива за production хоста, за да не е домейнът „магически стринг“ в скрипта на job-а.

  4. Подредба на smoke стъпката спрямо Initialize LOG_IP_KEY secret. Smoke стъпката предхожда стъпката за инициализация на LOG_IP_KEY; тъй като няма continue-on-error, провал на smoke прекъсва job-а и прескача тази инициализация. Почти сигурно е наред (тя е идемпотентна и се изпълнява при следващия deploy), но потвърдете, че подредбата е умишлена.

  5. Дребно: isHealthRequest приема HEAD, а buildHealthResponse винаги включва JSON тяло. Cloudflare премахва телата при HEAD, така че е безвредно — не е нужно действие.

Съответствие с конвенциите: ✅ чисто

Без TODO/заместители, без мъртъв код, без дублиране (преизползва normalizedPathname/rateLimitRequest), тестовете са смислени с проверки на точни стойности (статус кодове, точен JSON, точни аргументи на limit({key}) — отговаря на тестовата конвенция на репото), последователно именуване с peer limiter-ите, коректно разделение на отговорностите.

Препоръка: всичко в diff-а е издържано; единственото отворено е неизпълненият CI pipeline (Бележка 1) — да се одобри и пусне CI и да се потвърди зелено преди merge.

nedda76 added a commit to nedda76/sigma that referenced this pull request Jun 28, 2026
Both this PR and midt-bg#118 (/health) independently claimed namespace_id 1004. A duplicate silently disables one limiter (no deploy error) and git auto-merges the two bindings without conflict, so the dup would ship unnoticed. Take 1005 here (1004 stays with midt-bg#118's reviewed health limiter) to deconflict regardless of merge order.
@nedda76

nedda76 commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

@lyubomir-bozhinov — освен конфликта по rate-limit.ts, който спомена, имаше и тихо разминаване в wrangler.jsonc: #80 и този PR независимо взеха namespace_id "1004". Дубликатът тихо изключва единия limiter (без грешка при deploy), а git слива двата binding-а без конфликт, така че щеше да мине незабелязано.

Преместих #80 на 1005 — спокойна съм, че така няма да се слее тихо; здравният limiter тук остава с прегледания 1004, без промяна по този PR. Остава само краткият текстов rebase по rate-limit.ts, който описа. 🙏

@ydimitrof

Copy link
Copy Markdown
Contributor

Преглед на кода — PR #118

Чиста, добре ограничена промяна за production readiness и точно покритие на #117. Разделянето на pingDb / buildHealthResponse е спретнато, а двата клона (200/503) са тествани. Сигурността е издържана: няма твърдо кодирани тайни, само first-party URL-и, без нови зависимости, SELECT 1 е статична заявка с постоянна цена (без инжекция), тялото при 503 не издава нищо, а реалната грешка се логва от страната на сървъра, и новият HEALTH_RATE_LIMITER (60/60s на IP, уникален namespace_id 1004, изискван при deploy) коректно повтаря модела на search/csv/agg. Изтеглих клона и пуснах тестовете — 87/87 зелени.

Няколко бележки без блокери, като надграждам прегледите по-горе, а не ги повтарям:

  1. Покритие по суфикс на пътя (за потвърждение). Worker-ът съпоставя точния нормализиран път /health. Тъй като приложението е ssr:true + single-fetch, бихте ли потвърдили, че single-fetch заявка /health.data не може да достигне loader-а без лимит? health.tsx е resource route (без default export), затова би трябвало да върне 404 — само си струва едно изречение за потвърждение. Това е модел, споделен с limiter-ите за search/csv/agg, тоест е систематична проверка, не специфична за този PR.
  2. Подредба на стъпките в deploy. Smoke стъпката се изпълнява преди „Initialize LOG_IP_KEY secret if absent" без continue-on-error, така че провален smoke прекъсва job-а и прескача тази инициализация. Тя е идемпотентна и се изпълнява при следващия deploy, но моля потвърдете, че подредбата е умишлена.
  3. Ship-and-alert. Smoke се изпълнява след deploy, затова провал алармира, но worker-ът вече е на живо (без rollback) — същият съзнателен компромис като D1 gate-а в feat(etl): pipeline reconciliation gate (#97) #119; струва си да се отбележи.
  4. Дребно. Production хостът е твърдо кодиран (https://sigma.midt.bg), докато staging се извежда от ${SIGMA_WEB_NAME} — workflow променлива би премахнала „магическия стринг".

Единственото реално условие е, че CI никога не е изпълнявано (в action_required, клонът е UNSTABLE): тестовете минават локално, но maintainer трябва да одобри и пусне CI и да потвърди зелено преди merge.

Заключение: Одобрявам — бележки без блокери; моля потвърдете, че CI е зелено преди merge.

@ydimitrof

Copy link
Copy Markdown
Contributor

Преглед на кода — PR #118 (feat(web): add /health endpoint and post-deploy smoke check)

Благодаря за чистата и добре ограничена промяна. Издърпах клона, прегледах целия diff ред по ред, проверих rate-limit.ts, wrangler-render.mjs и deploy.yml локално, и сверих реализацията спрямо issue #117. По-долу надграждам вече отличните прегледи на @nedda76, @lyubomir-bozhinov, @cefothe и @ydimitrof, без да ги повтарям.

Сигурност / OWASP — чисто ✅

Проверих сам, не само прочетох:

  • Инжекция (A03). db.prepare('SELECT 1').first() е статичен литерал без конкатенация и без потребителски вход — нулева повърхност за SQL инжекция. SELECT 1 е с постоянна цена (не сканира редове), т.е. и DoW-safe.
  • Тайни / криптографски провали (A02/A05). Няма твърдо кодирани тайни. CF_ACCESS_CLIENT_ID/SECRET идват от Environment secrets и се подават само като curl headers; echo "$body" извежда единствено тялото на отговора, не и headers-ите — няма изтичане в лога.
  • Изтичане на информация. Тялото при 503 е само { ok:false, db:"error" }; реалната D1 грешка се логва само сървърно като структуриран JSON. Правилна позиция за публична probe.
  • SSRF / външни URL-и (A10). Само first-party хостове (sigma.midt.bg, ${SIGMA_WEB_NAME}.cf-midt.workers.dev). Няма нови зависимости.
  • Злонамерен код. Няма backdoors, обфускация, eval, нито мрежови call-ове извън собствената инфраструктура. Diff-ът съответства едно към едно на описанието.

Съответствие с issue #117

И трите acceptance criteria са изпълнени: /health връща 200 + { ok:true, db:"ok" } при здрава база; smoke стъпката проваля job-а при провал (--fail + jq -e); unit тестовете покриват и success, и failure пътя за D1 ping. Обхватът е точно колкото issue-то — без scope creep.

Бележки (без блокери)

  1. Fail-open лимитер върху публичен D1-докосващ endpoint. rateLimitRequest при липсващ binding или хвърлящ limiter връща null (пропуска) — съзнателен и последователен избор с останалите лимитери. Струва си само да се държи наум, че при отпаднал rate-limit слой /health остава неограничен D1 read; за probe това е приемлив компромис (availability > abuse), но заслужава да е осъзнато решение, а не случайност.

  2. Покритие по точен път vs .data (за потвърждение, систематично). Лимитерът съпоставя точно нормализирания /health; заявка /health.data не би съвпаднала. Тъй като health.tsx е resource route (без default export), очакването е такъв път да 404-не при рутиране, преди да достигне loader-а — но, както отбеляза и @ydimitrof, едно изречение за потвърждение би затворило въпроса окончателно. Това е модел, споделен със search/csv/agg лимитерите.

  3. Подредба на smoke спрямо Initialize LOG_IP_KEY secret и ship-and-alert без rollback — вече добре описани от @cefothe и @ydimitrof; съгласявам се, че са приемливи, стига да са умишлени.

Тестовете минават локално (pnpm --filter @sigma/web test — 87/87 зелени). Единственото реално условие преди merge е, че CI никога не е изпълнявано (action_required, клонът е UNSTABLE) — maintainer трябва да одобри и пусне pipeline-а и да потвърди зелено.

Заключение: Одобрявам — бележките са без блокери; моля потвърдете, че CI е зелено и че /health.data не заобикаля лимитера, преди merge.

@ydimitrof ydimitrof left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Общ преглед

PR-ът добавя GET /health endpoint (лек D1 SELECT 1 ping) с посветен rate limiter и smoke check след deploy. Кодът е чист, добре структуриран и с добро покритие от тестове. Разделянето на логиката (buildHealthResponse / pingDb) от route loader-а и от worker-level rate limiting е издържано. Не са открити секрети, зловреден код или проблемни зависимости. URL-ите (sigma.midt.bg, *.cf-midt.workers.dev) са собствени домейни на проекта.

Силни страни

  • Тестове: health.test.ts и health-rate-limit.test.ts покриват успешен път, DB грешка (503), нормализиране на пътя с trailing slash, несвързани пътища и fail-open поведение при липсваща/хвърляща binding — смислени тестове, не тривиални.
  • Сигурност/устойчивост: rate limiter (60/60s на IP) предпазва D1 от abuse; Cache-Control: no-store е коректен за readiness probe; fail-open при липсваща binding е разумен избор за health (наличност > строгост) и е тестван.
  • Консистентност: HEALTH_RATE_LIMITER е добавен и в REQUIRED_RATE_LIMITERS (wrangler-render.mjs), и в wrangler.jsonc, и е свързан в app.ts в същия стил като останалите лимитъри. Документацията в docs/deploy.md е обновена.

Забележки (незадължителни)

  1. SIGMA_WEB_NAME в staging клона на smoke check-а не е деклариран в env: на стъпката — трябва да се потвърди, че идва от job/workflow-level env, иначе staging URL-ът се чупи (https://.cf-midt.workers.dev).
  2. Поради curl --fail + set -e, при 503/провал стъпката приключва преди echo "$body", така че тялото на неуспешния отговор не се логва за дебъг.
  3. /health разкрива публично статуса на базата (db: ok|error) — минимално разкриване на информация, но приемливо предвид rate limiting-а.

Няма блокиращи проблеми. Препоръчвам изясняване на т.1 преди merge.

if [ "${{ needs.detect.outputs.env }}" = "production" ]; then
base="https://sigma.midt.bg"
else
base="https://${SIGMA_WEB_NAME}.cf-midt.workers.dev"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SIGMA_WEB_NAME се използва тук, но не е деклариран в env: блока на тази стъпка. Моля потвърдете, че е дефиниран на job- или workflow-ниво; в противен случай за не-production средите base става https://.cf-midt.workers.dev и smoke check-ът винаги ще фейлва. Ако вече е глобален env — игнорирайте.

curl_opts+=(-H "CF-Access-Client-Secret: ${CF_ACCESS_CLIENT_SECRET}")
fi
echo "Probing ${url}"
body="$(curl "${curl_opts[@]}" "$url")"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тъй като curl е с --fail и стъпката ползва set -e, при провал (напр. 503 от DB грешка) командната субституция връща ненулев код и стъпката спира преди echo "$body" на следващия ред — така тялото на неуспешния отговор не се вижда в лога. Помислете за curl -w '%{http_code}' или отделно печатане на тялото при грешка за по-лесен дебъг. Незадължително.

isProd: boolean,
): Promise<Response | null> {
if (!isHealthRequest(request)) return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fail-open поведението (връщане на null при липсваща/хвърляща binding, наследено от rateLimitRequest) е коректен избор за health probe и е покрито от тест. Само отбелязвам, че така при проблем с rate limiter binding-а /health остава напълно неограничен — приемливо за readiness endpoint.

@lyubomir-bozhinov lyubomir-bozhinov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Health endpoint-ът е коректен като payload ({ ok, ts, db }, без вътрешна инфо, Cache-Control: no-store). Но rate limiter-ът се заобикаля през .data twin-а — детайл на реда по-долу. Отделно PR-ът е dirty (конфликт с main) и иска rebase.

function isHealthRequest(request: Request): boolean {
return (
(request.method === 'GET' || request.method === 'HEAD') &&
normalizedPathname(request) === '/health'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isHealthRequest match-ва точно === '/health', а normalizedPathname маха само trailing slash + lowercase (rate-limit.ts:6-13) — не маха .data. Затова GET /health.data (валиден RRv7 single-fetch URL за route с loader) дава pathname /health.data/healthrateLimitHealthRoute връща null → loader-ът вика pingDb (SELECT 1) без лимит. Неограничени D1 ping-ове на публичен, unauthenticated endpoint = Denial-of-Wallet.

Fix:

const p = normalizedPathname(request);
return (request.method === 'GET' || request.method === 'HEAD') &&
  (p === '/health' || p === '/health.data');

Същият .data bypass важи и за /contracts.csv.data през isCsvRequest's endsWith('.csv') (pre-existing, не е от този PR) — струва си да се хване на едно място за всички path-keyed gate-ове.

@lyubomir-bozhinov

Copy link
Copy Markdown
Collaborator

Уточнение към ревюто ми по-горе — препроверих main: shared fix-ът за .data bypass-а вече е merge-нат (#185, closes #184). Сегашният normalizedPathname (apps/web/workers/rate-limit.ts на main) вече маха trailing .data преди strip-а на trailing slash.

Този PR внася isHealthRequest върху споделения normalizedPathname (import от ./rate-limit) и в момента е dirty. Значи самият rebase върху main затваря bypass-а автоматично: normalizedPathname('/health.data')/health, така че === '/health' вече match-ва и health limiter-ът гърми. Per-route кръпката, която предложих, е излишна и би била dead код след rebase — забрави я.

Действието е просто rebase върху main. Струва си да добавиш и regression тест, който удря /health.data и очаква 429 (аналогично на CSV lane-а в #177), за да се заключи класът. Извинявам се за подвеждащата насока в inline бележката.

todorkolev added a commit that referenced this pull request Aug 9, 2026
* ci: забрани трейлър, който кредитира агент, и запази тези с хора

Правилото в AGENTS.md се четеше като пълна забрана на `Co-Authored-By:`, а
буквалното му спазване би изтрило заслугата на сътрудниците: при squash GitHub
съставя трейлърите от авторите на коммитите в PR-а и те са единственото, което
държи външния автор в историята - авторът на самия squash коммит винаги е този,
който е отворил PR-а. 22 от 376 коммита на main ги носят, включително тези,
които пазят заслугата на StanislavBG, Румен и Йоан.

Забраненото е друго: трейлър, който кредитира агент - Claude Code, Codex,
Cursor, Copilot. Те са инструменти, не сътрудници. Проверката ги лови на ниво
PR и казва как се оправя, без да праща никого да пренаписва чужд форк.

Тестовете карат проверката срещу истинска история: човешки съавтор и
dependabot не се маркират, а трейлърът на Cursor в PR #118 се маркира.

Пътьом: post-create.sh предупреждава при самоличност като `t@e.com` или
`...MacBook-Pro.local`. И двете вече са влизали в публичната история точно по
този път.

* style(scripts): форматиране по prettier

* ci: премести проверката в задължителната работа, за да блокира наистина

Отделната работа се вижда в списъка, но правилото на main изисква само `check`.
Значи червен кръст, който нищо не спира - точно класът „проверка, която
изглежда, че пази". Стъпката вече е вътре в `check`, тъй че отказът е реален
и не зависи от админска промяна по правилото.
@nedda76

nedda76 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Този клон е в конфликт с main, тъй че към момента не може да се ревюира — дифът, който GitHub показва, вече не отговаря на това, което би влязло. Ще го пребазираш ли върху актуалния main (или merge на main в клона) и да разрешиш конфликтите? След това веднага го поглеждам. Благодаря! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Нова функционалност или предложение infra Област: infra web Област: web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add /health endpoint and post-deploy smoke check

5 participants