Безопасность довёл до ума — Cursor-генерацию переписал руками. IDOR закрыл, CSRF задушил, refresh rotation теперь как надо. HSTS на staging, ENABLE_DOCS=false, install.env recovery протестил. Backend: - jwt_denylist + auth_epoch: мгновенный revoke access JWT (logout/block/reset) - auth/admin/users: bump epoch, logout с Bearer, forgot_password skip для blocked - install_secrets: путь всегда apps/api/data/secrets/ (bootstrap из корня не ломает Docker) - seed: SEED_DEMO_USERS=false на prod/staging - тесты: jwt revoke, integration, coverage gate 90% Frontend: - logout шлёт Bearer, обработка TOKEN_REVOKED - guards TypeScript fix - E2E: blocked user → 401 сразу после block Infra: - staging/prod compose, TLS nginx, deploy-скрипты - k6 §17.2, backup/health/smoke scripts Docs: - docs/ на русском: project, security, deploy, release (старые md слили) - README короткий + план ТЗ + стандартные логины dev Код готов к плаванию. Капитан может идти писать фронт.
70 lines
1.8 KiB
TypeScript
70 lines
1.8 KiB
TypeScript
/// <reference types="vitest/config" />
|
|
import { defineConfig } from "vitest/config";
|
|
import react from "@vitejs/plugin-react";
|
|
import { resolve } from "node:path";
|
|
import { mainStaticPlugin } from "./vite.main-static";
|
|
|
|
const apiProxyTarget = process.env.VITE_API_URL ?? "http://127.0.0.1:8000";
|
|
|
|
export default defineConfig({
|
|
plugins: [react(), mainStaticPlugin(resolve(__dirname, "main"))],
|
|
build: {
|
|
rollupOptions: {
|
|
input: {
|
|
index: resolve(__dirname, "index.html"),
|
|
app: resolve(__dirname, "app.html")
|
|
}
|
|
}
|
|
},
|
|
server: {
|
|
port: 5173,
|
|
strictPort: true,
|
|
proxy: {
|
|
"/api": {
|
|
target: apiProxyTarget,
|
|
changeOrigin: true,
|
|
secure: false
|
|
}
|
|
}
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
"@app": resolve(__dirname, "src/app"),
|
|
"@pages": resolve(__dirname, "src/pages"),
|
|
"@modules": resolve(__dirname, "src/modules"),
|
|
"@shared": resolve(__dirname, "src/shared")
|
|
}
|
|
},
|
|
test: {
|
|
include: ["src/**/*.{test,spec}.{ts,tsx}"],
|
|
exclude: ["e2e/**", "node_modules/**"],
|
|
environment: "jsdom",
|
|
setupFiles: ["src/__tests__/setup.ts"],
|
|
testTimeout: 10_000,
|
|
coverage: {
|
|
provider: "v8",
|
|
include: ["src/**/*.{ts,tsx}"],
|
|
exclude: [
|
|
"src/**/*.d.ts",
|
|
"src/modules/**/index.ts",
|
|
"src/main.tsx",
|
|
"src/app/App.tsx",
|
|
"src/app/providers/**",
|
|
"src/app/router/guards/**",
|
|
"src/app/router/routes.tsx",
|
|
"src/pages/AdminPage.tsx",
|
|
"src/pages/ProfilePage.tsx",
|
|
"src/pages/VerifyPage.tsx",
|
|
"src/pages/ResetPasswordPage.tsx",
|
|
"src/pages/ForgotPasswordPage.tsx",
|
|
"src/shared/ui/AppHeader/**",
|
|
"src/modules/analytics/**"
|
|
],
|
|
reporter: ["text", "html"],
|
|
thresholds: {
|
|
lines: 85
|
|
}
|
|
}
|
|
}
|
|
});
|