c2casgiutils 0.0.0__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.
- c2casgiutils-0.0.0/PKG-INFO +466 -0
- c2casgiutils-0.0.0/README.md +411 -0
- c2casgiutils-0.0.0/c2casgiutils/__init__.py +26 -0
- c2casgiutils-0.0.0/c2casgiutils/auth.py +622 -0
- c2casgiutils-0.0.0/c2casgiutils/broadcast/__init__.py +170 -0
- c2casgiutils-0.0.0/c2casgiutils/broadcast/interface.py +28 -0
- c2casgiutils-0.0.0/c2casgiutils/broadcast/local.py +44 -0
- c2casgiutils-0.0.0/c2casgiutils/broadcast/redis.py +198 -0
- c2casgiutils-0.0.0/c2casgiutils/broadcast/utils.py +10 -0
- c2casgiutils-0.0.0/c2casgiutils/config.py +251 -0
- c2casgiutils-0.0.0/c2casgiutils/headers.py +268 -0
- c2casgiutils-0.0.0/c2casgiutils/health_checks.py +346 -0
- c2casgiutils-0.0.0/c2casgiutils/py.typed +0 -0
- c2casgiutils-0.0.0/c2casgiutils/redis_utils.py +77 -0
- c2casgiutils-0.0.0/c2casgiutils/scripts/genversion.py +62 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/__init__.py +91 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/headers.py +85 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/logging_.py +146 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/static/favicon-16x16.png +0 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/static/favicon-32x32.png +0 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/static/index.css +18 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/static/index.js +234 -0
- c2casgiutils-0.0.0/c2casgiutils/tools/templates/index.html +109 -0
- c2casgiutils-0.0.0/pyproject.toml +116 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: c2casgiutils
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Common utilities for Camptocamp ASGI applications
|
|
5
|
+
License: BSD-2-Clause
|
|
6
|
+
Keywords: sqlalchemy,asgi,fastapi
|
|
7
|
+
Author: Camptocamp
|
|
8
|
+
Author-email: info@camptocamp.com
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Environment :: Plugins
|
|
12
|
+
Classifier: Framework :: Pyramid
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Provides-Extra: alembic
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Provides-Extra: fastapi
|
|
28
|
+
Provides-Extra: prometheus
|
|
29
|
+
Provides-Extra: sentry
|
|
30
|
+
Provides-Extra: sqlalchemy
|
|
31
|
+
Requires-Dist: aiofile
|
|
32
|
+
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
|
33
|
+
Requires-Dist: aiohttp
|
|
34
|
+
Requires-Dist: alembic ; extra == "alembic"
|
|
35
|
+
Requires-Dist: alembic ; extra == "all"
|
|
36
|
+
Requires-Dist: fastapi[standard] ; extra == "all"
|
|
37
|
+
Requires-Dist: fastapi[standard] ; extra == "fastapi"
|
|
38
|
+
Requires-Dist: prometheus-client
|
|
39
|
+
Requires-Dist: prometheus-fastapi-instrumentator
|
|
40
|
+
Requires-Dist: prometheus-fastapi-instrumentator ; extra == "all"
|
|
41
|
+
Requires-Dist: prometheus-fastapi-instrumentator ; extra == "prometheus"
|
|
42
|
+
Requires-Dist: pydantic-settings
|
|
43
|
+
Requires-Dist: pyjwt
|
|
44
|
+
Requires-Dist: redis
|
|
45
|
+
Requires-Dist: sentry-sdk[fastapi]
|
|
46
|
+
Requires-Dist: sentry-sdk[fastapi] ; extra == "all"
|
|
47
|
+
Requires-Dist: sentry-sdk[fastapi] ; extra == "sentry"
|
|
48
|
+
Requires-Dist: sqlalchemy ; extra == "alembic"
|
|
49
|
+
Requires-Dist: sqlalchemy ; extra == "all"
|
|
50
|
+
Requires-Dist: sqlalchemy ; extra == "sqlalchemy"
|
|
51
|
+
Project-URL: Bug Tracker, https://github.com/camptocamp/c2casgiutils/issues
|
|
52
|
+
Project-URL: Repository, https://github.com/camptocamp/c2casgiutils
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# Camptocamp ASGI Utils
|
|
56
|
+
|
|
57
|
+
This package provides a set of utilities to help you build ASGI applications with Python.
|
|
58
|
+
|
|
59
|
+
## Stack
|
|
60
|
+
|
|
61
|
+
Stack that we consider that the project uses:
|
|
62
|
+
|
|
63
|
+
- [FastAPI](https://github.com/fastapi/fastapi)
|
|
64
|
+
- [uvicorn](https://www.uvicorn.org/)
|
|
65
|
+
- [SQLAlchemy](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html)
|
|
66
|
+
- [Redis](https://redis.readthedocs.io/en/stable/examples/asyncio_examples.html)
|
|
67
|
+
- [Prometheus FastAPI Instrumentator](https://github.com/trallnag/prometheus-fastapi-instrumentator)
|
|
68
|
+
- [Sentry](https://docs.sentry.io/platforms/python/integrations/fastapi/)
|
|
69
|
+
- [Pydantic settings](https://docs.pydantic.dev/latest/usage/settings/)
|
|
70
|
+
|
|
71
|
+
## Environment variables
|
|
72
|
+
|
|
73
|
+
See: https://github.com/camptocamp/c2casgiutils/blob/master/c2casgiutils/config.py
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install c2casgiutils[all]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Add in your application:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import c2casgiutils
|
|
85
|
+
from c2casgiutils import broadcast
|
|
86
|
+
from c2casgiutils import config
|
|
87
|
+
from prometheus_client import start_http_server
|
|
88
|
+
from prometheus_fastapi_instrumentator import Instrumentator
|
|
89
|
+
from contextlib import asynccontextmanager
|
|
90
|
+
|
|
91
|
+
@asynccontextmanager
|
|
92
|
+
async def _lifespan(main_app: FastAPI) -> None:
|
|
93
|
+
"""Handle application lifespan events."""
|
|
94
|
+
|
|
95
|
+
_LOGGER.info("Starting the application")
|
|
96
|
+
await c2casgiutils.startup(main_app)
|
|
97
|
+
|
|
98
|
+
yield
|
|
99
|
+
|
|
100
|
+
app = FastAPI(title="My fastapi_app application", lifespan=_lifespan)
|
|
101
|
+
|
|
102
|
+
app.mount('/c2c', c2casgiutils.app)
|
|
103
|
+
|
|
104
|
+
# For security headers (and compression)
|
|
105
|
+
|
|
106
|
+
# Add TrustedHostMiddleware (should be first)
|
|
107
|
+
app.add_middleware(
|
|
108
|
+
TrustedHostMiddleware,
|
|
109
|
+
allowed_hosts=["*"], # Configure with specific hosts in production
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Add HTTPSRedirectMiddleware
|
|
113
|
+
if os.environ.get("HTTP", "False").lower() not in ["true", "1"]:
|
|
114
|
+
app.add_middleware(HTTPSRedirectMiddleware)
|
|
115
|
+
|
|
116
|
+
# Add GZipMiddleware
|
|
117
|
+
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
|
118
|
+
|
|
119
|
+
# Set all CORS origins enabled
|
|
120
|
+
app.add_middleware(
|
|
121
|
+
CORSMiddleware,
|
|
122
|
+
allow_origins=["*"],
|
|
123
|
+
allow_credentials=True,
|
|
124
|
+
allow_methods=["*"],
|
|
125
|
+
allow_headers=["*"],
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
app.add_middleware(headers.ArmorHeaderMiddleware)
|
|
129
|
+
|
|
130
|
+
# Get Prometheus HTTP server port from environment variable 9000 by default
|
|
131
|
+
start_http_server(config.settings.prometheus.port)
|
|
132
|
+
|
|
133
|
+
instrumentator = Instrumentator(should_instrument_requests_inprogress=True)
|
|
134
|
+
instrumentator.instrument(app)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Broadcasting
|
|
138
|
+
|
|
139
|
+
To use the broadcasting you should do something like this:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
|
|
143
|
+
import c2casgiutils
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class BroadcastResponse(BaseModel):
|
|
147
|
+
"""Response from broadcast endpoint."""
|
|
148
|
+
|
|
149
|
+
result: list[dict[str, Any]] | None = None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
echo_handler: Callable[[], Awaitable[list[BroadcastResponse] | None]] = None # type: ignore[assignment]
|
|
153
|
+
|
|
154
|
+
# Create a handler that will receive broadcasts
|
|
155
|
+
async def echo_handler_() -> dict[str, Any]:
|
|
156
|
+
"""Echo handler for broadcast messages."""
|
|
157
|
+
return {"message": "Broadcast echo"}
|
|
158
|
+
|
|
159
|
+
# Subscribe the handler to a channel on module import
|
|
160
|
+
@asynccontextmanager
|
|
161
|
+
async def _lifespan(main_app: FastAPI) -> None:
|
|
162
|
+
"""Handle application lifespan events."""
|
|
163
|
+
|
|
164
|
+
_LOGGER.info("Starting the application")
|
|
165
|
+
await c2casgiutils.startup(main_app)
|
|
166
|
+
|
|
167
|
+
# Register the echo handler
|
|
168
|
+
global echo_handler # pylint: disable=global-statement
|
|
169
|
+
echo_handler = await broadcast.decorate(echo_handler_, expect_answers=True)
|
|
170
|
+
|
|
171
|
+
yield
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Then you can use the `echo_handler` function you will have the response of all the registered applications.
|
|
175
|
+
|
|
176
|
+
## Health checks
|
|
177
|
+
|
|
178
|
+
The `health_checks` module provides a flexible system for checking the health of various components of your application. Health checks are exposed through a REST API endpoint at `/c2c/health_checks` and are also integrated with Prometheus metrics.
|
|
179
|
+
|
|
180
|
+
### Basic Usage
|
|
181
|
+
|
|
182
|
+
To initialize health checks in your application:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from c2casgiutils import health_checks
|
|
186
|
+
|
|
187
|
+
# Add Redis health check
|
|
188
|
+
health_checks.FACTORY.add(health_checks.Redis(tags=["liveness", "redis", "all"]))
|
|
189
|
+
|
|
190
|
+
# Add SQLAlchemy database connection check
|
|
191
|
+
from sqlalchemy.ext.asyncio import async_sessionmaker, AsyncSession
|
|
192
|
+
health_checks.FACTORY.add(health_checks.SQLAlchemy(Session=your_async_sessionmaker, tags=["database", "all"]))
|
|
193
|
+
|
|
194
|
+
# Add Alembic migration version check
|
|
195
|
+
health_checks.FACTORY.add(health_checks.Alembic(
|
|
196
|
+
Session=your_async_sessionmaker,
|
|
197
|
+
config_file="alembic.ini",
|
|
198
|
+
tags=["migrations", "database", "all"]
|
|
199
|
+
))
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Available Health Checks
|
|
203
|
+
|
|
204
|
+
The package provides several built-in health check implementations:
|
|
205
|
+
|
|
206
|
+
1. **Redis**: Checks connectivity to Redis by pinging both master and slave instances
|
|
207
|
+
2. **SQLAlchemy**: Verifies database connectivity by executing a simple query
|
|
208
|
+
3. **Alembic**: Ensures the database schema is up-to-date with the latest migrations
|
|
209
|
+
|
|
210
|
+
### Custom Health Checks
|
|
211
|
+
|
|
212
|
+
You can create custom health checks by extending the `Check` base class:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from c2casgiutils.health_checks import Check, Result
|
|
216
|
+
|
|
217
|
+
class MyCustomCheck(Check):
|
|
218
|
+
async def check(self) -> Result:
|
|
219
|
+
# Your check logic here
|
|
220
|
+
try:
|
|
221
|
+
# Perform your check...
|
|
222
|
+
return Result(status_code=200, payload={"message": "Everything is fine!"})
|
|
223
|
+
except Exception as e:
|
|
224
|
+
return Result(status_code=500, payload={"error": str(e)})
|
|
225
|
+
|
|
226
|
+
# Add your custom check
|
|
227
|
+
health_checks.FACTORY.add(MyCustomCheck(tags=["custom", "all"]))
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Filtering Health Checks
|
|
231
|
+
|
|
232
|
+
Health checks can be filtered using tags or names:
|
|
233
|
+
|
|
234
|
+
- **Tags**: Add relevant tags when creating a check to categorize it
|
|
235
|
+
- **API Filtering**: Use query parameters to filter checks when calling the API:
|
|
236
|
+
- `/c2c/health_checks?tags=database,critical` - Run only checks with "database" or "critical" tags
|
|
237
|
+
- `/c2c/health_checks?name=Redis` - Run only the Redis check
|
|
238
|
+
|
|
239
|
+
### Prometheus Integration
|
|
240
|
+
|
|
241
|
+
Health check results are automatically exported to Prometheus metrics via the `health_checks_failure` gauge, allowing you to monitor and alert on health check failures.
|
|
242
|
+
|
|
243
|
+
## Middleware
|
|
244
|
+
|
|
245
|
+
### Headers Middleware
|
|
246
|
+
|
|
247
|
+
The `ArmorHeaderMiddleware` provides automatic security headers configuration for your ASGI application. It allows you to configure headers based on request netloc (host:port) and path patterns.
|
|
248
|
+
|
|
249
|
+
#### Basic Usage
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from c2casgiutils.headers import ArmorHeaderMiddleware
|
|
253
|
+
|
|
254
|
+
# Use default security headers
|
|
255
|
+
app.add_middleware(ArmorHeaderMiddleware)
|
|
256
|
+
|
|
257
|
+
# Or with custom configuration
|
|
258
|
+
app.add_middleware(ArmorHeaderMiddleware, headers_config=your_custom_config)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Default Security Headers
|
|
262
|
+
|
|
263
|
+
The middleware comes with sensible security defaults including:
|
|
264
|
+
|
|
265
|
+
- **Content-Security-Policy**: Restricts resource loading to prevent XSS attacks
|
|
266
|
+
- **X-Frame-Options**: Prevents clickjacking by denying iframe embedding
|
|
267
|
+
- **Strict-Transport-Security**: Forces HTTPS connections (disabled for localhost)
|
|
268
|
+
- **X-Content-Type-Options**: Prevents MIME-type sniffing
|
|
269
|
+
- **Referrer-Policy**: Controls referrer information sent with requests
|
|
270
|
+
- **Permissions-Policy**: Restricts access to browser features like geolocation
|
|
271
|
+
- **X-DNS-Prefetch-Control**: Disables DNS prefetching
|
|
272
|
+
- **Expect-CT**: Certificate Transparency enforcement
|
|
273
|
+
- **Origin-Agent-Cluster**: Isolates origin agent clusters
|
|
274
|
+
- **Cross-Origin policies**: CORP, COOP, COEP for cross-origin protection
|
|
275
|
+
|
|
276
|
+
#### Custom Configuration
|
|
277
|
+
|
|
278
|
+
You can configure headers based on request patterns:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from c2casgiutils.headers import ArmorHeaderMiddleware
|
|
282
|
+
|
|
283
|
+
custom_config = {
|
|
284
|
+
"api_endpoints": {
|
|
285
|
+
"path_match": r"^/api/.*", # Regex pattern for paths
|
|
286
|
+
"headers": {
|
|
287
|
+
"Access-Control-Allow-Origin": "*",
|
|
288
|
+
"X-Custom-Header": "api-value"
|
|
289
|
+
},
|
|
290
|
+
"order": 1 # Processing order
|
|
291
|
+
},
|
|
292
|
+
"admin_section": {
|
|
293
|
+
"netloc_match": r"^admin\..*", # Regex for host matching
|
|
294
|
+
"path_match": r"^/admin/.*",
|
|
295
|
+
"headers": {
|
|
296
|
+
"X-Robots-Tag": "noindex, nofollow"
|
|
297
|
+
},
|
|
298
|
+
"status_code": 200, # Only apply for specific status code
|
|
299
|
+
"order": 2
|
|
300
|
+
},
|
|
301
|
+
"success_responses": {
|
|
302
|
+
"headers": {
|
|
303
|
+
"Cache-Control": ["public", "max-age=3600"]
|
|
304
|
+
},
|
|
305
|
+
"status_code": (200, 299), # Apply for a range of status codes (200-299)
|
|
306
|
+
"order": 3
|
|
307
|
+
},
|
|
308
|
+
"api_methods": {
|
|
309
|
+
"path_match": r"^/api/.*",
|
|
310
|
+
"methods": ["GET", "HEAD"], # Only apply for specific HTTP methods
|
|
311
|
+
"headers": {
|
|
312
|
+
"Cache-Control": ["public", "max-age=3600"]
|
|
313
|
+
},
|
|
314
|
+
"order": 4
|
|
315
|
+
},
|
|
316
|
+
"remove_header": {
|
|
317
|
+
"headers": {
|
|
318
|
+
"Server": None # Remove header by setting to None
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
app.add_middleware(ArmorHeaderMiddleware, headers_config=custom_config)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### Header Value Types
|
|
327
|
+
|
|
328
|
+
Headers support multiple value types:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
headers = {
|
|
332
|
+
# String value
|
|
333
|
+
"X-Custom": "value",
|
|
334
|
+
|
|
335
|
+
# List (joined with "; ")
|
|
336
|
+
"Cache-Control": ["no-cache", "no-store", "must-revalidate"],
|
|
337
|
+
|
|
338
|
+
# Dictionary (for complex headers like CSP)
|
|
339
|
+
"Content-Security-Policy": {
|
|
340
|
+
"default-src": ["'self'"],
|
|
341
|
+
"script-src": ["'self'", "https://cdn.example.com"],
|
|
342
|
+
"style-src": ["'self'", "'unsafe-inline'"]
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
# List (joined with ", ") for Permissions-Policy
|
|
346
|
+
"Permissions-Policy": ["geolocation=()", "microphone=()"],
|
|
347
|
+
|
|
348
|
+
# Remove header
|
|
349
|
+
"Unwanted-Header": None
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Special Localhost Handling
|
|
354
|
+
|
|
355
|
+
The middleware automatically disables `Strict-Transport-Security` for localhost to facilitate development.
|
|
356
|
+
|
|
357
|
+
#### Status Code Configuration
|
|
358
|
+
|
|
359
|
+
You can apply headers conditionally based on response status codes:
|
|
360
|
+
|
|
361
|
+
- Apply to a single status code: `"status_code": 200`
|
|
362
|
+
- Apply to a range of status codes: `"status_code": (200, 299)` (for all 2xx success responses)
|
|
363
|
+
|
|
364
|
+
This feature is useful for adding caching headers only to successful responses, or special headers for specific error codes.
|
|
365
|
+
|
|
366
|
+
#### HTTP Method Filtering
|
|
367
|
+
|
|
368
|
+
You can configure headers to be applied only for specific HTTP methods:
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
{
|
|
372
|
+
"api_post_endpoints": {
|
|
373
|
+
"path_match": r"^/api/.*",
|
|
374
|
+
"methods": ["POST", "PUT", "PATCH"], # Only apply for these methods
|
|
375
|
+
"headers": {
|
|
376
|
+
"Cache-Control": "no-store"
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
"api_get_endpoints": {
|
|
380
|
+
"path_match": r"^/api/.*",
|
|
381
|
+
"methods": ["GET", "HEAD"], # Only apply for GET and HEAD requests
|
|
382
|
+
"headers": {
|
|
383
|
+
"Cache-Control": ["public", "max-age=3600"]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
This allows for fine-grained control over which headers are applied based on the request method, useful for implementing different caching strategies for read vs. write operations.
|
|
390
|
+
|
|
391
|
+
#### Content-Security-Policy and security considerations
|
|
392
|
+
|
|
393
|
+
With the default CSP your html application will not work, to make it working without impacting the security Of the other pages you should add in the `headers_config` something like this:
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
{
|
|
397
|
+
"my_page": {
|
|
398
|
+
"path_match": r"^your-path/?",
|
|
399
|
+
"headers": {
|
|
400
|
+
"Content-Security-Policy": {
|
|
401
|
+
"default-src": ["'self'"],
|
|
402
|
+
"script-src-elem": ["'self'", ...],
|
|
403
|
+
"style-src-elem": ["'self'", ...],
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
"order": 1
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
And do the same for other headers.
|
|
412
|
+
|
|
413
|
+
#### Cache-Control Header
|
|
414
|
+
|
|
415
|
+
The `Cache-Control` header can be configured to control caching behavior for different endpoints. You can specify it as a string, list, or dictionary:
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
{
|
|
419
|
+
"api_endpoints": {
|
|
420
|
+
"path_match": r"^/api/.*",
|
|
421
|
+
"headers": {
|
|
422
|
+
"Cache-Control": ["public", "max-age=3600"] # Cache for 1 hour
|
|
423
|
+
},
|
|
424
|
+
"order": 1
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
By default the middleware will not set any `Cache-Control` header, so you should explicitly configure it to enable caching.
|
|
430
|
+
|
|
431
|
+
## Authentication
|
|
432
|
+
|
|
433
|
+
The package also provides authentication utilities for GitHub-based authentication and API key validation. See the `auth.py` module for detailed configuration options.
|
|
434
|
+
|
|
435
|
+
## Prometheus Metrics
|
|
436
|
+
|
|
437
|
+
To enable Prometheus metrics in your FastAPI application, you can use the `prometheus_fastapi_instrumentator` package. Here's how to set it up:
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
from c2casgiutils import config
|
|
441
|
+
from prometheus_client import start_http_server
|
|
442
|
+
from prometheus_fastapi_instrumentator import Instrumentator
|
|
443
|
+
|
|
444
|
+
# Get Prometheus HTTP server port from environment variable 9000 by default
|
|
445
|
+
start_http_server(config.settings.prometheus.port)
|
|
446
|
+
|
|
447
|
+
instrumentator = Instrumentator(should_instrument_requests_inprogress=True)
|
|
448
|
+
instrumentator.instrument(app)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Sentry Integration
|
|
452
|
+
|
|
453
|
+
To enable error tracking with Sentry in your application:
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
import os
|
|
457
|
+
import sentry_sdk
|
|
458
|
+
|
|
459
|
+
# Initialize Sentry if the URL is provided
|
|
460
|
+
if config.settings.sentry.dsn or 'SENTRY_DSN' in os.environ:
|
|
461
|
+
_LOGGER.info("Sentry is enabled with URL: %s", config.settings.sentry.url or os.environ.get("SENTRY_DSN"))
|
|
462
|
+
sentry_sdk.init(**config.settings.sentry.model_dump())
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Sentry will automatically capture exceptions and errors in your FastAPI application. For more advanced usage, refer to the [Sentry Python SDK documentation](https://docs.sentry.io/platforms/python/) and [FastAPI integration guide](https://docs.sentry.io/platforms/python/integrations/fastapi/).
|
|
466
|
+
|