diff --git a/auth/roles.yaml b/auth/roles.yaml index eaf32a08..54b719fb 100644 --- a/auth/roles.yaml +++ b/auth/roles.yaml @@ -54,6 +54,13 @@ roles: - "/admin/**" - "/api/v1/admin/**" - "/trade-in/api/v1/admin/**" + expired: + # Пробный доступ закончился — нет доступа ни к чему. Аккаунт остаётся в + # caddy/users.caddy.snippet (basic_auth), чтобы дойти до фронта и увидеть + # сообщение; /me отдаёт role=expired → RouteGuard рендерит trial-экран. + paths: [] + deny: + - "/**" # NB: реальные analyst-логины ДОЛЖНЫ быть добавлены и в # caddy/users.caddy.snippet (Caddy basic_auth) силами devops — иначе Caddy не @@ -72,7 +79,7 @@ users: user8: pilot user9: pilot user10: pilot - praktika: pilot # пилот-аккаунт агентства «Практика» 2026-05-29 + praktika: expired # пробный доступ закончился 2026-06-27 — см. NoAccessScreen variant="trial" admintest: admin # temp QA 2026-05-26 pilottest: pilot # temp QA 2026-05-26 analysttest: analyst # temp QA 2026-06-07 (#962) diff --git a/tradein-mvp/backend/app/core/auth.py b/tradein-mvp/backend/app/core/auth.py index 06f896b6..a548e9e6 100644 --- a/tradein-mvp/backend/app/core/auth.py +++ b/tradein-mvp/backend/app/core/auth.py @@ -31,7 +31,7 @@ import yaml logger = logging.getLogger(__name__) -Role = Literal["admin", "pilot", "analyst"] +Role = Literal["admin", "pilot", "analyst", "expired"] class UserScope(TypedDict): diff --git a/tradein-mvp/frontend/src/components/auth/NoAccessScreen.tsx b/tradein-mvp/frontend/src/components/auth/NoAccessScreen.tsx index 06ef425d..e6f0338a 100644 --- a/tradein-mvp/frontend/src/components/auth/NoAccessScreen.tsx +++ b/tradein-mvp/frontend/src/components/auth/NoAccessScreen.tsx @@ -10,17 +10,26 @@ */ interface NoAccessScreenProps { - variant: "user" | "path" | "session"; + variant: "user" | "path" | "session" | "trial"; path?: string; } export function NoAccessScreen({ variant, path }: NoAccessScreenProps) { + const title = + variant === "session" + ? "Сессия истекла" + : variant === "trial" + ? "Пробный доступ закончился" + : "Доступа нет"; + const subtitle = variant === "session" ? "Сессия истекла или вы не авторизованы. Обновите страницу, чтобы войти снова." : variant === "user" ? "Учётная запись не привязана к роли. Обратитесь к администратору GenDesign — kopylov." - : "У вашей роли нет доступа к этому разделу. Вернитесь на главную или обратитесь к администратору."; + : variant === "path" + ? "У вашей роли нет доступа к этому разделу. Вернитесь на главную или обратитесь к администратору." + : null; return (
- {variant === "session" ? "Сессия истекла" : "Доступа нет"} + {title} -

- {subtitle} -

+ {variant === "trial" ? ( +

+ Пробный доступ к сервису «Мера» закончился. Чтобы продлить доступ + или получить полную версию — напишите Артёму Копылову:{" "} + + @ArtemKopylov87 + + . +

+ ) : ( +

+ {subtitle} +

+ )} {variant === "path" && path ? (

; + } + if (!isPathAllowed(data.allowed_paths, data.deny_paths, absolutePath)) { return ; } diff --git a/tradein-mvp/frontend/src/lib/useMe.ts b/tradein-mvp/frontend/src/lib/useMe.ts index 2c753c0e..6e33bef5 100644 --- a/tradein-mvp/frontend/src/lib/useMe.ts +++ b/tradein-mvp/frontend/src/lib/useMe.ts @@ -14,7 +14,7 @@ import { useQuery } from "@tanstack/react-query"; import { apiFetchWithStatus, HTTPError } from "@/lib/api"; -export type Role = "admin" | "pilot"; +export type Role = "admin" | "pilot" | "expired"; export interface UserScope { username: string;