feat: публичные лендинг-страницы для быстрой покупки VPN-подписок

- QuickPurchase: публичная страница покупки с выбором тарифа/периода/оплаты
- PurchaseSuccess: статус покупки с поллингом и копированием ссылки
- AdminLandings: управление лендингами (создание, сортировка, удаление)
- AdminLandingEditor: полнофункциональный редактор лендинга
- API-клиент landings.ts для всех эндпоинтов
- Роутинг /buy/:slug и /buy/success/:token
- Локализация ru/en/zh/fa для всех текстов лендинга
- Санитизация HTML/CSS, rate limit защита, DOMPurify
This commit is contained in:
Fringg
2026-03-06 07:02:21 +03:00
parent e278fec506
commit 8b5d777f0a
16 changed files with 3813 additions and 766 deletions

View File

@@ -37,6 +37,8 @@ const Info = lazy(() => import('./pages/Info'));
const Wheel = lazy(() => import('./pages/Wheel'));
const Connection = lazy(() => import('./pages/Connection'));
const ConnectionQR = lazy(() => import('./pages/ConnectionQR'));
const QuickPurchase = lazy(() => import('./pages/QuickPurchase'));
const PurchaseSuccess = lazy(() => import('./pages/PurchaseSuccess'));
const TopUpMethodSelect = lazy(() => import('./pages/TopUpMethodSelect'));
const TopUpAmount = lazy(() => import('./pages/TopUpAmount'));
const ConnectedAccounts = lazy(() => import('./pages/ConnectedAccounts'));
@@ -104,6 +106,8 @@ const AdminRoleAssign = lazy(() => import('./pages/AdminRoleAssign'));
const AdminPolicies = lazy(() => import('./pages/AdminPolicies'));
const AdminPolicyEdit = lazy(() => import('./pages/AdminPolicyEdit'));
const AdminAuditLog = lazy(() => import('./pages/AdminAuditLog'));
const AdminLandings = lazy(() => import('./pages/AdminLandings'));
const AdminLandingEditor = lazy(() => import('./pages/AdminLandingEditor'));
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
@@ -194,6 +198,22 @@ function App() {
</LazyPage>
}
/>
<Route
path="/buy/success/:token"
element={
<LazyPage>
<PurchaseSuccess />
</LazyPage>
}
/>
<Route
path="/buy/:slug"
element={
<LazyPage>
<QuickPurchase />
</LazyPage>
}
/>
{/* Protected routes */}
<Route
@@ -476,6 +496,36 @@ function App() {
</PermissionRoute>
}
/>
<Route
path="/admin/landings"
element={
<PermissionRoute permission="landings:read">
<LazyPage>
<AdminLandings />
</LazyPage>
</PermissionRoute>
}
/>
<Route
path="/admin/landings/create"
element={
<PermissionRoute permission="landings:create">
<LazyPage>
<AdminLandingEditor />
</LazyPage>
</PermissionRoute>
}
/>
<Route
path="/admin/landings/:id/edit"
element={
<PermissionRoute permission="landings:edit">
<LazyPage>
<AdminLandingEditor />
</LazyPage>
</PermissionRoute>
}
/>
<Route
path="/admin/servers"
element={