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)