fix(tradein): account-бренд приоритетен над ?brand= — cross-brand leak (#765) #780
1 changed files with 11 additions and 9 deletions
|
|
@ -3,10 +3,10 @@
|
||||||
/**
|
/**
|
||||||
* useBrand — white-label resolver для #657 («Практика» и пр.).
|
* useBrand — white-label resolver для #657 («Практика» и пр.).
|
||||||
*
|
*
|
||||||
* Активный бренд резолвится из ДВУХ источников (param wins):
|
* Активный бренд резолвится из ДВУХ источников (account-бренд приоритетен, #765):
|
||||||
* 1. query-параметр `?brand=<slug>` в URL — явный override (демо/превью);
|
* 1. `me.brand` из /me — привязка бренда к аккаунту (#657 brand-by-account) — WINS;
|
||||||
* 2. `me.brand` из /me — привязка бренда к аккаунту (#657 brand-by-account):
|
* 2. query-параметр `?brand=<slug>` — демо/превью, только если у аккаунта нет бренда.
|
||||||
* praktika/kopylov логинятся → брендированный UI автоматически, без ?brand=.
|
* (praktika/kopylov логинятся → свой бренд автоматически из me.brand, без ?brand=.)
|
||||||
* Если ни один не задан — нет бренда (generic UI), хук возвращает {data: null}.
|
* Если ни один не задан — нет бренда (generic UI), хук возвращает {data: null}.
|
||||||
*
|
*
|
||||||
* Mirror паттерна useMe.ts: TanStack Query, staleTime/gcTime Infinity
|
* Mirror паттерна useMe.ts: TanStack Query, staleTime/gcTime Infinity
|
||||||
|
|
@ -38,15 +38,17 @@ export function getBrandSlugFromUrl(): string | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Активный slug бренда: `?brand=` param ПЕРЕВЕШИВАЕТ me.brand (account binding).
|
* Активный slug бренда: account-бренд (me.brand) ПРИОРИТЕТЕН над `?brand=`.
|
||||||
|
* `?brand=` разрешён только когда у аккаунта нет своего бренда (демо/превью) —
|
||||||
|
* иначе любой залогиненный мог бы отрендерить чужой бренд (cross-brand leak, #765).
|
||||||
* Hook (читает useMe) — вызывать только в React-компонентах.
|
* Hook (читает useMe) — вызывать только в React-компонентах.
|
||||||
*/
|
*/
|
||||||
export function useActiveBrandSlug(): string | null {
|
export function useActiveBrandSlug(): string | null {
|
||||||
const { data: me } = useMe();
|
const { data: me } = useMe();
|
||||||
const urlSlug = getBrandSlugFromUrl();
|
const accountSlug = me?.brand ? me.brand.trim().toLowerCase() : null;
|
||||||
if (urlSlug) return urlSlug;
|
// Account-бренд приоритетен: ?brand= НЕ может его перекрыть (cross-brand leak).
|
||||||
const accountSlug = me?.brand;
|
if (accountSlug) return accountSlug;
|
||||||
return accountSlug ? accountSlug.trim().toLowerCase() : null;
|
return getBrandSlugFromUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchBrand(slug: string): Promise<Brand> {
|
async function fetchBrand(slug: string): Promise<Brand> {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue