fastapi-spawn 0.4.12__tar.gz → 0.4.14__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_spawn-0.4.12 → fastapi_spawn-0.4.14}/PKG-INFO +30 -1
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/README.md +29 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/__init__.py +1 -1
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/cli.py +6 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/generator.py +11 -0
- fastapi_spawn-0.4.14/fastapi_spawn/templates/app/core/cache.py.j2 +32 -0
- fastapi_spawn-0.4.14/fastapi_spawn/templates/app/core/permissions.py.j2 +52 -0
- fastapi_spawn-0.4.14/fastapi_spawn/templates/app/middleware/response_format.py.j2 +64 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/pyproject.toml.j2 +3 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/pyproject.toml +1 -1
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/.gitignore +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/LICENSE +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/config.py +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/constants.py +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/interactive.py +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/alembic/alembic.ini.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/alembic/env.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/__init__.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/deps.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/graphql.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/auth/router.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/auth/sso.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/health/router.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/payments/router.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/router.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/streaming/router.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/ws/router.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/ai.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/config.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/email.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/exceptions.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/logger.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/logging.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/monitoring.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/notifications.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/ocr.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/search.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/security.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/storage.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/vector_db.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/ws_manager.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/db/session.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/frontend/index.html.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/main.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/middleware/__init__.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/middleware/rate_limit.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/middleware/request_logger.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/Makefile.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/README.md.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/env.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/env_example.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/gitignore.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/base/pre_commit.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/ci/github/publish.yml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/ci/github/tests.yml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/ci/gitlab/gitlab-ci.yml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/db/seed.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/docker/Dockerfile.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/docker/docker-compose.yml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/docker/dockerignore.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/docker/docker-compose.prod.yml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/helm/Chart.yaml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/helm/values.yaml.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/terraform/main.tf.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/terraform/variables.tf.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/root/main.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tasks/arq_worker.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tasks/celery_app.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tasks/sample_tasks.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tests/conftest.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tests/test_health.py.j2 +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/utils.py +0 -0
- {fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-spawn
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.14
|
|
4
4
|
Summary: A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Bishwajitgarai/fastapi-spawn
|
|
6
6
|
Project-URL: Documentation, https://github.com/Bishwajitgarai/fastapi-spawn#readme
|
|
@@ -100,6 +100,30 @@ fastapi-spawn add alembic
|
|
|
100
100
|
fastapi-spawn add sentry
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🆘 Built-in Help & Auto-Suggestions
|
|
108
|
+
|
|
109
|
+
`fastapi-spawn` comes with an integrated global help menu and intelligent typo correction:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Display the interactive global help menu
|
|
113
|
+
fastapi-spawn help
|
|
114
|
+
|
|
115
|
+
# Or use the standard flag
|
|
116
|
+
fastapi-spawn --help
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Auto-Typo Correction (Did you mean?)
|
|
120
|
+
If you accidentally misspell a command, the CLI will catch it and suggest the right one automatically to save you time.
|
|
121
|
+
```bash
|
|
122
|
+
$ fastapi-spawn nw my_app
|
|
123
|
+
|
|
124
|
+
Error: No such command 'nw'. Did you mean 'new'?
|
|
125
|
+
```
|
|
126
|
+
|
|
103
127
|
---
|
|
104
128
|
|
|
105
129
|
## Generated Project Structure
|
|
@@ -528,6 +552,11 @@ fastapi-spawn add seed # Faker database seeding script (Users, Posts, Co
|
|
|
528
552
|
fastapi-spawn add resend # Resend modern email client
|
|
529
553
|
fastapi-spawn add sentry # Sentry APM integration
|
|
530
554
|
fastapi-spawn add prometheus # Prometheus metrics
|
|
555
|
+
|
|
556
|
+
# Advanced API Features
|
|
557
|
+
fastapi-spawn add rbac # Role-Based Access Control (@RequireRole decorator)
|
|
558
|
+
fastapi-spawn add caching # Redis API Response Caching (@cache decorator)
|
|
559
|
+
fastapi-spawn add response-format # Global formatted JSON response middleware
|
|
531
560
|
```
|
|
532
561
|
|
|
533
562
|
*Example:*
|
|
@@ -59,6 +59,30 @@ fastapi-spawn add alembic
|
|
|
59
59
|
fastapi-spawn add sentry
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 🆘 Built-in Help & Auto-Suggestions
|
|
67
|
+
|
|
68
|
+
`fastapi-spawn` comes with an integrated global help menu and intelligent typo correction:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Display the interactive global help menu
|
|
72
|
+
fastapi-spawn help
|
|
73
|
+
|
|
74
|
+
# Or use the standard flag
|
|
75
|
+
fastapi-spawn --help
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Auto-Typo Correction (Did you mean?)
|
|
79
|
+
If you accidentally misspell a command, the CLI will catch it and suggest the right one automatically to save you time.
|
|
80
|
+
```bash
|
|
81
|
+
$ fastapi-spawn nw my_app
|
|
82
|
+
|
|
83
|
+
Error: No such command 'nw'. Did you mean 'new'?
|
|
84
|
+
```
|
|
85
|
+
|
|
62
86
|
---
|
|
63
87
|
|
|
64
88
|
## Generated Project Structure
|
|
@@ -487,6 +511,11 @@ fastapi-spawn add seed # Faker database seeding script (Users, Posts, Co
|
|
|
487
511
|
fastapi-spawn add resend # Resend modern email client
|
|
488
512
|
fastapi-spawn add sentry # Sentry APM integration
|
|
489
513
|
fastapi-spawn add prometheus # Prometheus metrics
|
|
514
|
+
|
|
515
|
+
# Advanced API Features
|
|
516
|
+
fastapi-spawn add rbac # Role-Based Access Control (@RequireRole decorator)
|
|
517
|
+
fastapi-spawn add caching # Redis API Response Caching (@cache decorator)
|
|
518
|
+
fastapi-spawn add response-format # Global formatted JSON response middleware
|
|
490
519
|
```
|
|
491
520
|
|
|
492
521
|
*Example:*
|
|
@@ -305,6 +305,9 @@ _ADDABLE_FEATURES = {
|
|
|
305
305
|
"sso-microsoft": "FastAPI SSO (Microsoft only)",
|
|
306
306
|
"seed": "Database seeding script using Faker",
|
|
307
307
|
"ocr": "PDF & OCR data pipeline (PyMuPDF / Tesseract)",
|
|
308
|
+
"rbac": "Role-Based Access Control (Permissions) boilerplate",
|
|
309
|
+
"caching": "Redis-based response caching (fastapi-cache2)",
|
|
310
|
+
"response-format": "Global JSON response formatting middleware",
|
|
308
311
|
"docker": "Dockerfile + docker-compose.yml",
|
|
309
312
|
"ci": "GitHub Actions CI/CD workflows",
|
|
310
313
|
"helm": "Helm chart (infra/helm/)",
|
|
@@ -379,6 +382,9 @@ def _feature_guidance(feature: str, _project_dir: Path) -> None:
|
|
|
379
382
|
"sso-microsoft": ["1. Use 'fastapi-spawn new temp_app --extra sso-microsoft' and copy the resulting sso.py"],
|
|
380
383
|
"seed": ["Add dep: faker>=25.0.0", "Create db/seed.py (generate 100 mock users/posts)", "Run: uv run python db/seed.py"],
|
|
381
384
|
"ocr": ["Add deps: pymupdf>=1.24.0, pytesseract>=0.3.10", "Create app/core/ocr.py (PDF parsing pipeline)", "Install system deps: sudo apt install tesseract-ocr"],
|
|
385
|
+
"rbac": ["Create app/core/permissions.py", "Add @router.get(..., dependencies=[Depends(RequireRole(['admin']))])"],
|
|
386
|
+
"caching": ["Add dep: fastapi-cache2[redis]>=0.2.1", "Create app/core/cache.py", "Initialize cache in lifespan and use @cache(expire=60) on endpoints"],
|
|
387
|
+
"response-format": ["Create app/middleware/response_format.py", "Add app.add_middleware(ResponseFormattingMiddleware) to main.py"],
|
|
382
388
|
"docker": ["Create Dockerfile (multi-stage, uv-based)", "Create docker-compose.yml with all selected services", "Create .dockerignore"],
|
|
383
389
|
"ci": ["Create .github/workflows/tests.yml (matrix: 3.10, 3.11, 3.12)", "Create .github/workflows/publish.yml (v* tags → PyPI)", "Add PYPI_API_TOKEN to GitHub repo secrets"],
|
|
384
390
|
"helm": ["Create infra/helm/Chart.yaml", "Create infra/helm/values.yaml (replicas, image, resources)", "Run: helm install my-release ./infra/helm"],
|
|
@@ -236,6 +236,17 @@ class ProjectGenerator:
|
|
|
236
236
|
if "meilisearch" in extras:
|
|
237
237
|
self._render_to(core / "search.py", "app/core/search.py.j2")
|
|
238
238
|
|
|
239
|
+
if "rbac" in extras:
|
|
240
|
+
self._render_to(core / "permissions.py", "app/core/permissions.py.j2")
|
|
241
|
+
|
|
242
|
+
if "caching" in extras:
|
|
243
|
+
self._render_to(core / "cache.py", "app/core/cache.py.j2")
|
|
244
|
+
|
|
245
|
+
if "response-format" in extras:
|
|
246
|
+
middleware = pkg / "middleware"
|
|
247
|
+
middleware.mkdir(exist_ok=True)
|
|
248
|
+
self._render_to(middleware / "response_format.py", "app/middleware/response_format.py.j2")
|
|
249
|
+
|
|
239
250
|
|
|
240
251
|
def _generate_tasks(self, root: Path) -> None:
|
|
241
252
|
"""Root-level tasks/ directory."""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from fastapi_cache import FastAPICache
|
|
2
|
+
from fastapi_cache.backends.redis import RedisBackend
|
|
3
|
+
from redis import asyncio as aioredis
|
|
4
|
+
from app.core.config import settings
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
async def init_cache():
|
|
10
|
+
"""
|
|
11
|
+
Initialize Redis caching backend for fastapi-cache2.
|
|
12
|
+
Call this function in your application startup event (e.g., lifespan in main.py).
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
redis_url = getattr(settings, "REDIS_URL", "redis://localhost:6379/0")
|
|
16
|
+
redis = aioredis.from_url(redis_url, encoding="utf8", decode_responses=True)
|
|
17
|
+
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
|
|
18
|
+
logger.info("Successfully initialized FastAPI Redis Cache.")
|
|
19
|
+
except Exception as e:
|
|
20
|
+
logger.error(f"Failed to initialize cache: {e}")
|
|
21
|
+
|
|
22
|
+
# --- Usage Example ---
|
|
23
|
+
#
|
|
24
|
+
# from fastapi_cache.decorator import cache
|
|
25
|
+
# from fastapi import APIRouter
|
|
26
|
+
#
|
|
27
|
+
# router = APIRouter()
|
|
28
|
+
#
|
|
29
|
+
# @router.get("/heavy-data")
|
|
30
|
+
# @cache(expire=60) # Caches the response for 60 seconds (TTL)
|
|
31
|
+
# async def get_heavy_data():
|
|
32
|
+
# return {"data": "This response is cached!"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import List, Callable
|
|
2
|
+
from fastapi import HTTPException, Security, status
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
# Mock User model for demonstration. Replace with your actual User model from app.models
|
|
6
|
+
class User(BaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
email: str
|
|
9
|
+
role: str
|
|
10
|
+
|
|
11
|
+
def get_current_user() -> User:
|
|
12
|
+
"""
|
|
13
|
+
Mock dependency. Replace this with your actual JWT/Auth dependency.
|
|
14
|
+
"""
|
|
15
|
+
return User(id="123", email="user@example.com", role="user")
|
|
16
|
+
|
|
17
|
+
class RequireRole:
|
|
18
|
+
"""
|
|
19
|
+
RBAC Dependency.
|
|
20
|
+
Usage:
|
|
21
|
+
@router.get("/admin", dependencies=[Depends(RequireRole(["admin", "superadmin"]))])
|
|
22
|
+
"""
|
|
23
|
+
def __init__(self, allowed_roles: List[str]):
|
|
24
|
+
self.allowed_roles = allowed_roles
|
|
25
|
+
|
|
26
|
+
def __call__(self, current_user: User = Security(get_current_user)) -> User:
|
|
27
|
+
if current_user.role not in self.allowed_roles:
|
|
28
|
+
raise HTTPException(
|
|
29
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
30
|
+
detail="You do not have the required role to access this resource."
|
|
31
|
+
)
|
|
32
|
+
return current_user
|
|
33
|
+
|
|
34
|
+
class RequirePermission:
|
|
35
|
+
"""
|
|
36
|
+
Fine-grained access control dependency.
|
|
37
|
+
Usage:
|
|
38
|
+
@router.get("/data", dependencies=[Depends(RequirePermission("read:data"))])
|
|
39
|
+
"""
|
|
40
|
+
def __init__(self, required_permission: str):
|
|
41
|
+
self.required_permission = required_permission
|
|
42
|
+
|
|
43
|
+
def __call__(self, current_user: User = Security(get_current_user)) -> User:
|
|
44
|
+
# Replace this logic with your actual permission checking (e.g. query DB for user permissions)
|
|
45
|
+
user_permissions = ["read:data", "write:data"] if current_user.role == "admin" else ["read:data"]
|
|
46
|
+
|
|
47
|
+
if self.required_permission not in user_permissions:
|
|
48
|
+
raise HTTPException(
|
|
49
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
50
|
+
detail=f"Missing required permission: {self.required_permission}"
|
|
51
|
+
)
|
|
52
|
+
return current_user
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
3
|
+
from fastapi import Request
|
|
4
|
+
from starlette.responses import JSONResponse
|
|
5
|
+
|
|
6
|
+
class ResponseFormattingMiddleware(BaseHTTPMiddleware):
|
|
7
|
+
"""
|
|
8
|
+
Intercepts all JSON responses and formats them into a standard structure:
|
|
9
|
+
{
|
|
10
|
+
"status": "success" | "error",
|
|
11
|
+
"message": "...",
|
|
12
|
+
"data": { ... },
|
|
13
|
+
"error": { ... }
|
|
14
|
+
}
|
|
15
|
+
"""
|
|
16
|
+
async def dispatch(self, request: Request, call_next):
|
|
17
|
+
response = await call_next(request)
|
|
18
|
+
|
|
19
|
+
# Only process JSON responses
|
|
20
|
+
if response.headers.get("content-type") == "application/json":
|
|
21
|
+
body = b""
|
|
22
|
+
async for chunk in response.body_iterator:
|
|
23
|
+
body += chunk
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
data = json.loads(body.decode("utf-8"))
|
|
27
|
+
except json.JSONDecodeError:
|
|
28
|
+
data = body.decode("utf-8")
|
|
29
|
+
|
|
30
|
+
is_success = response.status_code < 400
|
|
31
|
+
|
|
32
|
+
# Avoid wrapping if already formatted (e.g. custom exceptions might do this)
|
|
33
|
+
if isinstance(data, dict) and "status" in data and "data" in data and "error" in data:
|
|
34
|
+
return JSONResponse(
|
|
35
|
+
content=data,
|
|
36
|
+
status_code=response.status_code,
|
|
37
|
+
headers=dict(response.headers)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Determine appropriate message
|
|
41
|
+
if is_success:
|
|
42
|
+
message = "Request successful"
|
|
43
|
+
else:
|
|
44
|
+
# FastAPI default validation errors are in `detail`
|
|
45
|
+
message = data.get("detail", "An error occurred") if isinstance(data, dict) else "An error occurred"
|
|
46
|
+
|
|
47
|
+
formatted_response = {
|
|
48
|
+
"status": "success" if is_success else "error",
|
|
49
|
+
"message": message,
|
|
50
|
+
"data": data if is_success else None,
|
|
51
|
+
"error": data if not is_success else None
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Remove content-length as the payload size has changed
|
|
55
|
+
headers = dict(response.headers)
|
|
56
|
+
headers.pop("content-length", None)
|
|
57
|
+
|
|
58
|
+
return JSONResponse(
|
|
59
|
+
content=formatted_response,
|
|
60
|
+
status_code=response.status_code,
|
|
61
|
+
headers=headers
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return response
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-spawn"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.14"
|
|
8
8
|
description = "A powerful CLI tool to scaffold production-ready FastAPI projects with flexible database, auth, broker, and deployment options."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/alembic/alembic.ini.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/auth/router.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/auth/sso.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/health/router.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/router.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/api/v1/ws/router.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/exceptions.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/logging.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/monitoring.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/notifications.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/security.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/storage.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/vector_db.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/core/ws_manager.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/frontend/index.html.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/app/middleware/__init__.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/ci/github/publish.yml.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/ci/github/tests.yml.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/ci/gitlab/gitlab-ci.yml.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/docker/docker-compose.yml.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/docker/dockerignore.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/helm/Chart.yaml.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/helm/values.yaml.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/infra/terraform/main.tf.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tasks/arq_worker.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tasks/celery_app.py.j2
RENAMED
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tasks/sample_tasks.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_spawn-0.4.12 → fastapi_spawn-0.4.14}/fastapi_spawn/templates/tests/test_health.py.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|