fastapi-ht 0.1.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.
- fastapi_ht-0.1.0/.github/workflows/ci.yml +30 -0
- fastapi_ht-0.1.0/.github/workflows/publish.yml +33 -0
- fastapi_ht-0.1.0/.gitignore +2 -0
- fastapi_ht-0.1.0/.python-version +1 -0
- fastapi_ht-0.1.0/PKG-INFO +132 -0
- fastapi_ht-0.1.0/README.md +120 -0
- fastapi_ht-0.1.0/public/example_use.png +0 -0
- fastapi_ht-0.1.0/public/logo.png +0 -0
- fastapi_ht-0.1.0/pyproject.toml +35 -0
- fastapi_ht-0.1.0/src/examples/basic_app.py +19 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/__init__.py +17 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/assets/__init__.py +1 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/assets/health-report.css +178 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/assets/health-report.html +33 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/checks.py +70 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/integration/__init__.py +3 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/integration/fastapi.py +36 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/models.py +32 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/registry.py +31 -0
- fastapi_ht-0.1.0/src/fastapi_health_check/ui.py +49 -0
- fastapi_ht-0.1.0/tests/conftest.py +84 -0
- fastapi_ht-0.1.0/tests/test_checks.py +50 -0
- fastapi_ht-0.1.0/tests/test_fastapi_integration.py +123 -0
- fastapi_ht-0.1.0/tests/test_registry.py +68 -0
- fastapi_ht-0.1.0/uv.lock +490 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Run tests
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Check out repository
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v6
|
|
25
|
+
|
|
26
|
+
- name: Sync project dependencies
|
|
27
|
+
run: uv sync --dev
|
|
28
|
+
|
|
29
|
+
- name: Run test suite
|
|
30
|
+
run: uv run pytest
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types:
|
|
6
|
+
- published
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
publish:
|
|
11
|
+
name: Publish to PyPI
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Check out repository
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v6
|
|
28
|
+
|
|
29
|
+
- name: Build distributions
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Publish distributions to PyPI
|
|
33
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-ht
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Health check utilities for FastAPI applications.
|
|
5
|
+
Project-URL: Homepage, https://github.com/PinnLabs/fastapi-health-check
|
|
6
|
+
Project-URL: Repository, https://github.com/PinnLabs/fastapi-health-check
|
|
7
|
+
Project-URL: Issues, https://github.com/PinnLabs/fastapi-health-check/issues
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: fastapi>=0.135.3
|
|
10
|
+
Requires-Dist: pydantic>=2.11.7
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img src="https://raw.githubusercontent.com/PinnLabs/fastapi-health-check/main/public/logo.png" alt="fastapi-health-check logo" width="520" style="display: block; margin:
|
|
15
|
+
0 auto;" />
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
# fastapi-health-check
|
|
19
|
+
|
|
20
|
+
FastAPI health checks with a small public API, a visual status page, and JSON responses from the same endpoint.
|
|
21
|
+
|
|
22
|
+
### Example interface
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
## What the library provides
|
|
27
|
+
|
|
28
|
+
- A base contract for advanced checks
|
|
29
|
+
- A lightweight registry for collecting and running checks
|
|
30
|
+
- A single `/ht` endpoint with HTML by default
|
|
31
|
+
- JSON responses when the client sends `Accept: application/json`
|
|
32
|
+
- A simple way to monitor any custom area of your system
|
|
33
|
+
|
|
34
|
+
## Important note
|
|
35
|
+
|
|
36
|
+
The library does not ship with a database check by default.
|
|
37
|
+
|
|
38
|
+
The only built-in check today is `AppAliveCheck`, which reports that the application is up. Database, Redis, queues, external APIs, or any other monitored area are meant to be registered by the user.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from fastapi import FastAPI
|
|
44
|
+
|
|
45
|
+
from fastapi_health_check import AppAliveCheck, HealthRegistry, health_check, install_health_check
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
app = FastAPI()
|
|
49
|
+
registry = HealthRegistry(
|
|
50
|
+
[
|
|
51
|
+
AppAliveCheck(),
|
|
52
|
+
health_check("database", lambda: "connection ok"),
|
|
53
|
+
health_check("redis", lambda: "cache reachable"),
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
install_health_check(app, registry)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This exposes `GET /ht`.
|
|
61
|
+
|
|
62
|
+
- In a browser, the route renders an HTML health page
|
|
63
|
+
- For automated integrations, the same route returns JSON when the client sends `Accept: application/json`
|
|
64
|
+
|
|
65
|
+
## Monitoring custom areas
|
|
66
|
+
|
|
67
|
+
If you want to monitor anything beyond the built-in app liveness check, the easiest option is the `health_check()` factory.
|
|
68
|
+
|
|
69
|
+
You can use it for:
|
|
70
|
+
|
|
71
|
+
- databases
|
|
72
|
+
- Redis or cache layers
|
|
73
|
+
- background queues
|
|
74
|
+
- external APIs
|
|
75
|
+
- storage services
|
|
76
|
+
- internal domain-specific dependencies
|
|
77
|
+
|
|
78
|
+
### Synchronous checks
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from fastapi_health_check import health_check
|
|
82
|
+
|
|
83
|
+
database_check = health_check("database", lambda: "connection ok")
|
|
84
|
+
redis_check = health_check("redis", lambda: "cache reachable")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Asynchronous checks
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from fastapi_health_check import health_check
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def payments_api_check() -> str | None:
|
|
94
|
+
return "payments API available"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
payments_check = health_check("payments_api", payments_api_check)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Class-based checks for advanced cases
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from fastapi_health_check import HealthCheck
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class QueueCheck(HealthCheck):
|
|
107
|
+
default_name = "queue"
|
|
108
|
+
|
|
109
|
+
async def check(self) -> str | None:
|
|
110
|
+
return "queue connected"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use class-based checks when you want:
|
|
114
|
+
|
|
115
|
+
- dependency injection through `__init__`
|
|
116
|
+
- reusable state
|
|
117
|
+
- more structured custom behavior
|
|
118
|
+
|
|
119
|
+
## Local manual testing
|
|
120
|
+
|
|
121
|
+
The repository includes a local example application at `src/examples/basic_app.py`.
|
|
122
|
+
|
|
123
|
+
Run it with:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv run uvicorn src.examples.basic_app:app --reload
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Then open:
|
|
130
|
+
|
|
131
|
+
- `http://127.0.0.1:8000/ht` for the HTML page
|
|
132
|
+
- `curl -H "Accept: application/json" http://127.0.0.1:8000/ht` for JSON
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/PinnLabs/fastapi-health-check/main/public/logo.png" alt="fastapi-health-check logo" width="520" style="display: block; margin:
|
|
3
|
+
0 auto;" />
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
# fastapi-health-check
|
|
7
|
+
|
|
8
|
+
FastAPI health checks with a small public API, a visual status page, and JSON responses from the same endpoint.
|
|
9
|
+
|
|
10
|
+
### Example interface
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
## What the library provides
|
|
15
|
+
|
|
16
|
+
- A base contract for advanced checks
|
|
17
|
+
- A lightweight registry for collecting and running checks
|
|
18
|
+
- A single `/ht` endpoint with HTML by default
|
|
19
|
+
- JSON responses when the client sends `Accept: application/json`
|
|
20
|
+
- A simple way to monitor any custom area of your system
|
|
21
|
+
|
|
22
|
+
## Important note
|
|
23
|
+
|
|
24
|
+
The library does not ship with a database check by default.
|
|
25
|
+
|
|
26
|
+
The only built-in check today is `AppAliveCheck`, which reports that the application is up. Database, Redis, queues, external APIs, or any other monitored area are meant to be registered by the user.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from fastapi import FastAPI
|
|
32
|
+
|
|
33
|
+
from fastapi_health_check import AppAliveCheck, HealthRegistry, health_check, install_health_check
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
app = FastAPI()
|
|
37
|
+
registry = HealthRegistry(
|
|
38
|
+
[
|
|
39
|
+
AppAliveCheck(),
|
|
40
|
+
health_check("database", lambda: "connection ok"),
|
|
41
|
+
health_check("redis", lambda: "cache reachable"),
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
install_health_check(app, registry)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This exposes `GET /ht`.
|
|
49
|
+
|
|
50
|
+
- In a browser, the route renders an HTML health page
|
|
51
|
+
- For automated integrations, the same route returns JSON when the client sends `Accept: application/json`
|
|
52
|
+
|
|
53
|
+
## Monitoring custom areas
|
|
54
|
+
|
|
55
|
+
If you want to monitor anything beyond the built-in app liveness check, the easiest option is the `health_check()` factory.
|
|
56
|
+
|
|
57
|
+
You can use it for:
|
|
58
|
+
|
|
59
|
+
- databases
|
|
60
|
+
- Redis or cache layers
|
|
61
|
+
- background queues
|
|
62
|
+
- external APIs
|
|
63
|
+
- storage services
|
|
64
|
+
- internal domain-specific dependencies
|
|
65
|
+
|
|
66
|
+
### Synchronous checks
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from fastapi_health_check import health_check
|
|
70
|
+
|
|
71
|
+
database_check = health_check("database", lambda: "connection ok")
|
|
72
|
+
redis_check = health_check("redis", lambda: "cache reachable")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Asynchronous checks
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from fastapi_health_check import health_check
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def payments_api_check() -> str | None:
|
|
82
|
+
return "payments API available"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
payments_check = health_check("payments_api", payments_api_check)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Class-based checks for advanced cases
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from fastapi_health_check import HealthCheck
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class QueueCheck(HealthCheck):
|
|
95
|
+
default_name = "queue"
|
|
96
|
+
|
|
97
|
+
async def check(self) -> str | None:
|
|
98
|
+
return "queue connected"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Use class-based checks when you want:
|
|
102
|
+
|
|
103
|
+
- dependency injection through `__init__`
|
|
104
|
+
- reusable state
|
|
105
|
+
- more structured custom behavior
|
|
106
|
+
|
|
107
|
+
## Local manual testing
|
|
108
|
+
|
|
109
|
+
The repository includes a local example application at `src/examples/basic_app.py`.
|
|
110
|
+
|
|
111
|
+
Run it with:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run uvicorn src.examples.basic_app:app --reload
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then open:
|
|
118
|
+
|
|
119
|
+
- `http://127.0.0.1:8000/ht` for the HTML page
|
|
120
|
+
- `curl -H "Accept: application/json" http://127.0.0.1:8000/ht` for JSON
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fastapi-ht"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Health check utilities for FastAPI applications."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"fastapi>=0.135.3",
|
|
13
|
+
"pydantic>=2.11.7",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://github.com/PinnLabs/fastapi-health-check"
|
|
18
|
+
Repository = "https://github.com/PinnLabs/fastapi-health-check"
|
|
19
|
+
Issues = "https://github.com/PinnLabs/fastapi-health-check/issues"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = [
|
|
23
|
+
"httpx>=0.28.1",
|
|
24
|
+
"mypy>=1.20.0",
|
|
25
|
+
"pytest>=9.0.3",
|
|
26
|
+
"ruff>=0.15.9",
|
|
27
|
+
"uvicorn>=0.44.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
pythonpath = ["src"]
|
|
32
|
+
testpaths = ["tests"]
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["src/fastapi_health_check"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from fastapi import FastAPI
|
|
2
|
+
from fastapi_health_check import (
|
|
3
|
+
AppAliveCheck,
|
|
4
|
+
HealthRegistry,
|
|
5
|
+
health_check,
|
|
6
|
+
install_health_check,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
app = FastAPI()
|
|
11
|
+
registry = HealthRegistry(
|
|
12
|
+
[
|
|
13
|
+
AppAliveCheck(),
|
|
14
|
+
health_check("database", lambda: "connection ok"),
|
|
15
|
+
health_check("redis", lambda: "cache reachable"),
|
|
16
|
+
]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
install_health_check(app, registry)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from fastapi_health_check.checks import AppAliveCheck, FunctionHealthCheck, HealthCheck, health_check
|
|
2
|
+
from fastapi_health_check.integration.fastapi import install_health_check
|
|
3
|
+
from fastapi_health_check.models import HealthCheckResult, HealthReport
|
|
4
|
+
from fastapi_health_check.registry import HealthRegistry
|
|
5
|
+
from fastapi_health_check.ui import render_health_report_page
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AppAliveCheck",
|
|
9
|
+
"FunctionHealthCheck",
|
|
10
|
+
"HealthCheck",
|
|
11
|
+
"HealthCheckResult",
|
|
12
|
+
"HealthRegistry",
|
|
13
|
+
"HealthReport",
|
|
14
|
+
"health_check",
|
|
15
|
+
"install_health_check",
|
|
16
|
+
"render_health_report_page",
|
|
17
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Static assets for the health check UI."""
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light;
|
|
3
|
+
--bg: #f4f8fb;
|
|
4
|
+
--panel: #ffffff;
|
|
5
|
+
--panel-border: #d7e4e9;
|
|
6
|
+
--text: #1d2733;
|
|
7
|
+
--muted: #5b6975;
|
|
8
|
+
--ok: #009688;
|
|
9
|
+
--ok-soft: #d8f2ee;
|
|
10
|
+
--fail: #e15656;
|
|
11
|
+
--fail-soft: #ffe2e2;
|
|
12
|
+
--accent: #0b7fab;
|
|
13
|
+
--accent-soft: rgba(11, 127, 171, 0.14);
|
|
14
|
+
--shadow: 0 22px 50px rgba(15, 23, 42, 0.08);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
* {
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
body {
|
|
22
|
+
margin: 0;
|
|
23
|
+
min-height: 100vh;
|
|
24
|
+
font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
25
|
+
color: var(--text);
|
|
26
|
+
background:
|
|
27
|
+
radial-gradient(circle at top left, var(--accent-soft), transparent 30%),
|
|
28
|
+
linear-gradient(180deg, #ffffff 0%, var(--bg) 100%);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.page {
|
|
32
|
+
width: min(960px, calc(100% - 32px));
|
|
33
|
+
margin: 0 auto;
|
|
34
|
+
padding: 56px 0 72px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.hero {
|
|
38
|
+
display: grid;
|
|
39
|
+
gap: 18px;
|
|
40
|
+
margin-bottom: 28px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.eyebrow {
|
|
44
|
+
margin: 0;
|
|
45
|
+
color: var(--accent);
|
|
46
|
+
font-size: 0.9rem;
|
|
47
|
+
font-weight: 700;
|
|
48
|
+
letter-spacing: 0.08em;
|
|
49
|
+
text-transform: uppercase;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
h1 {
|
|
53
|
+
margin: 0;
|
|
54
|
+
font-size: clamp(2rem, 4vw, 3.35rem);
|
|
55
|
+
line-height: 1.02;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.subtitle {
|
|
59
|
+
margin: 0;
|
|
60
|
+
max-width: 42rem;
|
|
61
|
+
color: var(--muted);
|
|
62
|
+
font-size: 1rem;
|
|
63
|
+
line-height: 1.6;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.summary {
|
|
67
|
+
display: grid;
|
|
68
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
69
|
+
gap: 16px;
|
|
70
|
+
margin-bottom: 24px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.summary-card,
|
|
74
|
+
.check-card {
|
|
75
|
+
background: var(--panel);
|
|
76
|
+
border: 1px solid var(--panel-border);
|
|
77
|
+
border-radius: 20px;
|
|
78
|
+
box-shadow: var(--shadow);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.summary-card {
|
|
82
|
+
padding: 22px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.summary-label {
|
|
86
|
+
margin: 0 0 10px;
|
|
87
|
+
color: var(--muted);
|
|
88
|
+
font-size: 0.9rem;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
letter-spacing: 0.06em;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.summary-value {
|
|
94
|
+
margin: 0;
|
|
95
|
+
font-size: 1.8rem;
|
|
96
|
+
font-weight: 700;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.badge {
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 8px;
|
|
103
|
+
width: fit-content;
|
|
104
|
+
padding: 8px 12px;
|
|
105
|
+
border-radius: 999px;
|
|
106
|
+
font-weight: 700;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.badge.ok {
|
|
110
|
+
color: var(--ok);
|
|
111
|
+
background: var(--ok-soft);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.badge.fail {
|
|
115
|
+
color: var(--fail);
|
|
116
|
+
background: var(--fail-soft);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.checks {
|
|
120
|
+
display: grid;
|
|
121
|
+
gap: 16px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.check-card {
|
|
125
|
+
padding: 20px 22px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.check-header {
|
|
129
|
+
display: flex;
|
|
130
|
+
flex-wrap: wrap;
|
|
131
|
+
justify-content: space-between;
|
|
132
|
+
gap: 12px;
|
|
133
|
+
margin-bottom: 12px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.check-name {
|
|
137
|
+
margin: 0;
|
|
138
|
+
font-size: 1.1rem;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.check-meta {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-wrap: wrap;
|
|
144
|
+
gap: 10px;
|
|
145
|
+
align-items: center;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.duration {
|
|
149
|
+
color: var(--muted);
|
|
150
|
+
font-variant-numeric: tabular-nums;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.message {
|
|
154
|
+
margin: 0;
|
|
155
|
+
color: var(--muted);
|
|
156
|
+
line-height: 1.6;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.empty {
|
|
160
|
+
padding: 28px;
|
|
161
|
+
text-align: center;
|
|
162
|
+
color: var(--muted);
|
|
163
|
+
background: rgba(255, 255, 255, 0.72);
|
|
164
|
+
border: 1px dashed var(--panel-border);
|
|
165
|
+
border-radius: 20px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@media (max-width: 640px) {
|
|
169
|
+
.page {
|
|
170
|
+
width: min(100% - 24px, 960px);
|
|
171
|
+
padding-top: 32px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.summary-card,
|
|
175
|
+
.check-card {
|
|
176
|
+
border-radius: 16px;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{ title }}</title>
|
|
7
|
+
<style>
|
|
8
|
+
{{ styles }}
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<main class="page">
|
|
13
|
+
<section class="hero">
|
|
14
|
+
<p class="eyebrow">FastAPI Health Check</p>
|
|
15
|
+
<h1>{{ title }}</h1>
|
|
16
|
+
<p class="subtitle">A compact operational view of backend dependencies, designed for quick status inspection.</p>
|
|
17
|
+
</section>
|
|
18
|
+
<section class="summary">
|
|
19
|
+
<article class="summary-card">
|
|
20
|
+
<p class="summary-label">Overall status</p>
|
|
21
|
+
<div class="badge {{ summary_class }}">{{ summary_label }}</div>
|
|
22
|
+
</article>
|
|
23
|
+
<article class="summary-card">
|
|
24
|
+
<p class="summary-label">Checks</p>
|
|
25
|
+
<p class="summary-value">{{ checks_count }}</p>
|
|
26
|
+
</article>
|
|
27
|
+
</section>
|
|
28
|
+
<section class="checks">
|
|
29
|
+
{{ checks_markup }}
|
|
30
|
+
</section>
|
|
31
|
+
</main>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
from inspect import isawaitable
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from time import perf_counter
|
|
7
|
+
|
|
8
|
+
from fastapi_health_check.models import HealthCheckResult
|
|
9
|
+
|
|
10
|
+
CheckHandler = Callable[[], str | None | Awaitable[str | None]]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HealthCheck(ABC):
|
|
14
|
+
default_name = ""
|
|
15
|
+
|
|
16
|
+
def __init__(self, name: str | None = None) -> None:
|
|
17
|
+
resolved_name = name or self.default_name or self.__class__.__name__.removesuffix("Check").lower()
|
|
18
|
+
if not resolved_name:
|
|
19
|
+
msg = "health checks must define a name"
|
|
20
|
+
raise ValueError(msg)
|
|
21
|
+
|
|
22
|
+
self.name = resolved_name
|
|
23
|
+
|
|
24
|
+
async def run(self) -> HealthCheckResult:
|
|
25
|
+
started_at = perf_counter()
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
message = await self.check()
|
|
29
|
+
except Exception as exc:
|
|
30
|
+
return HealthCheckResult(
|
|
31
|
+
name=self.name,
|
|
32
|
+
status="fail",
|
|
33
|
+
message=str(exc),
|
|
34
|
+
duration_ms=round((perf_counter() - started_at) * 1000, 3),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return HealthCheckResult(
|
|
38
|
+
name=self.name,
|
|
39
|
+
status="ok",
|
|
40
|
+
message=message,
|
|
41
|
+
duration_ms=round((perf_counter() - started_at) * 1000, 3),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
async def check(self) -> str | None:
|
|
46
|
+
"""Execute the health check and return an optional success message."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AppAliveCheck(HealthCheck):
|
|
50
|
+
default_name = "app_alive"
|
|
51
|
+
|
|
52
|
+
async def check(self) -> str | None:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class FunctionHealthCheck(HealthCheck):
|
|
57
|
+
def __init__(self, name: str, handler: CheckHandler) -> None:
|
|
58
|
+
super().__init__(name=name)
|
|
59
|
+
self._handler = handler
|
|
60
|
+
|
|
61
|
+
async def check(self) -> str | None:
|
|
62
|
+
result = self._handler()
|
|
63
|
+
if isawaitable(result):
|
|
64
|
+
return await result
|
|
65
|
+
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def health_check(name: str, handler: CheckHandler) -> FunctionHealthCheck:
|
|
70
|
+
return FunctionHealthCheck(name=name, handler=handler)
|