Compare commits
No commits in common. "d38e83df8296130f53342abe67cfd824c0fea098" and "51a2d7db84d247d8cdcf4370f9c3ea40eab2c0df" have entirely different histories.
d38e83df82
...
51a2d7db84
2 changed files with 5 additions and 54 deletions
|
|
@ -65,24 +65,12 @@ const PUBLIC_PATHS = ["/mera-public"];
|
||||||
* проверить доступ». Замер на проде: три вызова `/me` плюс подтянутый следом
|
* проверить доступ». Замер на проде: три вызова `/me` плюс подтянутый следом
|
||||||
* клиент чата поддержки.
|
* клиент чата поддержки.
|
||||||
*
|
*
|
||||||
* Те же короткие адреса раздаёт Caddy на публичном домене, и только на нём: на
|
* Те же четыре коротких адреса раздаёт Caddy на публичном домене, и только на
|
||||||
* `gendsgn.ru` их ловит catch-all 404, а `/` там отдаёт server-side redirect на
|
* нём: на `gendsgn.ru` их ловит catch-all 404, а `/` там отдаёт server-side
|
||||||
* `/v2` из `next.config` — то есть приложение по пути `/` вообще не рендерится
|
* redirect на `/v2` из `next.config` — то есть приложение по пути `/` вообще не
|
||||||
* и bypass ему не достаётся.
|
* рендерится и bypass ему не достаётся.
|
||||||
*
|
|
||||||
* ⚠️ ЭТОТ СПИСОК ОБЯЗАН СОВПАДАТЬ С МАТЧЕРОМ `@meraPages` В КОРНЕВОМ Caddyfile.
|
|
||||||
* Повтор инцидента 16.08.2026: короткий `/estimate` завели в Caddy и забыли
|
|
||||||
* здесь. Страница отдавала 200 и выглядела правильно, но после гидратации
|
|
||||||
* монтировался `GuardedRoute` — три запроса к `/me` (404, API на этом хосте не
|
|
||||||
* проксируется), виджет чата поддержки из закрытого контура и экран «Не удалось
|
|
||||||
* проверить доступ» внизу. Форма при этом переставала работать вовсе: разметка
|
|
||||||
* с сервера видна, а обработчики не подключались — человек печатал адрес и не
|
|
||||||
* получал ни подсказок, ни ошибки.
|
|
||||||
*
|
|
||||||
* Совпадение теперь запинено тестом (`__tests__/RouteGuard.publicPaths.test.ts`
|
|
||||||
* читает Caddyfile), потому что комментарий этот инцидент уже не предотвратил.
|
|
||||||
*/
|
*/
|
||||||
const PUBLIC_SHORT_PATHS = ["/", "/estimate", "/oferta", "/refund", "/privacy", "/v3"];
|
const PUBLIC_SHORT_PATHS = ["/", "/oferta", "/refund", "/privacy"];
|
||||||
|
|
||||||
export function isPublicPath(rawPath: string): boolean {
|
export function isPublicPath(rawPath: string): boolean {
|
||||||
// Точное совпадение — ОТДЕЛЬНОЙ веткой и первым делом. Префиксную проверку
|
// Точное совпадение — ОТДЕЛЬНОЙ веткой и первым делом. Префиксную проверку
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { isPublicPath } from "../RouteGuard";
|
import { isPublicPath } from "../RouteGuard";
|
||||||
|
|
@ -28,45 +25,11 @@ describe("isPublicPath", () => {
|
||||||
|
|
||||||
it("пропускает короткие адреса публичного домена", () => {
|
it("пропускает короткие адреса публичного домена", () => {
|
||||||
expect(isPublicPath("/")).toBe(true);
|
expect(isPublicPath("/")).toBe(true);
|
||||||
expect(isPublicPath("/estimate")).toBe(true);
|
|
||||||
expect(isPublicPath("/oferta")).toBe(true);
|
expect(isPublicPath("/oferta")).toBe(true);
|
||||||
expect(isPublicPath("/refund")).toBe(true);
|
expect(isPublicPath("/refund")).toBe(true);
|
||||||
expect(isPublicPath("/privacy")).toBe(true);
|
expect(isPublicPath("/privacy")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* ГЛАВНЫЙ ГЕЙТ, добавленный после повтора инцидента 16.08.2026.
|
|
||||||
*
|
|
||||||
* Список публичных путей в гварде и матчер `@meraPages` в Caddyfile — это
|
|
||||||
* одно и то же множество, записанное в двух местах. Первый раз разошлись на
|
|
||||||
* `/`: домен появился, гвард о нём не знал. Второй раз — на `/estimate`:
|
|
||||||
* короткий адрес завели в Caddy, в гвард добавить забыли. Оба раза страница
|
|
||||||
* отдавала 200 и выглядела правильно, а ломалась только после гидратации —
|
|
||||||
* то есть ни один тест и ни один смоук по коду ответа этого не видел.
|
|
||||||
*
|
|
||||||
* Проверка читает конфиг прокси, а не дублирует список: единственный способ
|
|
||||||
* не разойтись — сверяться с источником.
|
|
||||||
*/
|
|
||||||
it("совпадает с тем, что публичный домен реально раздаёт (Caddyfile)", () => {
|
|
||||||
// __tests__ → auth → components → src → frontend → tradein-mvp → корень.
|
|
||||||
const repoRoot = path.resolve(__dirname, "../../../../../..");
|
|
||||||
const caddyfile = readFileSync(path.join(repoRoot, "Caddyfile"), "utf8");
|
|
||||||
const served = caddyfile
|
|
||||||
.match(/@meraPages path ([^\n]+)/)?.[1]
|
|
||||||
.split(/\s+/)
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
expect(served, "не нашли матчер @meraPages — проверка стала бы пустой").toBeTruthy();
|
|
||||||
|
|
||||||
for (const route of served ?? []) {
|
|
||||||
expect(
|
|
||||||
isPublicPath(route),
|
|
||||||
`${route} раздаётся на meraocenka.ru, но гвард считает его закрытым — ` +
|
|
||||||
"после гидратации на нём смонтируется GuardedRoute и страница перестанет работать",
|
|
||||||
).toBe(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("НЕ делает публичным закрытый контур — главный риск правки", () => {
|
it("НЕ делает публичным закрытый контур — главный риск правки", () => {
|
||||||
for (const closed of [
|
for (const closed of [
|
||||||
"/v2",
|
"/v2",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue