399 lines
15 KiB
Python
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)
|