diff --git a/README.md b/README.md index a32d432..8c55a34 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # linkpulse -[![wakatime](https://wakatime.com/badge/github/Xevion/linkpulse.svg)](https://wakatime.com/badge/github/Xevion/linkpulse) +![Website Status](https://img.shields.io/website?url=https%3A%2F%2Flinkpulse.xevion.dev&up_message=online&down_message=down&label=linkpulse) ![Python Version](https://img.shields.io/badge/python-3.12-blue) ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Flinkpulse.xevion%2Fdev%2Fapi%2Fversion&query=%24.version&label=version) [![wakatime](https://wakatime.com/badge/github/Xevion/linkpulse.svg)](https://wakatime.com/badge/github/Xevion/linkpulse) A project for monitoring websites, built with FastAPI and React. diff --git a/backend/linkpulse/routers/misc.py b/backend/linkpulse/routers/misc.py index 57b1877..b953064 100644 --- a/backend/linkpulse/routers/misc.py +++ b/backend/linkpulse/routers/misc.py @@ -1,16 +1,42 @@ """Miscellaneous endpoints for the Linkpulse API.""" +from pathlib import Path from typing import Any +import structlog +import toml from fastapi import APIRouter from fastapi_cache.decorator import cache from linkpulse.utilities import get_db +logger = structlog.get_logger(__name__) + router = APIRouter() db = get_db() +@router.get("/api/version") +@cache(expire=None) +async def version() -> dict[str, str]: + """Get the version of the API. + :return: The version of the API. + :rtype: dict[str, str] + """ + + pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml" + + version = "unknown" + if pyproject_path.exists() and pyproject_path.is_file(): + data = toml.load(pyproject_path) + version = data["tool"]["poetry"]["version"] + logger.debug("Version loaded from pyproject.toml", version=version) + else: + version = "error" + + return {"version": version} + + @router.get("/health") async def health(): """An endpoint to check if the service is running. diff --git a/backend/linkpulse/tests/test_utilities.py b/backend/linkpulse/tests/test_utilities.py index e6d00c0..e620e40 100644 --- a/backend/linkpulse/tests/test_utilities.py +++ b/backend/linkpulse/tests/test_utilities.py @@ -1,6 +1,21 @@ +import re from linkpulse.utilities import utc_now +from fastapi.testclient import TestClient + +from linkpulse.app import app def test_utcnow_tz_aware(): dt = utc_now() dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None + + +def test_api_version(): + with TestClient(app) as client: + response = client.get("/api/version") + assert response.status_code == 200 + assert "version" in response.json() + + version = response.json()["version"] + assert isinstance(version, str) + assert re.match(r"^\d+\.\d+\.\d+$", version) diff --git a/backend/poetry.lock b/backend/poetry.lock index d5cc6ed..d4a3f03 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1626,6 +1626,17 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] typing = ["mypy (>=1.4)", "rich", "twisted"] +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + [[package]] name = "types-peewee" version = "3.17.7.20241017" @@ -1659,6 +1670,17 @@ files = [ {file = "types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7"}, ] +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +description = "Typing stubs for toml" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, + {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -1840,4 +1862,4 @@ h11 = ">=0.9.0,<1" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "9dff2a47bb95e65f15616bb82926eb81d418456c5ec60db46dfe06056c643e31" +content-hash = "4e094b6d46fab5f7886452b22c30fc520d396c6d77f9f92dfbd4ef36257c10df" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index b3d723e..495e606 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -5,7 +5,7 @@ description = "" authors = ["Xevion "] license = "GNU GPL v3" readme = "README.md" -package-mode = false +package-mode = true [tool.poetry.scripts] app = "linkpulse" @@ -32,6 +32,8 @@ pwdlib = {extras = ["argon2"], version = "^0.2.1"} pytest-xdist = "^3.6.1" email-validator = "^2.2.0" limits = "^3.13.0" +toml = "^0.10.2" +types-toml = "^0.10.8.20240310" [tool.poetry.group.dev.dependencies]