🚧 WIP
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
"""Pydantic request/response schemas shared across routers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field, StringConstraints
|
||||
|
||||
from app.models import ManagementRole
|
||||
|
||||
UsernameStr = Annotated[str, StringConstraints(pattern=r"^[a-zA-Z0-9_-]{1,100}$")]
|
||||
TopicFilterStr = Annotated[str, StringConstraints(min_length=1, max_length=200)]
|
||||
|
||||
ACL_TYPES = Literal[
|
||||
"publishClientSend",
|
||||
"publishClientReceive",
|
||||
"subscribeLiteral",
|
||||
"subscribePattern",
|
||||
"unsubscribeLiteral",
|
||||
"unsubscribePattern",
|
||||
]
|
||||
|
||||
|
||||
# --- Auth ------------------------------------------------------------------
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class SessionInfo(BaseModel):
|
||||
username: str
|
||||
role: ManagementRole
|
||||
must_change_password: bool
|
||||
|
||||
|
||||
class PasswordChangeRequest(BaseModel):
|
||||
current_password: str
|
||||
new_password: str = Field(min_length=12)
|
||||
|
||||
|
||||
# --- Management users -------------------------------------------------------
|
||||
|
||||
|
||||
class ManagementUserOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
username: str
|
||||
display_name: str | None
|
||||
role: ManagementRole
|
||||
enabled: bool
|
||||
must_change_password: bool
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class ManagementUserCreate(BaseModel):
|
||||
username: UsernameStr
|
||||
password: str = Field(min_length=12)
|
||||
display_name: str | None = None
|
||||
role: ManagementRole = ManagementRole.OPERATOR
|
||||
|
||||
|
||||
class ManagementUserUpdate(BaseModel):
|
||||
display_name: str | None = None
|
||||
role: ManagementRole | None = None
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
# --- MQTT clients ------------------------------------------------------------
|
||||
|
||||
|
||||
class AclRuleIn(BaseModel):
|
||||
"""One ACL rule as edited in the UI (spec section 14)."""
|
||||
|
||||
acltype: ACL_TYPES
|
||||
topic: TopicFilterStr
|
||||
allow: bool = True
|
||||
priority: int = Field(default=0, ge=0, le=100000)
|
||||
|
||||
|
||||
class RoleIn(BaseModel):
|
||||
rolename: UsernameStr
|
||||
text_name: str | None = None
|
||||
text_description: str | None = None
|
||||
acls: list[AclRuleIn] = Field(default_factory=list)
|
||||
|
||||
|
||||
class RoleUpdate(BaseModel):
|
||||
"""PUT /roles/{name} payload: rolename comes from the URL path."""
|
||||
|
||||
acls: list[AclRuleIn]
|
||||
|
||||
|
||||
class GroupRoleIn(BaseModel):
|
||||
rolename: str
|
||||
priority: int = 1
|
||||
|
||||
|
||||
class GroupIn(BaseModel):
|
||||
groupname: UsernameStr
|
||||
text_name: str | None = None
|
||||
text_description: str | None = None
|
||||
roles: list[GroupRoleIn] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ClientRoleIn(BaseModel):
|
||||
rolename: str
|
||||
priority: int = 1
|
||||
|
||||
|
||||
class ClientGroupIn(BaseModel):
|
||||
groupname: str
|
||||
priority: int = 1
|
||||
|
||||
|
||||
class MqttClientCreate(BaseModel):
|
||||
username: UsernameStr
|
||||
password: str | None = Field(
|
||||
default=None,
|
||||
description="If omitted a random password is generated and returned once.",
|
||||
)
|
||||
clientid: str | None = None
|
||||
text_name: str | None = None
|
||||
text_description: str | None = None
|
||||
groups: list[ClientGroupIn] = Field(default_factory=list)
|
||||
roles: list[ClientRoleIn] = Field(default_factory=list)
|
||||
# PostgreSQL-side metadata (spec section 11)
|
||||
owner: str | None = None
|
||||
purpose: str | None = None
|
||||
tags: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class MqttClientOut(BaseModel):
|
||||
username: str
|
||||
clientid: str | None
|
||||
text_name: str | None
|
||||
text_description: str | None
|
||||
enabled: bool
|
||||
groups: list[dict[str, Any]]
|
||||
roles: list[dict[str, Any]]
|
||||
owner: str | None
|
||||
purpose: str | None
|
||||
tags: list[str]
|
||||
last_known_connection: datetime | None
|
||||
|
||||
|
||||
class MqttPasswordReset(BaseModel):
|
||||
"""Response for create/reset — plaintext shown exactly once (spec 11)."""
|
||||
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
# --- Broker configuration ----------------------------------------------------
|
||||
|
||||
|
||||
class BrokerSettingsIn(BaseModel):
|
||||
key: str = Field(min_length=1, max_length=100)
|
||||
value: str
|
||||
value_type: Literal["string", "int", "bool"] = "string"
|
||||
|
||||
|
||||
class ListenerIn(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=100)
|
||||
enabled: bool = True
|
||||
port: int = Field(ge=1, le=65535)
|
||||
bind_address: str | None = None
|
||||
protocol: Literal["mqtt", "websockets"] = "mqtt"
|
||||
allow_anonymous: bool = False
|
||||
max_connections: int = -1
|
||||
tls_enabled: bool = False
|
||||
tls_min_version: Literal["tlsv1.2", "tlsv1.3"] | None = None
|
||||
require_certificate: bool = False
|
||||
use_identity_as_username: bool = False
|
||||
|
||||
|
||||
class ListenerOut(ListenerIn):
|
||||
id: uuid.UUID
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class ConfigPreviewOut(BaseModel):
|
||||
config_text: str
|
||||
warnings: list[str]
|
||||
|
||||
|
||||
class DeployRequest(BaseModel):
|
||||
comment: str | None = None
|
||||
|
||||
|
||||
class RevisionOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
revision_number: int
|
||||
created_by: str | None
|
||||
comment: str | None
|
||||
deployed: bool
|
||||
deployed_at: datetime | None
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class RevisionDetailOut(RevisionOut):
|
||||
config_text: str
|
||||
validation_result: dict[str, Any]
|
||||
Reference in New Issue
Block a user