fastapi-fullstack 0.2.2__tar.gz → 0.2.4__tar.gz
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.2.2 → fastapi_fullstack-0.2.4}/PKG-INFO +2 -2
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/pyproject.toml +9 -18
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/hooks/post_gen_project.py +2 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/commands/add-endpoint.md +40 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/commands/fix-issue.md +16 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/commands/review.md +31 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/api-conventions.md +90 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/architecture.md +109 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/code-style.md +67 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/exceptions-security.md +54 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/frontend.md +27 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/schemas-models.md +90 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/testing.md +85 -0
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/settings.json +17 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +2 -2
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/.gitignore +2 -4
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/.gitlab-ci.yml +2 -2
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/CLAUDE.md +187 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/Makefile +2 -2
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +3 -7
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +1 -1
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +151 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +69 -3
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +86 -62
- fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/backend/tests/test_ssrf.py +207 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/commands.md +1 -1
- fastapi_fullstack-0.2.2/template/{{cookiecutter.project_slug}}/CLAUDE.md +0 -134
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/.gitignore +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/LICENSE +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/README.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/fastapi_gen/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/fastapi_gen/cli.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/fastapi_gen/config.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/fastapi_gen/generator.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/fastapi_gen/prompts.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/VARIABLES.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/cookiecutter.json +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/.env.prod.example +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/AGENTS.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/README.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/.dockerignore +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/.env.example +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/Dockerfile +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/alembic.ini +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/admin.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/crewai_assistant.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/deepagents_assistant.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/langchain_assistant.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/langgraph_assistant.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/prompts.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/rag_tool.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/web_search.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/files.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/rag.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/rag.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/logging.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/chat_file.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/rag_document.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/sync_log.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/sync_source.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/main.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/config.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/connectors/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/connectors/google_drive.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/connectors/s3.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/documents.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/embeddings.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/image_describer.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/ingestion.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/models.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/reranker.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/retrieval.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/sources/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/sources/base.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/sources/google_drive.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/sources/s3.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/rag/vectorstore.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/chat_file.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/rag_document.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/sync_log.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/sync_source.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/file.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/rag.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/sync_source.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/file_storage.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/file_upload.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/rag_document.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/rag_sync.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/sync_source.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/arq_app.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/rag_tasks.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_metrics.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_openapi.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_migrations.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_services_conversation.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docker-compose.yml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/adding_features.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/architecture.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/configuration.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/file-processing.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/add-agent-tool.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/add-api-endpoint.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/add-background-task.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/add-rag-source.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/add-sync-connector.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/configure-sync-sources.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/howto/customize-agent-prompt.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/patterns.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/permissions.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/docs/testing.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/.dockerignore +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/.env.example +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/.gitignore +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/README.md +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/eslint.config.mjs +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/package.json +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/layout.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/login/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/register/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/chat/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/dashboard/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/layout.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/profile/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/rag/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/settings/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/auth/callback/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/error.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/layout.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/page.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/export/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/files/[id]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/files/upload/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/users/avatar/[userId]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/users/me/avatar/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/agent/models/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/collections/[name]/documents/[documentId]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/collections/[name]/documents/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/collections/[name]/info/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/collections/[name]/ingest/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/collections/[name]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/collections/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/documents/[docId]/download/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/documents/[docId]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/documents/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/search/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/supported-formats/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/sync/[syncId]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/sync/connectors/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/sync/local/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/sync/sources/[sourceId]/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/sync/sources/[sourceId]/trigger/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/v1/rag/sync/sources/route.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/global-error.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/not-found.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/copy-button.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/markdown-content.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-approval-dialog.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/auth-guard.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/breadcrumb.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/landing-nav.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/page-transition.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/accordion.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/alert-dialog.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/avatar.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/checkbox.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/dialog.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/dropdown-menu.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/popover.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/progress.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/radio-group.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/scroll-area.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/select.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/separator.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/sheet.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/skeleton.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/spinner.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/switch.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/table.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/tabs.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/textarea.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/tooltip.tsx +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/file-api.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/rag-api.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-sidebar-store.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/sidebar-store.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/speech.d.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/vercel.json +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/configmap.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/deployment.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/ingress.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/kustomization.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/namespace.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/secret.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/kubernetes/service.yaml +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/nginx/nginx.conf +0 -0
- {fastapi_fullstack-0.2.2 → fastapi_fullstack-0.2.4}/template/{{cookiecutter.project_slug}}/nginx/ssl/.gitkeep +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-fullstack
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Full-stack FastAPI + Next.js template generator with PydanticAI/LangChain agents, WebSocket streaming, 20+ enterprise integrations, and Logfire/LangSmith observability. Ship AI apps fast. CLI tool to generate production-ready FastAPI + Next.js projects with AI agents, auth, and observability.
|
|
5
5
|
Project-URL: Homepage, https://github.com/vstorm-co/full-stack-ai-agent-template
|
|
6
6
|
Project-URL: Documentation, https://github.com/vstorm-co/full-stack-ai-agent-template#readme
|
|
@@ -27,11 +27,11 @@ Requires-Dist: pydantic>=2.0.0
|
|
|
27
27
|
Requires-Dist: questionary>=2.0.0
|
|
28
28
|
Requires-Dist: rich>=13.0.0
|
|
29
29
|
Provides-Extra: dev
|
|
30
|
-
Requires-Dist: mypy>=1.13.0; extra == 'dev'
|
|
31
30
|
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
32
31
|
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
33
32
|
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
34
33
|
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ty>=0.0.29; extra == 'dev'
|
|
35
35
|
Provides-Extra: docs
|
|
36
36
|
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
|
|
37
37
|
Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fastapi-fullstack"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = "Full-stack FastAPI + Next.js template generator with PydanticAI/LangChain agents, WebSocket streaming, 20+ enterprise integrations, and Logfire/LangSmith observability. Ship AI apps fast. CLI tool to generate production-ready FastAPI + Next.js projects with AI agents, auth, and observability."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -58,7 +58,7 @@ dev = [
|
|
|
58
58
|
"pytest>=8.0.0",
|
|
59
59
|
"pytest-cov>=4.0.0",
|
|
60
60
|
"ruff>=0.8.0",
|
|
61
|
-
"
|
|
61
|
+
"ty>=0.0.29",
|
|
62
62
|
"pre-commit>=3.0.0",
|
|
63
63
|
]
|
|
64
64
|
docs = [
|
|
@@ -134,25 +134,16 @@ indent-style = "space"
|
|
|
134
134
|
skip-magic-trailing-comma = false
|
|
135
135
|
line-ending = "auto"
|
|
136
136
|
|
|
137
|
-
[tool.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
disallow_untyped_defs = true
|
|
143
|
-
disallow_incomplete_defs = true
|
|
144
|
-
check_untyped_defs = true
|
|
145
|
-
ignore_missing_imports = true
|
|
146
|
-
# Exclude Jinja2 template files
|
|
137
|
+
[tool.ty.environment]
|
|
138
|
+
python-version = "3.11"
|
|
139
|
+
|
|
140
|
+
[tool.ty.src]
|
|
141
|
+
# Exclude Jinja2 template files and integration tests with dynamic imports
|
|
147
142
|
exclude = [
|
|
148
143
|
"template/",
|
|
144
|
+
"tests/",
|
|
149
145
|
]
|
|
150
146
|
|
|
151
|
-
[[tool.mypy.overrides]]
|
|
152
|
-
module = "fastapi_gen.config"
|
|
153
|
-
# Pydantic's @computed_field on top of @property is valid but mypy doesn't like it
|
|
154
|
-
disable_error_code = ["prop-decorator"]
|
|
155
|
-
|
|
156
147
|
[tool.pytest.ini_options]
|
|
157
148
|
testpaths = ["tests"]
|
|
158
149
|
python_files = ["test_*.py"]
|
|
@@ -177,6 +168,6 @@ exclude_lines = [
|
|
|
177
168
|
|
|
178
169
|
[dependency-groups]
|
|
179
170
|
dev = [
|
|
180
|
-
"
|
|
171
|
+
"ty>=0.0.29",
|
|
181
172
|
"pytest-cov>=7.0.0",
|
|
182
173
|
]
|
|
@@ -263,6 +263,8 @@ if not use_frontend:
|
|
|
263
263
|
if os.path.exists(frontend_dir):
|
|
264
264
|
shutil.rmtree(frontend_dir)
|
|
265
265
|
print("Removed frontend/ directory (frontend not enabled)")
|
|
266
|
+
# Remove frontend-specific Claude rule
|
|
267
|
+
remove_file(os.path.join(os.getcwd(), ".claude", "rules", "frontend.md"))
|
|
266
268
|
|
|
267
269
|
|
|
268
270
|
# Remove .env files if generate_env is false
|
fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/commands/add-endpoint.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Scaffold a new API endpoint with full layering
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Create a new API endpoint: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
Follow the project's layered architecture. Create files in this order:
|
|
8
|
+
|
|
9
|
+
1. **Schema** (`backend/app/schemas/<entity>.py`):
|
|
10
|
+
- Inherit `BaseSchema` (and `TimestampSchema` for Read)
|
|
11
|
+
- Create `*Create`, `*Update`, `*Read`, `*List` models
|
|
12
|
+
- Use `Field()` with constraints, `EmailStr` where applicable
|
|
13
|
+
|
|
14
|
+
2. **DB Model** (`backend/app/db/models/<entity>.py`):
|
|
15
|
+
- Inherit `Base, TimestampMixin`
|
|
16
|
+
- Use `Mapped[type]` + `mapped_column()`
|
|
17
|
+
- Add `__repr__`, relationships with `cascade="all, delete-orphan"`
|
|
18
|
+
|
|
19
|
+
3. **Repository** (`backend/app/repositories/<entity>_repo.py`):
|
|
20
|
+
- Stateless async functions: `get_by_id`, `get_multi`, `create`, `update`, `delete`
|
|
21
|
+
- Use `db.flush()` + `db.refresh()`, keyword-only args after `db`
|
|
22
|
+
|
|
23
|
+
4. **Service** (`backend/app/services/<entity>.py`):
|
|
24
|
+
- Class with `__init__(self, db: AsyncSession)`
|
|
25
|
+
- Raise `NotFoundError`, `AlreadyExistsError` as appropriate
|
|
26
|
+
|
|
27
|
+
5. **DI** (`backend/app/api/deps.py`):
|
|
28
|
+
- Add factory function and `Annotated` alias: `EntitySvc = Annotated[EntityService, Depends(get_entity_service)]`
|
|
29
|
+
|
|
30
|
+
6. **Route** (`backend/app/api/routes/v1/<entity>.py`):
|
|
31
|
+
- CRUD: GET list, GET by id, POST (201), PATCH, DELETE (204)
|
|
32
|
+
- Use DI aliases, `response_model`, `-> Any` return type
|
|
33
|
+
|
|
34
|
+
7. **Register** router in `backend/app/api/routes/v1/__init__.py`
|
|
35
|
+
|
|
36
|
+
8. **Migration**: `cd backend && uv run alembic revision --autogenerate -m "Add <entity> table"`
|
|
37
|
+
|
|
38
|
+
9. **Test** (`backend/tests/`): mirror source structure
|
|
39
|
+
|
|
40
|
+
10. Lint: `cd backend && uv run ruff check . --fix && uv run ruff format .`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Investigate and fix an issue
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Fix the issue: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
1. **Understand** — search the codebase for relevant code, read the files, understand current behavior
|
|
8
|
+
2. **Reproduce** — if possible, identify a test case or request that triggers the issue
|
|
9
|
+
3. **Root cause** — trace through Routes → Services → Repositories to find where the bug originates
|
|
10
|
+
4. **Fix** — implement the fix following project conventions:
|
|
11
|
+
- Domain exceptions in services (not HTTP errors)
|
|
12
|
+
- `db.flush()` in repositories (not `commit`)
|
|
13
|
+
- Type hints on all changed signatures
|
|
14
|
+
5. **Test** — run `cd backend && uv run pytest` to verify no regressions
|
|
15
|
+
6. **Lint** — run `cd backend && uv run ruff check . --fix && uv run ruff format .`
|
|
16
|
+
7. **Summary** — explain what was changed and why
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Review code changes against project conventions
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Review all staged and unstaged changes in the current branch.
|
|
6
|
+
|
|
7
|
+
For each changed file, verify:
|
|
8
|
+
|
|
9
|
+
**Architecture:**
|
|
10
|
+
- Routes only call services, never repositories
|
|
11
|
+
- Services raise domain exceptions (NotFoundError, AlreadyExistsError, etc.), not HTTP exceptions
|
|
12
|
+
- Repositories use `db.flush()` + `db.refresh()`, never `db.commit()`
|
|
13
|
+
- DI uses Annotated aliases from `deps.py` (CurrentUser, *Svc), not raw `Depends()` in signatures
|
|
14
|
+
|
|
15
|
+
**Schemas & Types:**
|
|
16
|
+
- Separate Create/Update/Read/List Pydantic models
|
|
17
|
+
- Type hints on all function signatures (params + return)
|
|
18
|
+
- Modern syntax: `str | None` not `Optional[str]`
|
|
19
|
+
- Route return type is `-> Any`
|
|
20
|
+
|
|
21
|
+
**Code Quality:**
|
|
22
|
+
- No debug code (print, commented-out code, TODO without issue reference)
|
|
23
|
+
- No security issues (SQL injection, exposed secrets, missing auth)
|
|
24
|
+
- Consistent naming (snake_case functions, PascalCase classes)
|
|
25
|
+
- Imports ordered: stdlib → third-party → local
|
|
26
|
+
|
|
27
|
+
**Validation:**
|
|
28
|
+
1. Run `cd backend && uv run ruff check .`
|
|
29
|
+
2. Run `cd backend && uv run pytest` (if test files changed)
|
|
30
|
+
|
|
31
|
+
Provide findings with specific file:line references and suggest fixes.
|
fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/api-conventions.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: API design, REST conventions, auth, pagination, response format
|
|
3
|
+
globs: ["backend/app/api/**/*.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Conventions
|
|
7
|
+
|
|
8
|
+
## Route Structure
|
|
9
|
+
|
|
10
|
+
- All routes under `/api/v1/` prefix
|
|
11
|
+
- One file per domain entity in `api/routes/v1/`
|
|
12
|
+
- Use `APIRouter()` with tags
|
|
13
|
+
|
|
14
|
+
## HTTP Methods & Status Codes
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# GET — read
|
|
18
|
+
@router.get("/{id}", response_model=EntityRead)
|
|
19
|
+
|
|
20
|
+
# GET list — paginated
|
|
21
|
+
@router.get("", response_model=EntityList)
|
|
22
|
+
|
|
23
|
+
# POST — create (201)
|
|
24
|
+
@router.post("", response_model=EntityRead, status_code=status.HTTP_201_CREATED)
|
|
25
|
+
|
|
26
|
+
# PATCH — partial update
|
|
27
|
+
@router.patch("/{id}", response_model=EntityRead)
|
|
28
|
+
|
|
29
|
+
# DELETE — no content (204)
|
|
30
|
+
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pagination
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
@router.get("", response_model=ConversationList)
|
|
37
|
+
async def list_items(
|
|
38
|
+
service: ConversationSvc,
|
|
39
|
+
user: CurrentUser,
|
|
40
|
+
skip: int = Query(0, ge=0, description="Items to skip"),
|
|
41
|
+
limit: int = Query(50, ge=1, le=100, description="Max items to return"),
|
|
42
|
+
) -> Any:
|
|
43
|
+
items, total = await service.list(user_id=user.id, skip=skip, limit=limit)
|
|
44
|
+
return ConversationList(items=items, total=total)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Authentication
|
|
48
|
+
|
|
49
|
+
- `CurrentUser` — JWT Bearer token (any authenticated user)
|
|
50
|
+
- `CurrentAdmin` — JWT + admin role check via `RoleChecker`
|
|
51
|
+
- `ValidAPIKey` — API key from header (service-to-service)
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# Protected endpoint
|
|
55
|
+
async def get_profile(user: CurrentUser) -> Any: ...
|
|
56
|
+
|
|
57
|
+
# Admin-only endpoint
|
|
58
|
+
async def delete_user(user: CurrentAdmin) -> Any: ...
|
|
59
|
+
|
|
60
|
+
# API key endpoint
|
|
61
|
+
async def webhook_callback(api_key: ValidAPIKey) -> Any: ...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Response Format
|
|
65
|
+
|
|
66
|
+
All route handlers return `-> Any`. The `response_model` parameter handles serialization.
|
|
67
|
+
|
|
68
|
+
Error responses follow this JSON structure:
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"error": {
|
|
72
|
+
"code": "NOT_FOUND",
|
|
73
|
+
"message": "User not found",
|
|
74
|
+
"details": {"user_id": "..."}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## File Upload
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
@router.post("/me/avatar", response_model=UserRead)
|
|
83
|
+
async def upload_avatar(
|
|
84
|
+
file: UploadFile = File(...),
|
|
85
|
+
user: CurrentUser,
|
|
86
|
+
service: UserSvc,
|
|
87
|
+
) -> Any:
|
|
88
|
+
data = await file.read()
|
|
89
|
+
return await service.update_avatar(user.id, data, file.filename or "avatar.jpg")
|
|
90
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Layered architecture patterns — Routes, Services, Repositories, DI
|
|
3
|
+
globs: ["backend/app/**/*.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Architecture
|
|
7
|
+
|
|
8
|
+
## Layered Architecture: Routes → Services → Repositories
|
|
9
|
+
|
|
10
|
+
Routes NEVER import or call repositories directly. Always go through a service.
|
|
11
|
+
|
|
12
|
+
## Repositories (`app/repositories/`)
|
|
13
|
+
|
|
14
|
+
Pure data access — stateless functions (not classes):
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
async def get_by_id(db: AsyncSession, entity_id: UUID) -> Entity | None:
|
|
18
|
+
result = await db.execute(select(Entity).where(Entity.id == entity_id))
|
|
19
|
+
return result.scalar_one_or_none()
|
|
20
|
+
|
|
21
|
+
async def create(db: AsyncSession, *, field1: str, field2: str) -> Entity:
|
|
22
|
+
entity = Entity(field1=field1, field2=field2)
|
|
23
|
+
db.add(entity)
|
|
24
|
+
await db.flush()
|
|
25
|
+
await db.refresh(entity)
|
|
26
|
+
return entity
|
|
27
|
+
|
|
28
|
+
async def update(db: AsyncSession, *, db_entity: Entity, update_data: dict[str, Any]) -> Entity:
|
|
29
|
+
for field, value in update_data.items():
|
|
30
|
+
setattr(db_entity, field, value)
|
|
31
|
+
await db.flush()
|
|
32
|
+
await db.refresh(db_entity)
|
|
33
|
+
return db_entity
|
|
34
|
+
|
|
35
|
+
async def delete(db: AsyncSession, entity_id: UUID) -> Entity | None:
|
|
36
|
+
entity = await get_by_id(db, entity_id)
|
|
37
|
+
if entity:
|
|
38
|
+
await db.delete(entity)
|
|
39
|
+
await db.flush()
|
|
40
|
+
return entity
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Rules:
|
|
44
|
+
- ALWAYS `db.flush()` + `db.refresh()`, NEVER `db.commit()` — session auto-commits in `get_db_session`
|
|
45
|
+
- Use keyword-only args after `db`: `create(db, *, email: str, name: str)`
|
|
46
|
+
- Return the entity (or None for get/delete), never return IDs or dicts
|
|
47
|
+
- Functions are async for PostgreSQL/MongoDB, sync for SQLite
|
|
48
|
+
|
|
49
|
+
## Services (`app/services/`)
|
|
50
|
+
|
|
51
|
+
Business logic — class-based with DB session:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
class UserService:
|
|
55
|
+
def __init__(self, db: AsyncSession):
|
|
56
|
+
self.db = db
|
|
57
|
+
|
|
58
|
+
async def get_by_id(self, user_id: UUID) -> User:
|
|
59
|
+
user = await user_repo.get_by_id(self.db, user_id)
|
|
60
|
+
if not user:
|
|
61
|
+
raise NotFoundError(message="User not found", details={"user_id": user_id})
|
|
62
|
+
return user
|
|
63
|
+
|
|
64
|
+
async def create(self, data: UserCreate) -> User:
|
|
65
|
+
existing = await user_repo.get_by_email(self.db, data.email)
|
|
66
|
+
if existing:
|
|
67
|
+
raise AlreadyExistsError(message="Email already registered", details={"email": data.email})
|
|
68
|
+
hashed_password = get_password_hash(data.password)
|
|
69
|
+
return await user_repo.create(self.db, email=data.email, hashed_password=hashed_password)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Rules:
|
|
73
|
+
- Raise domain exceptions, NEVER return error codes or None for "not found"
|
|
74
|
+
- Services call repo functions, never build raw queries
|
|
75
|
+
- One service per domain entity
|
|
76
|
+
|
|
77
|
+
## Dependency Injection (`app/api/deps.py`)
|
|
78
|
+
|
|
79
|
+
Use `Annotated` type aliases — never raw `Depends()` in route signatures:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
DBSession = Annotated[AsyncSession, Depends(get_db_session)]
|
|
83
|
+
UserSvc = Annotated[UserService, Depends(get_user_service)]
|
|
84
|
+
CurrentUser = Annotated[User, Depends(get_current_user)]
|
|
85
|
+
CurrentAdmin = Annotated[User, Depends(RoleChecker(UserRole.ADMIN))]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Service factories take `DBSession` and return service instances:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
def get_user_service(db: DBSession) -> UserService:
|
|
92
|
+
return UserService(db)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Routes (`app/api/routes/v1/`)
|
|
96
|
+
|
|
97
|
+
HTTP layer only — validate, delegate, return:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
@router.get("/{user_id}", response_model=UserRead)
|
|
101
|
+
async def get_user(user_id: UUID, service: UserSvc, user: CurrentUser) -> Any:
|
|
102
|
+
return await service.get_by_id(user_id)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Rules:
|
|
106
|
+
- Return type is always `-> Any` (response_model handles serialization)
|
|
107
|
+
- Use `status_code=status.HTTP_201_CREATED` for POST, `HTTP_204_NO_CONTENT` for DELETE
|
|
108
|
+
- DELETE endpoints: `response_model=None`
|
|
109
|
+
- Pagination: `skip: int = Query(0, ge=0)`, `limit: int = Query(50, ge=1, le=100)`
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Code style, formatting, naming, imports, and type hints
|
|
3
|
+
globs: ["backend/**/*.py", "*.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Style
|
|
7
|
+
|
|
8
|
+
## Formatting
|
|
9
|
+
|
|
10
|
+
- Use `ruff` for linting and formatting: `ruff check . --fix && ruff format .`
|
|
11
|
+
- Line length: 120 characters
|
|
12
|
+
|
|
13
|
+
## Type Hints
|
|
14
|
+
|
|
15
|
+
- Type hints on ALL function signatures — parameters and return types
|
|
16
|
+
- Use modern syntax: `str | None` not `Optional[str]`, `list[User]` not `List[User]`
|
|
17
|
+
- Use `Annotated[Type, Depends(...)]` for DI (defined as aliases in `deps.py`)
|
|
18
|
+
- Use `dict[str, Any]` for generic dicts
|
|
19
|
+
- Use `Literal["value1", "value2"]` for string enums in schemas
|
|
20
|
+
- Use `TYPE_CHECKING` block for circular import resolution:
|
|
21
|
+
```python
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from app.db.models.session import Session
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Naming
|
|
28
|
+
|
|
29
|
+
| Element | Convention | Example |
|
|
30
|
+
|---------|-----------|---------|
|
|
31
|
+
| Files | snake_case | `user_repo.py`, `conversation_service.py` |
|
|
32
|
+
| Classes | PascalCase | `UserService`, `ConversationRead` |
|
|
33
|
+
| Functions/variables | snake_case | `get_by_id`, `user_service` |
|
|
34
|
+
| Constants | UPPER_CASE | `DEFAULT_SYSTEM_PROMPT` |
|
|
35
|
+
| Private | _leading_underscore | `_create_agent` |
|
|
36
|
+
| DB tables | snake_case plural | `users`, `conversations` |
|
|
37
|
+
| API URLs | kebab-case | `/api/v1/conversations` |
|
|
38
|
+
|
|
39
|
+
## Imports — strictly ordered, separated by blank lines
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# 1. Standard library
|
|
43
|
+
import logging
|
|
44
|
+
from collections.abc import AsyncGenerator
|
|
45
|
+
from datetime import UTC, datetime
|
|
46
|
+
from typing import Annotated, Any
|
|
47
|
+
from uuid import UUID
|
|
48
|
+
|
|
49
|
+
# 2. Third-party
|
|
50
|
+
from fastapi import APIRouter, Depends, Query, status
|
|
51
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
52
|
+
from sqlalchemy import select
|
|
53
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
54
|
+
|
|
55
|
+
# 3. Local application
|
|
56
|
+
from app.api.deps import CurrentUser, UserSvc
|
|
57
|
+
from app.core.exceptions import NotFoundError
|
|
58
|
+
from app.schemas.user import UserCreate, UserRead
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Other Conventions
|
|
62
|
+
|
|
63
|
+
- `datetime.now(UTC)` not `datetime.utcnow()`
|
|
64
|
+
- `secrets.compare_digest()` for constant-time comparisons
|
|
65
|
+
- `__repr__` on all DB models
|
|
66
|
+
- Async for PostgreSQL/MongoDB I/O, sync for SQLite
|
|
67
|
+
- Keyword-only args in repo functions after `db` parameter
|
fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/exceptions-security.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exception handling patterns and security conventions
|
|
3
|
+
globs: ["backend/app/core/**/*.py", "backend/app/services/**/*.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exceptions & Security
|
|
7
|
+
|
|
8
|
+
## Domain Exceptions (`app/core/exceptions.py`)
|
|
9
|
+
|
|
10
|
+
All extend `AppException`. Always pass `message` and `details`:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
raise NotFoundError(message="User not found", details={"user_id": str(user_id)})
|
|
14
|
+
raise AlreadyExistsError(message="Email already registered", details={"email": email})
|
|
15
|
+
raise AuthenticationError(message="Invalid or expired token")
|
|
16
|
+
raise AuthorizationError(message="Role 'admin' required for this action")
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Exception handlers in `api/exception_handlers.py` automatically:
|
|
20
|
+
- Map to HTTP status codes
|
|
21
|
+
- Log with structured context (path, method, error code)
|
|
22
|
+
- Return consistent JSON error format
|
|
23
|
+
- Add `WWW-Authenticate: Bearer` header on 401
|
|
24
|
+
|
|
25
|
+
## Security Patterns
|
|
26
|
+
|
|
27
|
+
JWT auth (`core/security.py`):
|
|
28
|
+
- `create_access_token(subject)` / `create_refresh_token(subject)` — encode with `jwt.encode()`
|
|
29
|
+
- `verify_token(token)` → `dict | None` — decode with `jwt.decode()`
|
|
30
|
+
- Token payload: `{"exp": ..., "sub": user_id, "type": "access"|"refresh"}`
|
|
31
|
+
|
|
32
|
+
Password hashing:
|
|
33
|
+
- `get_password_hash(password)` — bcrypt
|
|
34
|
+
- `verify_password(plain, hashed)` — bcrypt `checkpw`
|
|
35
|
+
- NEVER store plain passwords
|
|
36
|
+
|
|
37
|
+
API keys:
|
|
38
|
+
- `secrets.compare_digest()` for constant-time comparison
|
|
39
|
+
- `APIKeyHeader(name=settings.API_KEY_HEADER, auto_error=False)`
|
|
40
|
+
|
|
41
|
+
## Role-Based Access Control
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
class RoleChecker:
|
|
45
|
+
def __init__(self, required_role: UserRole) -> None:
|
|
46
|
+
self.required_role = required_role
|
|
47
|
+
|
|
48
|
+
async def __call__(self, user: Annotated[User, Depends(get_current_user)]) -> User:
|
|
49
|
+
if not user.has_role(self.required_role):
|
|
50
|
+
raise AuthorizationError(message=f"Role '{self.required_role.value}' required")
|
|
51
|
+
return user
|
|
52
|
+
|
|
53
|
+
CurrentAdmin = Annotated[User, Depends(RoleChecker(UserRole.ADMIN))]
|
|
54
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Frontend conventions for Next.js
|
|
3
|
+
globs: ["frontend/**/*.ts", "frontend/**/*.tsx", "frontend/**/*.css"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Frontend Conventions
|
|
7
|
+
|
|
8
|
+
## Stack
|
|
9
|
+
|
|
10
|
+
- Next.js 15 with App Router
|
|
11
|
+
- TypeScript strict mode
|
|
12
|
+
- Tailwind CSS for styling
|
|
13
|
+
- i18n support built-in
|
|
14
|
+
|
|
15
|
+
## Structure
|
|
16
|
+
|
|
17
|
+
- Pages in `frontend/src/app/` following Next.js App Router conventions
|
|
18
|
+
- Reusable components in `frontend/src/components/`
|
|
19
|
+
- API client functions in `frontend/src/lib/`
|
|
20
|
+
- Types in `frontend/src/types/`
|
|
21
|
+
|
|
22
|
+
## Conventions
|
|
23
|
+
|
|
24
|
+
- Use `"use client"` directive only when component needs client-side interactivity
|
|
25
|
+
- Prefer Server Components by default
|
|
26
|
+
- Use `fetch` with proper error handling for API calls
|
|
27
|
+
- Keep components small and focused — extract when a component exceeds ~100 lines
|
fastapi_fullstack-0.2.4/template/{{cookiecutter.project_slug}}/.claude/rules/schemas-models.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pydantic schema patterns and SQLAlchemy model conventions
|
|
3
|
+
globs: ["backend/app/schemas/**/*.py", "backend/app/db/models/**/*.py", "backend/app/db/base.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Schemas & Models
|
|
7
|
+
|
|
8
|
+
## Pydantic Schemas (`app/schemas/`)
|
|
9
|
+
|
|
10
|
+
Base schema with shared config:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
class BaseSchema(BaseModel):
|
|
14
|
+
model_config = ConfigDict(
|
|
15
|
+
from_attributes=True,
|
|
16
|
+
populate_by_name=True,
|
|
17
|
+
str_strip_whitespace=True,
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Separate models per operation:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
class UserCreate(BaseSchema):
|
|
25
|
+
email: EmailStr = Field(max_length=255)
|
|
26
|
+
password: str = Field(min_length=8, max_length=128)
|
|
27
|
+
full_name: str | None = Field(default=None, max_length=255)
|
|
28
|
+
|
|
29
|
+
class UserUpdate(BaseSchema):
|
|
30
|
+
email: EmailStr | None = Field(default=None, max_length=255)
|
|
31
|
+
password: str | None = Field(default=None, min_length=8, max_length=128)
|
|
32
|
+
full_name: str | None = Field(default=None, max_length=255)
|
|
33
|
+
is_active: bool | None = None
|
|
34
|
+
|
|
35
|
+
class UserRead(BaseSchema, TimestampSchema):
|
|
36
|
+
id: UUID
|
|
37
|
+
email: EmailStr
|
|
38
|
+
full_name: str | None = None
|
|
39
|
+
role: UserRole = UserRole.USER
|
|
40
|
+
avatar_url: str | None = None
|
|
41
|
+
|
|
42
|
+
class UserList(BaseSchema):
|
|
43
|
+
items: list[UserRead]
|
|
44
|
+
total: int
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Rules:
|
|
48
|
+
- `*Create` — required fields for creation, with `Field()` constraints
|
|
49
|
+
- `*Update` — all fields optional (`type | None = None`)
|
|
50
|
+
- `*Read` — includes `id` and timestamps, inherits `TimestampSchema`
|
|
51
|
+
- `*List` — `items` list + `total` count
|
|
52
|
+
- Use `@field_validator` for complex deserialization (e.g., JSON string → dict)
|
|
53
|
+
|
|
54
|
+
## SQLAlchemy Models (`app/db/models/`)
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
class User(Base, TimestampMixin):
|
|
58
|
+
__tablename__ = "users"
|
|
59
|
+
|
|
60
|
+
id: Mapped[UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
61
|
+
email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
|
|
62
|
+
hashed_password: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
63
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
64
|
+
|
|
65
|
+
conversations: Mapped[list["Conversation"]] = relationship(
|
|
66
|
+
"Conversation", back_populates="user", cascade="all, delete-orphan"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def __repr__(self) -> str:
|
|
70
|
+
return f"<User(id={self.id}, email={self.email})>"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Rules:
|
|
74
|
+
- Always inherit `Base` and `TimestampMixin` (provides `created_at`, `updated_at`)
|
|
75
|
+
- Use `Mapped[type]` with `mapped_column()` for all columns
|
|
76
|
+
- ForeignKey with `ondelete="CASCADE"` for parent references
|
|
77
|
+
- Always define `__repr__`
|
|
78
|
+
- Naming convention in `Base.metadata`: `{table}_{col}_key`, `{table}_{col}_fkey`, etc.
|
|
79
|
+
|
|
80
|
+
## TimestampMixin
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
class TimestampMixin:
|
|
84
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
85
|
+
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
86
|
+
)
|
|
87
|
+
updated_at: Mapped[datetime | None] = mapped_column(
|
|
88
|
+
DateTime(timezone=True), onupdate=func.now(), nullable=True
|
|
89
|
+
)
|
|
90
|
+
```
|