Enforce http(s) schema on service base_url fields

Add a shared ServiceBaseUrl type (BeforeValidator + Field description) in
integrations/base.py and apply it to base_url across all six service configs
(grafana, prometheus, alertmanager, jellyfin, jellyseerr, nextcloud). Missing
http:// or https:// schema now fails fast with a clear 422 instead of breaking
HTTP clients silently. Tests cover reject/accept cases; REQUIREMENTS updated.
This commit is contained in:
Developer
2026-06-26 09:52:20 +00:00
parent 56b919ea1f
commit eebc86a52b
9 changed files with 71 additions and 9 deletions
@@ -16,9 +16,37 @@ map. There is no runtime plugin loading.
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from typing import Annotated, Any
from pydantic import BaseModel
from pydantic import BaseModel, BeforeValidator, Field
def _validate_service_base_url(value: Any) -> str:
"""Require an absolute http(s) URL for service ``base_url`` fields.
Relative hosts (e.g. ``grafana.example.com``) break downstream HTTP clients
because ``requests`` treats them as relative paths, so we fail fast with a
clear error instead of letting the call silently malfunction.
"""
if not isinstance(value, str):
raise ValueError("base_url must be a string starting with http:// or https://")
text = value.strip()
if not text:
raise ValueError("base_url must not be empty")
lowered = text.lower()
if not (lowered.startswith("http://") or lowered.startswith("https://")):
raise ValueError("base_url must start with http:// or https:// (include the schema)")
return text
#: Shared annotated type for service ``base_url`` fields. applying the validator
#: uniformly across every integration so missing schemas are rejected at the
#: config boundary with a helpful message.
ServiceBaseUrl = Annotated[
str,
Field(description="Absolute URL including the http:// or https:// schema."),
BeforeValidator(_validate_service_base_url),
]
class ServiceConfigBase(BaseModel):
@@ -26,6 +54,9 @@ class ServiceConfigBase(BaseModel):
Subclass this in each integration module and declare the connection fields.
The JSON schema is derived via ``model_json_schema()`` and exposed to the UI.
Connection URLs should use the :data:`ServiceBaseUrl` type so the
``http(s)://`` schema is enforced consistently across integrations.
"""