Files

479 lines
29 KiB
Python

from datetime import datetime
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, JSON, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from .database import Base
class Category(Base):
__tablename__ = "categories"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(100), unique=True, index=True)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
image_path: Mapped[str | None] = mapped_column(String(255), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
assets: Mapped[list["Asset"]] = relationship(back_populates="category")
visible_fields: Mapped[list["CategoryField"]] = relationship(
back_populates="category", cascade="all, delete-orphan", order_by="CategoryField.sort_order"
)
class FieldDefinition(Base):
__tablename__ = "field_definitions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
field_name: Mapped[str] = mapped_column(String(80), unique=True, index=True)
label: Mapped[str] = mapped_column(String(120))
description: Mapped[str | None] = mapped_column(Text, nullable=True)
data_type: Mapped[str] = mapped_column(String(30), default="short_text")
readonly: Mapped[bool] = mapped_column(Boolean, default=False)
is_system: Mapped[bool] = mapped_column(Boolean, default=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
translation_key: Mapped[str | None] = mapped_column(String(180), nullable=True)
searchable: Mapped[bool] = mapped_column(Boolean, default=True)
filterable: Mapped[bool] = mapped_column(Boolean, default=True)
chart_enabled: Mapped[bool] = mapped_column(Boolean, default=True)
excel_import: Mapped[bool] = mapped_column(Boolean, default=True)
excel_export: Mapped[bool] = mapped_column(Boolean, default=True)
bulk_edit: Mapped[bool] = mapped_column(Boolean, default=True)
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
list_width: Mapped[int | None] = mapped_column(Integer, nullable=True)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
category_links: Mapped[list["CategoryField"]] = relationship(back_populates="definition", cascade="all, delete-orphan")
values: Mapped[list["AssetFieldValue"]] = relationship(back_populates="definition", cascade="all, delete-orphan")
class CategoryField(Base):
__tablename__ = "category_fields"
__table_args__ = (UniqueConstraint("category_id", "field_name"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
category_id: Mapped[int] = mapped_column(ForeignKey("categories.id", ondelete="CASCADE"))
field_name: Mapped[str] = mapped_column(String(80))
label: Mapped[str] = mapped_column(String(100))
field_definition_id: Mapped[int | None] = mapped_column(ForeignKey("field_definitions.id", ondelete="CASCADE"), nullable=True, index=True)
active: Mapped[bool] = mapped_column(Boolean, default=True)
required: Mapped[bool] = mapped_column(Boolean, default=False)
show_in_list: Mapped[bool] = mapped_column(Boolean, default=False)
readonly: Mapped[bool] = mapped_column(Boolean, default=False)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
category: Mapped[Category] = relationship(back_populates="visible_fields")
definition: Mapped[FieldDefinition | None] = relationship(back_populates="category_links")
@property
def effective_label(self) -> str:
return self.definition.label if self.definition else self.label
@property
def effective_readonly(self) -> bool:
return bool(self.definition.readonly) if self.definition else bool(getattr(self, "readonly", False))
class Asset(Base):
__tablename__ = "assets"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
category_id: Mapped[int] = mapped_column(ForeignKey("categories.id"), index=True)
asset_tag: Mapped[str | None] = mapped_column(String(100), unique=True, nullable=True, index=True)
name: Mapped[str] = mapped_column(String(150), index=True)
manufacturer: Mapped[str | None] = mapped_column(String(150), nullable=True)
model: Mapped[str | None] = mapped_column(String(150), nullable=True)
serial_number: Mapped[str | None] = mapped_column(String(150), nullable=True, index=True)
status: Mapped[str | None] = mapped_column(String(60), nullable=True)
assigned_to: Mapped[str | None] = mapped_column(String(150), nullable=True)
assigned_on: Mapped[str | None] = mapped_column(String(20), nullable=True)
location: Mapped[str | None] = mapped_column(String(150), nullable=True)
room_number: Mapped[str | None] = mapped_column(String(80), nullable=True, index=True)
parent_asset_id: Mapped[int | None] = mapped_column(ForeignKey("assets.id", ondelete="SET NULL"), nullable=True, index=True)
department: Mapped[str | None] = mapped_column(String(150), nullable=True)
purchase_date: Mapped[str | None] = mapped_column(String(20), nullable=True)
warranty_until: Mapped[str | None] = mapped_column(String(20), nullable=True)
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
image_path: Mapped[str | None] = mapped_column(String(255), nullable=True)
hostname: Mapped[str | None] = mapped_column(String(150), nullable=True, index=True)
operating_system: Mapped[str | None] = mapped_column(String(150), nullable=True)
os_patch_level: Mapped[str | None] = mapped_column(String(150), nullable=True)
mac_address: Mapped[str | None] = mapped_column(String(50), nullable=True)
ip_address: Mapped[str | None] = mapped_column(String(50), nullable=True)
cpu: Mapped[str | None] = mapped_column(String(200), nullable=True)
memory_gb: Mapped[str | None] = mapped_column(String(40), nullable=True)
storage_gb: Mapped[str | None] = mapped_column(String(40), nullable=True)
storage_details: Mapped[str | None] = mapped_column(Text, nullable=True)
gpu_name: Mapped[str | None] = mapped_column(Text, nullable=True)
tpm_version: Mapped[str | None] = mapped_column(String(50), nullable=True)
memory_details: Mapped[str | None] = mapped_column(Text, nullable=True)
antivirus: Mapped[str | None] = mapped_column(Text, nullable=True)
mesh_group: Mapped[str | None] = mapped_column(String(180), nullable=True)
mesh_mtype: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
mesh_node_id: Mapped[str | None] = mapped_column(String(180), unique=True, nullable=True, index=True)
mesh_last_sync: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
mesh_last_seen: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
mesh_online: Mapped[bool | None] = mapped_column(Boolean, nullable=True, index=True)
mesh_online_state: Mapped[str] = mapped_column(String(20), default="unknown", index=True)
mesh_presence_updated_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
mesh_last_online_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
mesh_presence_source: Mapped[str | None] = mapped_column(String(40), nullable=True)
mesh_sync_status: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True)
mesh_sync_message: Mapped[str | None] = mapped_column(Text, nullable=True)
mesh_conflicts: Mapped[dict] = mapped_column(JSON, default=dict)
mesh_source_data: Mapped[dict] = mapped_column(JSON, default=dict)
mesh_hardware_data: Mapped[dict] = mapped_column(JSON, default=dict)
mesh_software_data: Mapped[list | dict] = mapped_column(JSON, default=list)
mesh_inventory_updated_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
extra_data: Mapped[dict] = mapped_column(JSON, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
category: Mapped[Category] = relationship(back_populates="assets")
parent_asset: Mapped["Asset | None"] = relationship("Asset", remote_side="Asset.id", back_populates="child_assets", foreign_keys=[parent_asset_id])
child_assets: Mapped[list["Asset"]] = relationship("Asset", back_populates="parent_asset", foreign_keys=[parent_asset_id])
class AssetFieldValue(Base):
__tablename__ = "asset_field_values"
__table_args__ = (UniqueConstraint("asset_id", "field_definition_id"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
asset_id: Mapped[int] = mapped_column(ForeignKey("assets.id", ondelete="CASCADE"), index=True)
field_definition_id: Mapped[int] = mapped_column(ForeignKey("field_definitions.id", ondelete="CASCADE"), index=True)
value_text: Mapped[str | None] = mapped_column(Text, nullable=True)
value_number: Mapped[str | None] = mapped_column(String(100), nullable=True)
value_boolean: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
value_date: Mapped[str | None] = mapped_column(String(20), nullable=True)
value_datetime: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
value_json: Mapped[dict | list | None] = mapped_column(JSON, nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
asset: Mapped[Asset] = relationship()
definition: Mapped[FieldDefinition] = relationship(back_populates="values")
class MeshFieldMapping(Base):
__tablename__ = "mesh_field_mappings"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
source_path: Mapped[str] = mapped_column(String(255), index=True)
target_field_name: Mapped[str] = mapped_column(String(80), index=True)
transform: Mapped[str] = mapped_column(String(40), default="raw")
multi_value_mode: Mapped[str] = mapped_column(String(30), default="first")
separator: Mapped[str] = mapped_column(String(20), default="\n")
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
priority: Mapped[int] = mapped_column(Integer, default=0)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
update_rule: Mapped[str] = mapped_column(String(30), default="fill_empty")
is_system: Mapped[bool] = mapped_column(Boolean, default=False)
class AssetHistory(Base):
__tablename__ = "asset_history"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
asset_id: Mapped[int] = mapped_column(ForeignKey("assets.id", ondelete="CASCADE"), index=True)
changed_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
source: Mapped[str] = mapped_column(String(50), default="manual")
changed_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
changes: Mapped[dict] = mapped_column(JSON, default=dict)
class StatusOption(Base):
__tablename__ = "status_options"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(60), unique=True, index=True)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
active: Mapped[bool] = mapped_column(Boolean, default=True)
use_for_issue: Mapped[bool] = mapped_column(Boolean, default=False)
use_for_return: Mapped[bool] = mapped_column(Boolean, default=False)
class SyncRun(Base):
__tablename__ = "sync_runs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
source: Mapped[str] = mapped_column(String(50), default="meshcentral")
started_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
finished_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
status: Mapped[str] = mapped_column(String(30), default="running")
devices_found: Mapped[int] = mapped_column(Integer, default=0)
created_count: Mapped[int] = mapped_column(Integer, default=0)
updated_count: Mapped[int] = mapped_column(Integer, default=0)
conflict_count: Mapped[int] = mapped_column(Integer, default=0)
error_count: Mapped[int] = mapped_column(Integer, default=0)
message: Mapped[str | None] = mapped_column(Text, nullable=True)
class ChartDefinition(Base):
__tablename__ = "chart_definitions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(150), unique=True, index=True)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
chart_type: Mapped[str] = mapped_column(String(30), default="bar")
group_field: Mapped[str] = mapped_column(String(80), default="category")
stack_field: Mapped[str | None] = mapped_column(String(80), nullable=True)
dashboard_visible: Mapped[bool] = mapped_column(Boolean, default=False)
show_empty: Mapped[bool] = mapped_column(Boolean, default=False)
max_items: Mapped[int] = mapped_column(Integer, default=12)
sort_mode: Mapped[str] = mapped_column(String(30), default="count_desc")
value_mode: Mapped[str] = mapped_column(String(30), default="raw")
value_mappings: Mapped[list] = mapped_column(JSON, default=list)
excluded_statuses: Mapped[list] = mapped_column(JSON, default=list)
is_system: Mapped[bool] = mapped_column(Boolean, default=False)
created_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class Language(Base):
__tablename__ = "languages"
code: Mapped[str] = mapped_column(String(10), primary_key=True)
name: Mapped[str] = mapped_column(String(80))
native_name: Mapped[str] = mapped_column(String(80))
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
class Translation(Base):
__tablename__ = "translations"
__table_args__ = (UniqueConstraint("translation_key", "language_code"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
translation_key: Mapped[str] = mapped_column(String(180), index=True)
language_code: Mapped[str] = mapped_column(ForeignKey("languages.code", ondelete="CASCADE"), index=True)
text_value: Mapped[str] = mapped_column(Text)
description: Mapped[str | None] = mapped_column(String(255), nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
username: Mapped[str] = mapped_column(String(120), unique=True, index=True)
display_name: Mapped[str | None] = mapped_column(String(180), nullable=True)
email: Mapped[str | None] = mapped_column(String(180), nullable=True)
password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
auth_source: Mapped[str] = mapped_column(String(30), default="local")
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
is_admin: Mapped[bool] = mapped_column(Boolean, default=False)
is_protected: Mapped[bool] = mapped_column(Boolean, default=False)
allowed_mesh_groups: Mapped[list] = mapped_column(JSON, default=list)
asset_access_scopes: Mapped[list] = mapped_column(JSON, default=list)
profile_department: Mapped[str | None] = mapped_column(String(150), nullable=True)
profile_location: Mapped[str | None] = mapped_column(String(150), nullable=True)
language_code: Mapped[str] = mapped_column(String(10), default="en")
persist_browser_state: Mapped[bool] = mapped_column(Boolean, default=False)
toast_position: Mapped[str] = mapped_column(String(30), default="top-right")
toast_duration_seconds: Mapped[int] = mapped_column(Integer, default=6)
theme_mode: Mapped[str] = mapped_column(String(20), default="light")
last_login: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class JobDefinition(Base):
__tablename__ = "job_definitions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(180), unique=True, index=True)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
job_kind: Mapped[str] = mapped_column(String(30), default="script", index=True)
configuration: Mapped[dict] = mapped_column(JSON, default=dict)
system_key: Mapped[str | None] = mapped_column(String(120), unique=True, nullable=True, index=True)
platform: Mapped[str] = mapped_column(String(30), default="windows", index=True)
interpreter: Mapped[str] = mapped_column(String(30), default="powershell")
source_type: Mapped[str] = mapped_column(String(30), default="inline")
source_path: Mapped[str | None] = mapped_column(Text, nullable=True)
inline_script: Mapped[str | None] = mapped_column(Text, nullable=True)
arguments: Mapped[str | None] = mapped_column(Text, nullable=True)
timeout_seconds: Mapped[int] = mapped_column(Integer, default=300)
success_codes: Mapped[str] = mapped_column(String(120), default="0")
enabled: Mapped[bool] = mapped_column(Boolean, default=True, index=True)
is_system: Mapped[bool] = mapped_column(Boolean, default=False)
revision: Mapped[int] = mapped_column(Integer, default=1)
created_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
updated_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
revisions: Mapped[list["JobDefinitionRevision"]] = relationship(back_populates="definition", cascade="all, delete-orphan", order_by="JobDefinitionRevision.revision.desc()")
parameters: Mapped[list["JobDefinitionParameter"]] = relationship(back_populates="definition", cascade="all, delete-orphan", order_by="JobDefinitionParameter.sort_order")
class JobDefinitionParameter(Base):
__tablename__ = "job_definition_parameters"
__table_args__ = (UniqueConstraint("definition_id", "parameter_key", name="uq_job_definition_parameter_key"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
definition_id: Mapped[int] = mapped_column(ForeignKey("job_definitions.id", ondelete="CASCADE"), index=True)
parameter_key: Mapped[str] = mapped_column(String(80))
label: Mapped[str] = mapped_column(String(140))
description: Mapped[str | None] = mapped_column(Text, nullable=True)
data_type: Mapped[str] = mapped_column(String(30), default="text")
default_value: Mapped[str | None] = mapped_column(Text, nullable=True)
required: Mapped[bool] = mapped_column(Boolean, default=False)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
definition: Mapped["JobDefinition"] = relationship(back_populates="parameters")
class JobDefinitionAssetParameter(Base):
__tablename__ = "job_definition_asset_parameters"
__table_args__ = (UniqueConstraint("definition_parameter_id", "asset_id", name="uq_job_definition_asset_parameter"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
definition_parameter_id: Mapped[int] = mapped_column(ForeignKey("job_definition_parameters.id", ondelete="CASCADE"), index=True)
asset_id: Mapped[int] = mapped_column(ForeignKey("assets.id", ondelete="CASCADE"), index=True)
value_text: Mapped[str | None] = mapped_column(Text, nullable=True)
updated_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
parameter: Mapped["JobDefinitionParameter"] = relationship()
asset: Mapped["Asset"] = relationship()
class JobDefinitionRevision(Base):
__tablename__ = "job_definition_revisions"
__table_args__ = (UniqueConstraint("definition_id", "revision", name="uq_job_definition_revision"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
definition_id: Mapped[int] = mapped_column(ForeignKey("job_definitions.id", ondelete="CASCADE"), index=True)
revision: Mapped[int] = mapped_column(Integer)
snapshot: Mapped[dict] = mapped_column(JSON, default=dict)
changed_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
definition: Mapped["JobDefinition"] = relationship(back_populates="revisions")
class SoftwarePackage(Base):
__tablename__ = "software_packages"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(180), unique=True, index=True)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
package_type: Mapped[str] = mapped_column(String(40), default="command")
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
is_system: Mapped[bool] = mapped_column(Boolean, default=False)
command_windows: Mapped[str | None] = mapped_column(Text, nullable=True)
command_linux: Mapped[str | None] = mapped_column(Text, nullable=True)
command_macos: Mapped[str | None] = mapped_column(Text, nullable=True)
command_android: Mapped[str | None] = mapped_column(Text, nullable=True)
callback_timeout_minutes: Mapped[int] = mapped_column(Integer, default=30)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
parameters: Mapped[list["SoftwarePackageParameter"]] = relationship(back_populates="package", cascade="all, delete-orphan", order_by="SoftwarePackageParameter.sort_order")
jobs: Mapped[list["SoftwareJob"]] = relationship(back_populates="package")
class SoftwarePackageParameter(Base):
__tablename__ = "software_package_parameters"
__table_args__ = (UniqueConstraint("package_id", "name"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
package_id: Mapped[int] = mapped_column(ForeignKey("software_packages.id", ondelete="CASCADE"), index=True)
name: Mapped[str] = mapped_column(String(80))
label: Mapped[str] = mapped_column(String(120))
data_type: Mapped[str] = mapped_column(String(30), default="text")
required: Mapped[bool] = mapped_column(Boolean, default=False)
secret: Mapped[bool] = mapped_column(Boolean, default=False)
default_value: Mapped[str | None] = mapped_column(Text, nullable=True)
validation_regex: Mapped[str | None] = mapped_column(String(255), nullable=True)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
package: Mapped[SoftwarePackage] = relationship(back_populates="parameters")
class SoftwareJob(Base):
__tablename__ = "software_jobs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
asset_id: Mapped[int] = mapped_column(ForeignKey("assets.id", ondelete="CASCADE"), index=True)
package_id: Mapped[int] = mapped_column(ForeignKey("software_packages.id"), index=True)
status: Mapped[str] = mapped_column(String(30), default="created", index=True)
job_type: Mapped[str] = mapped_column(String(50), default="software_inventory", index=True)
action: Mapped[str] = mapped_column(String(30), default="run", index=True)
priority: Mapped[int] = mapped_column(Integer, default=100, index=True)
platform: Mapped[str] = mapped_column(String(30), default="unknown")
parameters: Mapped[dict] = mapped_column(JSON, default=dict)
result_data: Mapped[dict] = mapped_column(JSON, default=dict)
attempt_count: Mapped[int] = mapped_column(Integer, default=0)
max_attempts: Mapped[int] = mapped_column(Integer, default=1)
scheduled_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, index=True)
callback_token_hash: Mapped[str] = mapped_column(String(64), index=True)
callback_expires_at: Mapped[datetime] = mapped_column(DateTime)
callback_completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
command_preview: Mapped[str | None] = mapped_column(Text, nullable=True)
resolved_script: Mapped[str | None] = mapped_column(Text, nullable=True)
meshctrl_stdout: Mapped[str | None] = mapped_column(Text, nullable=True)
meshctrl_stderr: Mapped[str | None] = mapped_column(Text, nullable=True)
exit_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
message: Mapped[str | None] = mapped_column(Text, nullable=True)
log_text: Mapped[str] = mapped_column(Text, default="")
created_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
sent_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
started_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
finished_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
asset: Mapped[Asset] = relationship()
package: Mapped[SoftwarePackage] = relationship(back_populates="jobs")
events: Mapped[list["JobEvent"]] = relationship(back_populates="job", cascade="all, delete-orphan", order_by="JobEvent.created_at")
class AssetJobState(Base):
__tablename__ = "asset_job_states"
__table_args__ = (
UniqueConstraint("asset_id", "state_key", name="uq_asset_job_state_asset_key"),
Index("ix_asset_job_states_key_status", "state_key", "last_status"),
Index("ix_asset_job_states_key_success", "state_key", "last_success_at"),
Index("ix_asset_job_states_asset_key", "asset_id", "state_key"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
asset_id: Mapped[int] = mapped_column(ForeignKey("assets.id", ondelete="CASCADE"), index=True)
state_key: Mapped[str] = mapped_column(String(120), index=True)
job_type: Mapped[str] = mapped_column(String(50), index=True)
job_definition_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
definition_revision: Mapped[int | None] = mapped_column(Integer, nullable=True)
last_success_revision: Mapped[int | None] = mapped_column(Integer, nullable=True)
last_job_id: Mapped[int | None] = mapped_column(ForeignKey("software_jobs.id", ondelete="SET NULL"), nullable=True, index=True)
last_status: Mapped[str] = mapped_column(String(30), default="created", index=True)
last_status_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
last_created_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_sent_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_started_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_success_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, index=True)
execution_count: Mapped[int] = mapped_column(Integer, default=0)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
asset: Mapped[Asset] = relationship()
last_job: Mapped[SoftwareJob | None] = relationship()
class JobEvent(Base):
__tablename__ = "job_events"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
job_id: Mapped[int] = mapped_column(ForeignKey("software_jobs.id", ondelete="CASCADE"), index=True)
attempt: Mapped[int] = mapped_column(Integer, default=0)
event_type: Mapped[str] = mapped_column(String(40), default="status", index=True)
status: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True)
message: Mapped[str | None] = mapped_column(Text, nullable=True)
created_by: Mapped[str | None] = mapped_column(String(150), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
job: Mapped["SoftwareJob"] = relationship(back_populates="events")
class InstalledSoftware(Base):
__tablename__ = "installed_software"
__table_args__ = (UniqueConstraint("asset_id", "name", "version", "publisher", "architecture", name="uq_installed_software_identity"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
asset_id: Mapped[int] = mapped_column(ForeignKey("assets.id", ondelete="CASCADE"), index=True)
name: Mapped[str] = mapped_column(String(300), index=True)
version: Mapped[str] = mapped_column(String(160), default="")
publisher: Mapped[str] = mapped_column(String(220), default="")
architecture: Mapped[str] = mapped_column(String(40), default="")
platform: Mapped[str] = mapped_column(String(40), default="unknown", index=True)
install_date: Mapped[str | None] = mapped_column(String(40), nullable=True)
source: Mapped[str] = mapped_column(String(50), default="meshcentral-inventory")
first_seen_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
last_seen_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
asset: Mapped[Asset] = relationship()