Files
Unraid-AppOps-Gateway-Public/backend/app/db/models.py
T

399 lines
15 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Any
from sqlalchemy import JSON, CheckConstraint
from sqlmodel import Column, Field, SQLModel
from ..core.time import utc_now
class App(SQLModel, table=True):
id: str = Field(primary_key=True)
name: str
category: str
status: str = Field(default="healthy")
criticality: str = Field(default="low")
connector_status: str = Field(default="disconnected")
container_status: str = Field(default="stopped")
voice_enabled: bool = Field(default=False)
protected: bool = Field(default=False)
last_action: str | None = None
discovery_status: str | None = None
discovery_source: str | None = None
needs_review: bool = Field(default=False)
created_at: datetime = Field(default_factory=utc_now)
class Connector(SQLModel, table=True):
id: str = Field(primary_key=True)
name: str
status: str = Field(default="disconnected")
last_test: str = Field(default="never")
secret_ref: str | None = None
capabilities: list[str] = Field(default_factory=list, sa_column=Column(JSON))
type: str = Field(default="Infrastructure")
class NotificationRule(SQLModel, table=True):
__tablename__ = "notification_rules"
id: str = Field(primary_key=True)
name: str
enabled: bool = Field(default=True)
source: str
event_type: str
severity_min: str = Field(default="medium")
categories_json: list[str] = Field(default_factory=list, sa_column=Column(JSON))
targets_json: list[str] = Field(default_factory=list, sa_column=Column(JSON))
channels_json: list[str] = Field(default_factory=lambda: ["dashboard"], sa_column=Column(JSON))
cooldown_seconds: int = Field(default=3600)
quiet_hours_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
conditions_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
dedupe_key_template: str = Field(default="{source}:{event_type}:{target}:{category}")
created_at: datetime = Field(default_factory=utc_now)
updated_at: datetime = Field(default_factory=utc_now)
class NotificationEvent(SQLModel, table=True):
__tablename__ = "notification_events"
id: str = Field(primary_key=True)
rule_id: str
source: str
event_type: str
severity: str
title: str
message: str
category: str | None = None
target: str | None = None
dedupe_key: str
channels_json: list[str] = Field(default_factory=list, sa_column=Column(JSON))
status: str = Field(default="queued")
reason: str | None = None
payload_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
read: bool = Field(default=False)
created_at: datetime = Field(default_factory=utc_now)
sent_at: datetime | None = None
suppressed_until: datetime | None = None
class NotificationChannel(SQLModel, table=True):
__tablename__ = "notification_channels"
id: str = Field(primary_key=True)
type: str = Field(default="dashboard")
name: str
enabled: bool = Field(default=True)
config_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
secret_refs_json: list[str] = Field(default_factory=list, sa_column=Column(JSON))
last_test_at: datetime | None = None
created_at: datetime = Field(default_factory=utc_now)
updated_at: datetime = Field(default_factory=utc_now)
class AppSetting(SQLModel, table=True):
__tablename__ = "app_settings"
key: str = Field(primary_key=True)
value_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
is_secret: bool = Field(default=False)
updated_at: datetime = Field(default_factory=utc_now)
class Secret(SQLModel, table=True):
__tablename__ = "secrets"
id: str = Field(primary_key=True)
name: str = Field(index=True)
encrypted_value: str
created_at: datetime = Field(default_factory=utc_now)
updated_at: datetime = Field(default_factory=utc_now)
last_used_at: datetime | None = None
used_by_json: list[str] = Field(default_factory=list, sa_column=Column(JSON))
metadata_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
class Policy(SQLModel, table=True):
id: str = Field(primary_key=True)
action: str
target: str
voice: str
manual: str
risk: str
reason: str | None = None
provenance_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
class AuditEvent(SQLModel, table=True):
id: str = Field(primary_key=True)
timestamp: datetime = Field(default_factory=utc_now)
source: str
identity: str = Field(default="manual")
action_id: str
target: str
target_type: str = Field(default="app")
risk: str
policy_decision: str
reason: str | None = None
execution_plan: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
result: str = Field(default="pending")
before_state: dict[str, Any] | None = Field(default=None, sa_column=Column(JSON))
after_state: dict[str, Any] | None = Field(default=None, sa_column=Column(JSON))
post_checks: dict[str, Any] | None = Field(default=None, sa_column=Column(JSON))
redacted_metadata: dict[str, Any] | None = Field(default=None, sa_column=Column(JSON))
class Approval(SQLModel, table=True):
id: str = Field(primary_key=True)
action: str
target: str
source: str = Field(default="manual")
risk: str
status: str = Field(default="pending")
requester: str = Field(default="system")
approved_by: str | None = None
executed_by: str | None = None
action_fingerprint: str | None = None
request_context_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
reason: str | None = None
expires_at: datetime | None = None
created_at: datetime = Field(default_factory=utc_now)
decided_at: datetime | None = None
executed_at: datetime | None = None
dismissed_at: datetime | None = None
job_id: str | None = None
audit_event_id: str | None = None
class JobRun(SQLModel, table=True):
id: str = Field(primary_key=True)
type: str
status: str = Field(default="queued")
source: str = Field(default="manual")
action_id: str
target: str
risk: str
policy_decision: str
steps_json: str = Field(default="[]")
progress: int = Field(default=0)
started_at: datetime | None = Field(default=None)
finished_at: datetime | None = Field(default=None)
duration_ms: int | None = None
audit_event_id: str | None = None
result_summary: str | None = None
class DeploymentJob(SQLModel, table=True):
"""Durable, redacted state for one Deployment Control Plane request.
The plan is immutable and excludes secret values. Secrets remain in the
encrypted Secret store and are resolved only at the bounded controller
request boundary.
"""
__tablename__ = "deployment_jobs"
id: str = Field(primary_key=True)
application_id: str = Field(index=True)
repository: str = Field(index=True)
revision: str = Field(index=True)
plan_sha256: str = Field(index=True)
idempotency_sha256: str = Field(index=True)
status: str = Field(default="queued", index=True)
stage: str = Field(default="accepted")
plan_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
preflight_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
evidence_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
broker_request_id: str | None = Field(default=None, index=True)
result_summary: str | None = None
created_at: datetime = Field(default_factory=utc_now, index=True)
updated_at: datetime = Field(default_factory=utc_now)
completed_at: datetime | None = None
class WorkMeshPromotion(SQLModel, table=True):
"""Durable approval and reconciliation state for fixed WorkMesh promotion."""
__tablename__ = "workmesh_promotions"
id: str = Field(primary_key=True)
revision: str = Field(index=True)
preflight_sha256: str = Field(index=True)
idempotency_sha256: str = Field(index=True, unique=True)
status: str = Field(default="approval_required", index=True)
stage: str = Field(default="preflight_accepted")
preflight_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
evidence_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
approval_id: str | None = Field(default=None, index=True)
broker_request_id: str | None = Field(default=None, index=True)
result_summary: str | None = None
audit_event_id: str | None = None
created_at: datetime = Field(default_factory=utc_now, index=True)
updated_at: datetime = Field(default_factory=utc_now)
completed_at: datetime | None = None
class Enrollment(SQLModel, table=True):
"""One immutable application-management boundary established by AppOps."""
__tablename__ = "enrollments"
id: str = Field(primary_key=True)
application_id: str = Field(index=True)
repository: str = Field(index=True)
revision: str = Field(index=True)
profile: str = Field(index=True)
controller_identity: str = Field(index=True)
plan_sha256: str = Field(index=True)
status: str = Field(default="planned", index=True)
target_secret_ref: str = Field(index=True)
oidc_secret_ref: str | None = Field(default=None, index=True)
plan_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
resources_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
provenance_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
last_verified_at: datetime | None = None
created_at: datetime = Field(default_factory=utc_now, index=True)
updated_at: datetime = Field(default_factory=utc_now)
completed_at: datetime | None = None
class EnrollmentJob(SQLModel, table=True):
"""Restart-safe, redacted evidence for enrollment orchestration."""
__tablename__ = "enrollment_jobs"
id: str = Field(primary_key=True)
enrollment_id: str = Field(index=True)
application_id: str = Field(index=True)
operation: str = Field(default="enroll", index=True)
idempotency_sha256: str = Field(index=True)
status: str = Field(default="queued", index=True)
stage: str = Field(default="planned")
preflight_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
evidence_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
result_summary: str | None = None
created_at: datetime = Field(default_factory=utc_now, index=True)
updated_at: datetime = Field(default_factory=utc_now)
completed_at: datetime | None = None
class RepoPulseAdoptionJournal(SQLModel, table=True):
"""One durable protocol intent for the fixed RepoPulse legacy adoption."""
__tablename__ = "repopulse_adoption_journal"
__table_args__ = (
CheckConstraint(
"slot = 'repopulse' AND repository = 'jens/repopulse' "
"AND application_id = 'repopulse'",
name="ck_repopulse_adoption_fixed_identity",
),
)
slot: str = Field(default="repopulse", primary_key=True)
repository: str
application_id: str
intent_id: str = Field(index=True)
idempotency_sha256: str
preflight_sha256: str
phase: str
status: str
revision: int = Field(default=1, ge=1)
intent_json: dict[str, Any] = Field(sa_column=Column(JSON, nullable=False))
created_at: datetime = Field(default_factory=utc_now)
updated_at: datetime = Field(default_factory=utc_now)
class HealthCheck(SQLModel, table=True):
id: str = Field(primary_key=True)
service: str
status: str
checked_at: datetime = Field(default_factory=utc_now)
details: str | None = None
class AiMemoryItem(SQLModel, table=True):
id: str = Field(primary_key=True)
key: str
value: str
category: str = Field(default="app_note")
pinned: bool = Field(default=False)
updated_at: datetime = Field(default_factory=utc_now)
class AiRecommendation(SQLModel, table=True):
id: str = Field(primary_key=True)
action: str
title: str
reasoning: str
target: str
severity: str
suggested_actions: list[str] = Field(default_factory=list, sa_column=Column(JSON))
class KnowledgeChunk(SQLModel, table=True):
__tablename__ = "knowledge_chunks"
id: str = Field(primary_key=True)
source_uri: str = Field(index=True)
source_type: str = Field(default="documentation", index=True)
title: str
section: str | None = None
content: str
content_hash: str = Field(index=True)
updated_at: datetime = Field(default_factory=utc_now)
class DiscoveryRun(SQLModel, table=True):
__tablename__ = "discovery_runs"
id: str = Field(primary_key=True)
source: str = Field(default="portainer")
started_at: datetime = Field(default_factory=utc_now, index=True)
finished_at: datetime | None = None
status: str = Field(default="running")
containers_found: int = Field(default=0)
apps_matched: int = Field(default=0)
new_items: int = Field(default=0)
changed_items: int = Field(default=0)
errors_json: list[dict[str, Any]] = Field(default_factory=list, sa_column=Column(JSON))
class DiscoveredContainer(SQLModel, table=True):
__tablename__ = "discovered_containers"
id: str = Field(primary_key=True)
discovery_run_id: str = Field(index=True)
container_id: str = Field(index=True)
name: str
image: str
state: str = Field(default="unknown")
status: str = Field(default="unknown")
ports_json: list[Any] = Field(default_factory=list, sa_column=Column(JSON))
mounts_json: list[Any] = Field(default_factory=list, sa_column=Column(JSON))
networks_json: list[Any] = Field(default_factory=list, sa_column=Column(JSON))
labels_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
suggested_app_type: str = Field(default="unknown")
suggested_category: str = Field(default="unknown")
suggested_criticality: str = Field(default="low")
suggested_connector: str = Field(default="none")
suggested_policy_profile: str = Field(default="default")
import_status: str = Field(default="discovered")
matched_app_id: str | None = None
raw_metadata_json: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
class AppContainerMapping(SQLModel, table=True):
__tablename__ = "app_container_mappings"
id: str = Field(primary_key=True)
app_id: str = Field(index=True)
container_id: str = Field(index=True)
container_name: str
role: str = Field(default="primary")
source: str = Field(default="discovery")
is_primary: bool = Field(default=True)
last_seen_run_id: str | None = None
last_seen_at: datetime = Field(default_factory=utc_now)