fastapi-fullstack 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_fullstack-0.1.7.dist-info/METADATA +739 -0
- fastapi_fullstack-0.1.7.dist-info/RECORD +241 -0
- fastapi_fullstack-0.1.7.dist-info/WHEEL +4 -0
- fastapi_fullstack-0.1.7.dist-info/entry_points.txt +2 -0
- fastapi_fullstack-0.1.7.dist-info/licenses/LICENSE +21 -0
- fastapi_gen/__init__.py +3 -0
- fastapi_gen/cli.py +442 -0
- fastapi_gen/config.py +356 -0
- fastapi_gen/generator.py +207 -0
- fastapi_gen/prompts.py +874 -0
- fastapi_gen/template/VARIABLES.md +276 -0
- fastapi_gen/template/cookiecutter.json +93 -0
- fastapi_gen/template/hooks/post_gen_project.py +355 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.env.prod.example +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +109 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/AGENTS.md +55 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +315 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +768 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.env.example +155 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +447 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +226 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/langchain_assistant.py +226 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/prompts.py +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +541 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +98 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +902 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +498 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +267 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +130 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +334 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +838 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +192 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +850 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +180 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py +890 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +261 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +435 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/adding_features.md +132 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/architecture.md +63 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/patterns.md +161 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/testing.md +120 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.dockerignore +40 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +648 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +69 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/layout.tsx +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/login/page.tsx +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/register/page.tsx +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/chat/page.tsx +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/dashboard/page.tsx +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/layout.tsx +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/profile/page.tsx +152 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/auth/callback/page.tsx +113 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/layout.tsx +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/page.tsx +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +323 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +22 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +234 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +72 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +328 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/copy-button.tsx +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/local-conversation-sidebar.tsx +295 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/markdown-content.tsx +167 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +79 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +79 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +65 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +82 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +105 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +13 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/sheet.tsx +109 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +181 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-local-chat.ts +165 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +37 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-sidebar-store.ts +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/local-chat-store.ts +255 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/sidebar-store.ts +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +83 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
{%- if cookiecutter.use_frontend and cookiecutter.use_jwt %}
|
|
2
|
+
import { test, expect } from "@playwright/test";
|
|
3
|
+
|
|
4
|
+
test.describe("Authentication", () => {
|
|
5
|
+
test.describe("Login Page", () => {
|
|
6
|
+
test("should display login form", async ({ page }) => {
|
|
7
|
+
await page.goto("/login");
|
|
8
|
+
|
|
9
|
+
// Check for login form elements
|
|
10
|
+
await expect(page.getByRole("heading", { name: /sign in|log in/i })).toBeVisible();
|
|
11
|
+
await expect(page.getByLabel(/email/i)).toBeVisible();
|
|
12
|
+
await expect(page.getByLabel(/password/i)).toBeVisible();
|
|
13
|
+
await expect(page.getByRole("button", { name: /sign in|log in/i })).toBeVisible();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("should show validation errors for empty form", async ({ page }) => {
|
|
17
|
+
await page.goto("/login");
|
|
18
|
+
|
|
19
|
+
// Submit empty form
|
|
20
|
+
await page.getByRole("button", { name: /sign in|log in/i }).click();
|
|
21
|
+
|
|
22
|
+
// Should show validation errors
|
|
23
|
+
await expect(page.getByText(/required|invalid/i)).toBeVisible();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should show error for invalid credentials", async ({ page }) => {
|
|
27
|
+
await page.goto("/login");
|
|
28
|
+
|
|
29
|
+
// Fill in invalid credentials
|
|
30
|
+
await page.getByLabel(/email/i).fill("invalid@example.com");
|
|
31
|
+
await page.getByLabel(/password/i).fill("wrongpassword");
|
|
32
|
+
await page.getByRole("button", { name: /sign in|log in/i }).click();
|
|
33
|
+
|
|
34
|
+
// Should show error message
|
|
35
|
+
await expect(
|
|
36
|
+
page.getByText(/invalid|incorrect|failed|error/i)
|
|
37
|
+
).toBeVisible({ timeout: 5000 });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("should have link to registration", async ({ page }) => {
|
|
41
|
+
await page.goto("/login");
|
|
42
|
+
|
|
43
|
+
// Should have link to register page
|
|
44
|
+
const registerLink = page.getByRole("link", { name: /sign up|register|create account/i });
|
|
45
|
+
await expect(registerLink).toBeVisible();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test.describe("Registration Page", () => {
|
|
50
|
+
test("should display registration form", async ({ page }) => {
|
|
51
|
+
await page.goto("/register");
|
|
52
|
+
|
|
53
|
+
// Check for registration form elements
|
|
54
|
+
await expect(page.getByRole("heading", { name: /sign up|register|create/i })).toBeVisible();
|
|
55
|
+
await expect(page.getByLabel(/email/i)).toBeVisible();
|
|
56
|
+
await expect(page.getByLabel(/password/i).first()).toBeVisible();
|
|
57
|
+
await expect(page.getByRole("button", { name: /sign up|register|create/i })).toBeVisible();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("should validate password requirements", async ({ page }) => {
|
|
61
|
+
await page.goto("/register");
|
|
62
|
+
|
|
63
|
+
// Fill in weak password
|
|
64
|
+
await page.getByLabel(/email/i).fill("newuser@example.com");
|
|
65
|
+
await page.getByLabel(/password/i).first().fill("weak");
|
|
66
|
+
|
|
67
|
+
// Should show password requirements error
|
|
68
|
+
await page.getByRole("button", { name: /sign up|register|create/i }).click();
|
|
69
|
+
await expect(page.getByText(/password|characters|strong/i)).toBeVisible();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("should have link to login", async ({ page }) => {
|
|
73
|
+
await page.goto("/register");
|
|
74
|
+
|
|
75
|
+
// Should have link to login page
|
|
76
|
+
const loginLink = page.getByRole("link", { name: /sign in|log in|already have/i });
|
|
77
|
+
await expect(loginLink).toBeVisible();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test.describe("Authenticated User", () => {
|
|
82
|
+
// Use authenticated state from setup
|
|
83
|
+
test.use({
|
|
84
|
+
storageState: ".playwright/.auth/user.json",
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("should redirect to dashboard after login", async ({ page }) => {
|
|
88
|
+
await page.goto("/");
|
|
89
|
+
|
|
90
|
+
// Should be redirected to dashboard or see dashboard content
|
|
91
|
+
await expect(page).toHaveURL(/dashboard|home/i);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("should show user menu or profile", async ({ page }) => {
|
|
95
|
+
await page.goto("/dashboard");
|
|
96
|
+
|
|
97
|
+
// Should have user profile/menu element
|
|
98
|
+
await expect(
|
|
99
|
+
page.getByRole("button", { name: /profile|account|user/i }).or(
|
|
100
|
+
page.getByText(/@|user/i)
|
|
101
|
+
)
|
|
102
|
+
).toBeVisible();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("should be able to logout", async ({ page }) => {
|
|
106
|
+
await page.goto("/dashboard");
|
|
107
|
+
|
|
108
|
+
// Find and click logout button
|
|
109
|
+
const logoutButton = page.getByRole("button", { name: /log out|sign out/i }).or(
|
|
110
|
+
page.getByRole("link", { name: /log out|sign out/i })
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (await logoutButton.isVisible()) {
|
|
114
|
+
await logoutButton.click();
|
|
115
|
+
|
|
116
|
+
// Should be redirected to login or home
|
|
117
|
+
await expect(page).toHaveURL(/login|\/$/);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
{%- elif cookiecutter.use_frontend %}
|
|
123
|
+
import { test, expect } from "@playwright/test";
|
|
124
|
+
|
|
125
|
+
test.describe("Public Access", () => {
|
|
126
|
+
test("should allow public access to main page", async ({ page }) => {
|
|
127
|
+
await page.goto("/");
|
|
128
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
{%- else %}
|
|
132
|
+
/* Auth tests - frontend not configured */
|
|
133
|
+
export {};
|
|
134
|
+
{%- endif %}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
{%- if cookiecutter.use_frontend and cookiecutter.enable_ai_agent %}
|
|
2
|
+
import { test, expect } from "@playwright/test";
|
|
3
|
+
|
|
4
|
+
test.describe("AI Chat", () => {
|
|
5
|
+
{%- if cookiecutter.use_jwt %}
|
|
6
|
+
// Use authenticated state
|
|
7
|
+
test.use({
|
|
8
|
+
storageState: ".playwright/.auth/user.json",
|
|
9
|
+
});
|
|
10
|
+
{%- endif %}
|
|
11
|
+
|
|
12
|
+
test.beforeEach(async ({ page }) => {
|
|
13
|
+
await page.goto("/chat");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test.describe("Chat Interface", () => {
|
|
17
|
+
test("should display chat container", async ({ page }) => {
|
|
18
|
+
// Chat container should be visible
|
|
19
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
20
|
+
|
|
21
|
+
// Input should be present
|
|
22
|
+
const input = page.getByRole("textbox", { name: /message|type|ask/i }).or(
|
|
23
|
+
page.getByPlaceholder(/message|type|ask/i)
|
|
24
|
+
);
|
|
25
|
+
await expect(input).toBeVisible();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("should have send button", async ({ page }) => {
|
|
29
|
+
const sendButton = page.getByRole("button", { name: /send|submit/i }).or(
|
|
30
|
+
page.locator('button[type="submit"]')
|
|
31
|
+
);
|
|
32
|
+
await expect(sendButton).toBeVisible();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("should allow typing a message", async ({ page }) => {
|
|
36
|
+
const input = page.getByRole("textbox").first();
|
|
37
|
+
await input.fill("Hello, AI assistant!");
|
|
38
|
+
await expect(input).toHaveValue("Hello, AI assistant!");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test.describe("Chat Functionality", () => {
|
|
43
|
+
test("should send message and receive response", async ({ page }) => {
|
|
44
|
+
const input = page.getByRole("textbox").first();
|
|
45
|
+
const sendButton = page.getByRole("button", { name: /send|submit/i }).or(
|
|
46
|
+
page.locator('button[type="submit"]')
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Send a message
|
|
50
|
+
await input.fill("Hello!");
|
|
51
|
+
await sendButton.click();
|
|
52
|
+
|
|
53
|
+
// User message should appear in chat
|
|
54
|
+
await expect(page.getByText("Hello!")).toBeVisible();
|
|
55
|
+
|
|
56
|
+
// Wait for AI response (with reasonable timeout)
|
|
57
|
+
await expect(
|
|
58
|
+
page.locator("[data-role='assistant']").or(
|
|
59
|
+
page.getByText(/thinking|processing/i)
|
|
60
|
+
)
|
|
61
|
+
).toBeVisible({ timeout: 30000 });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("should show loading state while waiting for response", async ({ page }) => {
|
|
65
|
+
const input = page.getByRole("textbox").first();
|
|
66
|
+
const sendButton = page.getByRole("button", { name: /send|submit/i }).or(
|
|
67
|
+
page.locator('button[type="submit"]')
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Send a message
|
|
71
|
+
await input.fill("What is 2 + 2?");
|
|
72
|
+
await sendButton.click();
|
|
73
|
+
|
|
74
|
+
// Should show some loading indicator
|
|
75
|
+
await expect(
|
|
76
|
+
page.getByText(/thinking|loading|processing/i).or(
|
|
77
|
+
page.locator(".animate-pulse, .animate-spin")
|
|
78
|
+
)
|
|
79
|
+
).toBeVisible();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("should clear input after sending", async ({ page }) => {
|
|
83
|
+
const input = page.getByRole("textbox").first();
|
|
84
|
+
const sendButton = page.getByRole("button", { name: /send|submit/i }).or(
|
|
85
|
+
page.locator('button[type="submit"]')
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
await input.fill("Test message");
|
|
89
|
+
await sendButton.click();
|
|
90
|
+
|
|
91
|
+
// Input should be cleared
|
|
92
|
+
await expect(input).toHaveValue("");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test.describe("Message Display", () => {
|
|
97
|
+
test("should display user messages correctly", async ({ page }) => {
|
|
98
|
+
const input = page.getByRole("textbox").first();
|
|
99
|
+
const sendButton = page.getByRole("button", { name: /send|submit/i }).or(
|
|
100
|
+
page.locator('button[type="submit"]')
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
await input.fill("My test message");
|
|
104
|
+
await sendButton.click();
|
|
105
|
+
|
|
106
|
+
// Message should be styled as user message
|
|
107
|
+
const userMessage = page.locator("[data-role='user']").or(
|
|
108
|
+
page.getByText("My test message")
|
|
109
|
+
);
|
|
110
|
+
await expect(userMessage).toBeVisible();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("should support multiple messages", async ({ page }) => {
|
|
114
|
+
const input = page.getByRole("textbox").first();
|
|
115
|
+
const sendButton = page.getByRole("button", { name: /send|submit/i }).or(
|
|
116
|
+
page.locator('button[type="submit"]')
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Send first message
|
|
120
|
+
await input.fill("First message");
|
|
121
|
+
await sendButton.click();
|
|
122
|
+
await expect(page.getByText("First message")).toBeVisible();
|
|
123
|
+
|
|
124
|
+
// Wait for response
|
|
125
|
+
await page.waitForTimeout(1000);
|
|
126
|
+
|
|
127
|
+
// Send second message
|
|
128
|
+
await input.fill("Second message");
|
|
129
|
+
await sendButton.click();
|
|
130
|
+
await expect(page.getByText("Second message")).toBeVisible();
|
|
131
|
+
|
|
132
|
+
// Both messages should be visible
|
|
133
|
+
await expect(page.getByText("First message")).toBeVisible();
|
|
134
|
+
await expect(page.getByText("Second message")).toBeVisible();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test.describe("Keyboard Navigation", () => {
|
|
139
|
+
test("should send message on Enter key", async ({ page }) => {
|
|
140
|
+
const input = page.getByRole("textbox").first();
|
|
141
|
+
|
|
142
|
+
await input.fill("Keyboard test");
|
|
143
|
+
await input.press("Enter");
|
|
144
|
+
|
|
145
|
+
// Message should be sent
|
|
146
|
+
await expect(page.getByText("Keyboard test")).toBeVisible();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("should support Shift+Enter for new line", async ({ page }) => {
|
|
150
|
+
const input = page.getByRole("textbox").first();
|
|
151
|
+
|
|
152
|
+
await input.fill("Line 1");
|
|
153
|
+
await input.press("Shift+Enter");
|
|
154
|
+
await input.type("Line 2");
|
|
155
|
+
|
|
156
|
+
// Should contain multiline text (behavior may vary)
|
|
157
|
+
const value = await input.inputValue();
|
|
158
|
+
expect(value).toContain("Line 1");
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
{%- if cookiecutter.enable_conversation_persistence %}
|
|
164
|
+
test.describe("Conversation Persistence", () => {
|
|
165
|
+
{%- if cookiecutter.use_jwt %}
|
|
166
|
+
test.use({
|
|
167
|
+
storageState: ".playwright/.auth/user.json",
|
|
168
|
+
});
|
|
169
|
+
{%- endif %}
|
|
170
|
+
|
|
171
|
+
test("should show conversation list", async ({ page }) => {
|
|
172
|
+
await page.goto("/chat");
|
|
173
|
+
|
|
174
|
+
// Should have some way to view conversation history
|
|
175
|
+
const conversationList = page.getByRole("list").or(
|
|
176
|
+
page.locator("[data-testid='conversation-list']")
|
|
177
|
+
);
|
|
178
|
+
// List may or may not be visible depending on UI
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("should create new conversation", async ({ page }) => {
|
|
182
|
+
await page.goto("/chat");
|
|
183
|
+
|
|
184
|
+
// Find new conversation button
|
|
185
|
+
const newChatButton = page.getByRole("button", { name: /new|create/i }).or(
|
|
186
|
+
page.getByText(/new chat/i)
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (await newChatButton.isVisible()) {
|
|
190
|
+
await newChatButton.click();
|
|
191
|
+
// Should start a new conversation
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
{%- endif %}
|
|
196
|
+
{%- elif cookiecutter.use_frontend %}
|
|
197
|
+
import { test, expect } from "@playwright/test";
|
|
198
|
+
|
|
199
|
+
test.describe("Chat", () => {
|
|
200
|
+
test.skip("AI agent not enabled", async () => {
|
|
201
|
+
// Skip chat tests when AI agent is not configured
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
{%- else %}
|
|
205
|
+
/* Chat tests - frontend not configured */
|
|
206
|
+
export {};
|
|
207
|
+
{%- endif %}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{%- if cookiecutter.use_frontend %}
|
|
2
|
+
import { test, expect } from "@playwright/test";
|
|
3
|
+
|
|
4
|
+
test.describe("Home Page", () => {
|
|
5
|
+
test("should load the home page", async ({ page }) => {
|
|
6
|
+
await page.goto("/");
|
|
7
|
+
await expect(page).toHaveTitle(/{{ cookiecutter.project_name }}/i);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("should have navigation elements", async ({ page }) => {
|
|
11
|
+
await page.goto("/");
|
|
12
|
+
|
|
13
|
+
// Check for main navigation elements
|
|
14
|
+
const nav = page.getByRole("navigation");
|
|
15
|
+
await expect(nav).toBeVisible();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("should be accessible", async ({ page }) => {
|
|
19
|
+
await page.goto("/");
|
|
20
|
+
|
|
21
|
+
// Basic accessibility checks
|
|
22
|
+
// Main landmark should exist
|
|
23
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
24
|
+
|
|
25
|
+
// Page should have a heading
|
|
26
|
+
const heading = page.getByRole("heading", { level: 1 });
|
|
27
|
+
await expect(heading).toBeVisible();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test.describe("Navigation", () => {
|
|
32
|
+
{%- if cookiecutter.use_jwt %}
|
|
33
|
+
test("unauthenticated user should see login link", async ({ page }) => {
|
|
34
|
+
// Clear any stored auth state
|
|
35
|
+
await page.context().clearCookies();
|
|
36
|
+
await page.goto("/");
|
|
37
|
+
|
|
38
|
+
// Should have login/sign in link
|
|
39
|
+
const loginLink = page.getByRole("link", { name: /log in|sign in/i });
|
|
40
|
+
await expect(loginLink).toBeVisible();
|
|
41
|
+
});
|
|
42
|
+
{%- endif %}
|
|
43
|
+
|
|
44
|
+
test("should navigate between pages", async ({ page }) => {
|
|
45
|
+
await page.goto("/");
|
|
46
|
+
|
|
47
|
+
// Test navigation to different sections
|
|
48
|
+
const links = await page.getByRole("link").all();
|
|
49
|
+
expect(links.length).toBeGreaterThan(0);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test.describe("Responsive Design", () => {
|
|
54
|
+
test("should work on mobile viewport", async ({ page }) => {
|
|
55
|
+
await page.setViewportSize({ width: 375, height: 667 });
|
|
56
|
+
await page.goto("/");
|
|
57
|
+
|
|
58
|
+
// Page should still be functional
|
|
59
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("should work on tablet viewport", async ({ page }) => {
|
|
63
|
+
await page.setViewportSize({ width: 768, height: 1024 });
|
|
64
|
+
await page.goto("/");
|
|
65
|
+
|
|
66
|
+
// Page should still be functional
|
|
67
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
{%- else %}
|
|
71
|
+
/* Home tests - frontend not configured */
|
|
72
|
+
export {};
|
|
73
|
+
{%- endif %}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_logfire %}
|
|
2
|
+
import { registerOTel } from "@vercel/otel";
|
|
3
|
+
|
|
4
|
+
export function register() {
|
|
5
|
+
registerOTel({
|
|
6
|
+
serviceName: "{{ cookiecutter.project_slug }}-frontend",
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
{%- else %}
|
|
10
|
+
// Logfire/OpenTelemetry instrumentation is disabled
|
|
11
|
+
export function register() {
|
|
12
|
+
// No-op when Logfire is disabled
|
|
13
|
+
}
|
|
14
|
+
{%- endif %}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_i18n %}
|
|
2
|
+
{
|
|
3
|
+
"common": {
|
|
4
|
+
"loading": "Loading...",
|
|
5
|
+
"error": "Error",
|
|
6
|
+
"success": "Success",
|
|
7
|
+
"save": "Save",
|
|
8
|
+
"cancel": "Cancel",
|
|
9
|
+
"delete": "Delete",
|
|
10
|
+
"edit": "Edit",
|
|
11
|
+
"create": "Create",
|
|
12
|
+
"search": "Search",
|
|
13
|
+
"back": "Back",
|
|
14
|
+
"next": "Next",
|
|
15
|
+
"previous": "Previous",
|
|
16
|
+
"submit": "Submit",
|
|
17
|
+
"close": "Close",
|
|
18
|
+
"confirm": "Confirm",
|
|
19
|
+
"yes": "Yes",
|
|
20
|
+
"no": "No"
|
|
21
|
+
},
|
|
22
|
+
"navigation": {
|
|
23
|
+
"home": "Home",
|
|
24
|
+
"dashboard": "Dashboard",
|
|
25
|
+
"profile": "Profile",
|
|
26
|
+
"settings": "Settings",
|
|
27
|
+
"logout": "Logout"
|
|
28
|
+
},
|
|
29
|
+
"auth": {
|
|
30
|
+
"login": "Login",
|
|
31
|
+
"register": "Register",
|
|
32
|
+
"email": "Email",
|
|
33
|
+
"password": "Password",
|
|
34
|
+
"confirmPassword": "Confirm Password",
|
|
35
|
+
"forgotPassword": "Forgot Password?",
|
|
36
|
+
"noAccount": "Don't have an account?",
|
|
37
|
+
"hasAccount": "Already have an account?",
|
|
38
|
+
"loginSuccess": "Successfully logged in",
|
|
39
|
+
"registerSuccess": "Account created successfully",
|
|
40
|
+
"logoutSuccess": "Successfully logged out",
|
|
41
|
+
"invalidCredentials": "Invalid email or password",
|
|
42
|
+
"emailRequired": "Email is required",
|
|
43
|
+
"passwordRequired": "Password is required",
|
|
44
|
+
"passwordMismatch": "Passwords do not match",
|
|
45
|
+
"continueWithGoogle": "Continue with Google"
|
|
46
|
+
},
|
|
47
|
+
"profile": {
|
|
48
|
+
"title": "Profile",
|
|
49
|
+
"personalInfo": "Personal Information",
|
|
50
|
+
"name": "Name",
|
|
51
|
+
"email": "Email",
|
|
52
|
+
"changePassword": "Change Password",
|
|
53
|
+
"currentPassword": "Current Password",
|
|
54
|
+
"newPassword": "New Password",
|
|
55
|
+
"updateProfile": "Update Profile",
|
|
56
|
+
"deleteAccount": "Delete Account",
|
|
57
|
+
"deleteAccountWarning": "This action cannot be undone. All your data will be permanently deleted."
|
|
58
|
+
},
|
|
59
|
+
"chat": {
|
|
60
|
+
"title": "Chat",
|
|
61
|
+
"newChat": "New Chat",
|
|
62
|
+
"sendMessage": "Send a message...",
|
|
63
|
+
"thinking": "Thinking...",
|
|
64
|
+
"noConversations": "No conversations yet",
|
|
65
|
+
"startNewChat": "Start a new chat",
|
|
66
|
+
"deleteConversation": "Delete conversation",
|
|
67
|
+
"archiveConversation": "Archive conversation",
|
|
68
|
+
"renameConversation": "Rename conversation"
|
|
69
|
+
},
|
|
70
|
+
"errors": {
|
|
71
|
+
"generic": "Something went wrong",
|
|
72
|
+
"notFound": "Page not found",
|
|
73
|
+
"unauthorized": "You are not authorized to view this page",
|
|
74
|
+
"forbidden": "Access forbidden",
|
|
75
|
+
"serverError": "Server error. Please try again later.",
|
|
76
|
+
"networkError": "Network error. Please check your connection."
|
|
77
|
+
},
|
|
78
|
+
"footer": {
|
|
79
|
+
"copyright": "All rights reserved"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
{%- else %}
|
|
83
|
+
{}
|
|
84
|
+
{%- endif %}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_i18n %}
|
|
2
|
+
{
|
|
3
|
+
"common": {
|
|
4
|
+
"loading": "Ładowanie...",
|
|
5
|
+
"error": "Błąd",
|
|
6
|
+
"success": "Sukces",
|
|
7
|
+
"save": "Zapisz",
|
|
8
|
+
"cancel": "Anuluj",
|
|
9
|
+
"delete": "Usuń",
|
|
10
|
+
"edit": "Edytuj",
|
|
11
|
+
"create": "Utwórz",
|
|
12
|
+
"search": "Szukaj",
|
|
13
|
+
"back": "Wstecz",
|
|
14
|
+
"next": "Dalej",
|
|
15
|
+
"previous": "Wstecz",
|
|
16
|
+
"submit": "Wyślij",
|
|
17
|
+
"close": "Zamknij",
|
|
18
|
+
"confirm": "Potwierdź",
|
|
19
|
+
"yes": "Tak",
|
|
20
|
+
"no": "Nie"
|
|
21
|
+
},
|
|
22
|
+
"navigation": {
|
|
23
|
+
"home": "Strona główna",
|
|
24
|
+
"dashboard": "Panel",
|
|
25
|
+
"profile": "Profil",
|
|
26
|
+
"settings": "Ustawienia",
|
|
27
|
+
"logout": "Wyloguj"
|
|
28
|
+
},
|
|
29
|
+
"auth": {
|
|
30
|
+
"login": "Zaloguj się",
|
|
31
|
+
"register": "Zarejestruj się",
|
|
32
|
+
"email": "Email",
|
|
33
|
+
"password": "Hasło",
|
|
34
|
+
"confirmPassword": "Potwierdź hasło",
|
|
35
|
+
"forgotPassword": "Zapomniałeś hasła?",
|
|
36
|
+
"noAccount": "Nie masz konta?",
|
|
37
|
+
"hasAccount": "Masz już konto?",
|
|
38
|
+
"loginSuccess": "Zalogowano pomyślnie",
|
|
39
|
+
"registerSuccess": "Konto utworzone pomyślnie",
|
|
40
|
+
"logoutSuccess": "Wylogowano pomyślnie",
|
|
41
|
+
"invalidCredentials": "Nieprawidłowy email lub hasło",
|
|
42
|
+
"emailRequired": "Email jest wymagany",
|
|
43
|
+
"passwordRequired": "Hasło jest wymagane",
|
|
44
|
+
"passwordMismatch": "Hasła nie są takie same",
|
|
45
|
+
"continueWithGoogle": "Kontynuuj z Google"
|
|
46
|
+
},
|
|
47
|
+
"profile": {
|
|
48
|
+
"title": "Profil",
|
|
49
|
+
"personalInfo": "Dane osobowe",
|
|
50
|
+
"name": "Imię",
|
|
51
|
+
"email": "Email",
|
|
52
|
+
"changePassword": "Zmień hasło",
|
|
53
|
+
"currentPassword": "Obecne hasło",
|
|
54
|
+
"newPassword": "Nowe hasło",
|
|
55
|
+
"updateProfile": "Zaktualizuj profil",
|
|
56
|
+
"deleteAccount": "Usuń konto",
|
|
57
|
+
"deleteAccountWarning": "Tej akcji nie można cofnąć. Wszystkie Twoje dane zostaną trwale usunięte."
|
|
58
|
+
},
|
|
59
|
+
"chat": {
|
|
60
|
+
"title": "Czat",
|
|
61
|
+
"newChat": "Nowy czat",
|
|
62
|
+
"sendMessage": "Napisz wiadomość...",
|
|
63
|
+
"thinking": "Myślę...",
|
|
64
|
+
"noConversations": "Brak konwersacji",
|
|
65
|
+
"startNewChat": "Rozpocznij nowy czat",
|
|
66
|
+
"deleteConversation": "Usuń konwersację",
|
|
67
|
+
"archiveConversation": "Archiwizuj konwersację",
|
|
68
|
+
"renameConversation": "Zmień nazwę konwersacji"
|
|
69
|
+
},
|
|
70
|
+
"errors": {
|
|
71
|
+
"generic": "Coś poszło nie tak",
|
|
72
|
+
"notFound": "Strona nie znaleziona",
|
|
73
|
+
"unauthorized": "Nie masz uprawnień do wyświetlenia tej strony",
|
|
74
|
+
"forbidden": "Dostęp zabroniony",
|
|
75
|
+
"serverError": "Błąd serwera. Spróbuj ponownie później.",
|
|
76
|
+
"networkError": "Błąd sieci. Sprawdź połączenie."
|
|
77
|
+
},
|
|
78
|
+
"footer": {
|
|
79
|
+
"copyright": "Wszelkie prawa zastrzeżone"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
{%- else %}
|
|
83
|
+
{}
|
|
84
|
+
{%- endif %}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
{%- if cookiecutter.enable_i18n %}
|
|
3
|
+
import createNextIntlPlugin from 'next-intl/plugin';
|
|
4
|
+
|
|
5
|
+
const withNextIntl = createNextIntlPlugin('./src/i18n.ts');
|
|
6
|
+
{%- endif %}
|
|
7
|
+
|
|
8
|
+
// Content Security Policy directives
|
|
9
|
+
const ContentSecurityPolicy = `
|
|
10
|
+
default-src 'self';
|
|
11
|
+
script-src 'self' 'unsafe-eval' 'unsafe-inline';
|
|
12
|
+
style-src 'self' 'unsafe-inline';
|
|
13
|
+
img-src 'self' blob: data: https:;
|
|
14
|
+
font-src 'self' data:;
|
|
15
|
+
connect-src 'self' ws: wss: http://localhost:* https://localhost:*;
|
|
16
|
+
frame-ancestors 'none';
|
|
17
|
+
base-uri 'self';
|
|
18
|
+
form-action 'self';
|
|
19
|
+
`.replace(/\n/g, " ").trim();
|
|
20
|
+
|
|
21
|
+
const securityHeaders = [
|
|
22
|
+
{
|
|
23
|
+
key: "Content-Security-Policy",
|
|
24
|
+
value: ContentSecurityPolicy,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: "X-Content-Type-Options",
|
|
28
|
+
value: "nosniff",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: "X-Frame-Options",
|
|
32
|
+
value: "DENY",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "X-XSS-Protection",
|
|
36
|
+
value: "1; mode=block",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: "Referrer-Policy",
|
|
40
|
+
value: "strict-origin-when-cross-origin",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: "Permissions-Policy",
|
|
44
|
+
value: "camera=(), microphone=(), geolocation=()",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const nextConfig: NextConfig = {
|
|
49
|
+
output: "standalone",
|
|
50
|
+
|
|
51
|
+
// Security headers
|
|
52
|
+
async headers() {
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
source: "/(.*)",
|
|
56
|
+
headers: securityHeaders,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Environment variables available on the server side only
|
|
62
|
+
serverRuntimeConfig: {
|
|
63
|
+
apiUrl: process.env.BACKEND_URL || "http://localhost:{{ cookiecutter.backend_port }}",
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Environment variables available on both server and client
|
|
67
|
+
publicRuntimeConfig: {
|
|
68
|
+
appName: "{{ cookiecutter.project_name }}",
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
{%- if cookiecutter.enable_i18n %}
|
|
73
|
+
export default withNextIntl(nextConfig);
|
|
74
|
+
{%- else %}
|
|
75
|
+
export default nextConfig;
|
|
76
|
+
{%- endif %}
|