decomposer: generate deliverable files for Define the service contract and project architecture for the FastAPI endpoint monitoring service.; Implement the typed monitor CRUD API and concurrency-safe in-memory state according to the service design.; Implement secure on-demand endpoint checks with status updates, latency measurement, robust error handling, and redacted structured logs.; Add operational API endpoints and environment-driven runtime configuration to the monitoring service.; Create automated tests for the monitoring service.; Package the service with Docker and developer documentation.; Validate the complete project.
Some checks failed
ci / quality (push) Has been cancelled
ci / container (push) Has been cancelled

This commit is contained in:
2026-08-09 16:07:02 +00:00
parent 1b631b35a6
commit dcecec8e7a

View File

@@ -1,65 +1,57 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from enum import Enum from enum import Enum
from typing import Annotated
from uuid import UUID from uuid import UUID
from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, StringConstraints, field_validator from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, field_validator
Name = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1, max_length=100)]
def utcnow() -> datetime: def utc_now() -> datetime:
return datetime.now(timezone.utc) return datetime.now(timezone.utc)
class MonitorStatus(str, Enum): class MonitorState(str, Enum):
unknown = "unknown" UNKNOWN = "unknown"
up = "up" UP = "up"
down = "down" DOWN = "down"
error = "error" ERROR = "error"
class MonitorCreate(BaseModel): class CurrentStatus(BaseModel):
name: Name state: MonitorState = MonitorState.UNKNOWN
url: AnyHttpUrl checked_at: datetime | None = None
latency_ms: float | None = Field(default=None, ge=0)
http_status: int | None = Field(default=None, ge=100, le=599)
error: str | None = Field(default=None, max_length=500)
@field_validator("url")
class MonitorFields(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
name: str = Field(min_length=1, max_length=100)
target_url: AnyHttpUrl
@field_validator("target_url")
@classmethod @classmethod
def no_credentials(cls, value: AnyHttpUrl) -> AnyHttpUrl: def reject_credentials(cls, value: AnyHttpUrl) -> AnyHttpUrl:
if value.username or value.password: if value.username is not None or value.password is not None:
raise ValueError("URL credentials are not allowed") raise ValueError("URL credentials are not allowed")
return value return value
class MonitorUpdate(MonitorCreate): class MonitorCreate(MonitorFields):
pass pass
class Monitor(BaseModel): class MonitorUpdate(MonitorFields):
model_config = ConfigDict(frozen=True) pass
class Monitor(MonitorFields):
id: UUID id: UUID
name: str
url: AnyHttpUrl
status: MonitorStatus = MonitorStatus.unknown
created_at: datetime created_at: datetime
updated_at: datetime updated_at: datetime
checked_at: datetime | None = None current_status: CurrentStatus
latency_ms: float | None = Field(default=None, ge=0)
http_status: int | None = Field(default=None, ge=100, le=599)
error: str | None = None
class CheckResult(BaseModel): class Health(BaseModel):
status: MonitorStatus
checked_at: datetime
latency_ms: float = Field(ge=0)
http_status: int | None = None
error: str | None = None
class StatusResponse(CheckResult):
id: UUID
class ProbeResponse(BaseModel):
status: str status: str