chore(home): mirror from kalinamall (9883e6a) with papatramp URLs
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
from app.models.agent_command import AgentCommand
|
||||
from app.models.api_key import ApiKey
|
||||
from app.models.event import Event
|
||||
from app.models.host import Host
|
||||
from app.models.notification_channel import NotificationChannel
|
||||
from app.models.notification_cooldown import NotificationCooldown
|
||||
from app.models.notification_policy import NotificationPolicy
|
||||
from app.models.problem import Problem, ProblemEvent
|
||||
from app.models.login_attempt import LoginAttempt
|
||||
from app.models.ui_settings import UiSettings
|
||||
from app.models.event_severity_override import EventSeverityOverride
|
||||
from app.models.event_type_visibility import EventTypeVisibility
|
||||
from app.models.user import User
|
||||
from app.models.mobile_settings import MobileSettings
|
||||
from app.models.mobile_enrollment_code import MobileEnrollmentCode
|
||||
from app.models.mobile_device import MobileDevice
|
||||
from app.models.mobile_refresh_token import MobileRefreshToken
|
||||
|
||||
__all__ = [
|
||||
"ApiKey",
|
||||
"AgentCommand",
|
||||
"Event",
|
||||
"Host",
|
||||
"NotificationChannel",
|
||||
"NotificationCooldown",
|
||||
"NotificationPolicy",
|
||||
"Problem",
|
||||
"ProblemEvent",
|
||||
"User",
|
||||
"EventSeverityOverride",
|
||||
"EventTypeVisibility",
|
||||
"LoginAttempt",
|
||||
"UiSettings",
|
||||
"MobileSettings",
|
||||
"MobileEnrollmentCode",
|
||||
"MobileDevice",
|
||||
"MobileRefreshToken",
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, Text, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class AgentCommand(Base):
|
||||
__tablename__ = "agent_commands"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
command_uuid: Mapped[str] = mapped_column(String(36), unique=True, index=True)
|
||||
host_id: Mapped[int] = mapped_column(ForeignKey("hosts.id", ondelete="CASCADE"), index=True)
|
||||
event_id: Mapped[int | None] = mapped_column(ForeignKey("events.id", ondelete="SET NULL"), nullable=True)
|
||||
command_type: Mapped[str] = mapped_column(String(32))
|
||||
params: Mapped[dict | None] = mapped_column(JSONB, default=dict)
|
||||
status: Mapped[str] = mapped_column(String(16), default="pending", index=True)
|
||||
result_stdout: Mapped[str | None] = mapped_column(Text)
|
||||
result_stderr: Mapped[str | None] = mapped_column(Text)
|
||||
requested_by: Mapped[str | None] = mapped_column(String(128))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
host: Mapped["Host"] = relationship(back_populates="agent_commands")
|
||||
event: Mapped["Event | None"] = relationship()
|
||||
@@ -0,0 +1,17 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class ApiKey(Base):
|
||||
__tablename__ = "api_keys"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
name: Mapped[str] = mapped_column(String(128))
|
||||
key_prefix: Mapped[str] = mapped_column(String(16), index=True)
|
||||
key_hash: Mapped[str] = mapped_column(String(128), unique=True)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
@@ -0,0 +1,30 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, Text, UniqueConstraint, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Event(Base):
|
||||
__tablename__ = "events"
|
||||
__table_args__ = (UniqueConstraint("event_id", name="uq_events_event_id"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
event_id: Mapped[str] = mapped_column(String(36), index=True)
|
||||
host_id: Mapped[int] = mapped_column(ForeignKey("hosts.id"), index=True)
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
|
||||
received_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
category: Mapped[str] = mapped_column(String(64), index=True)
|
||||
type: Mapped[str] = mapped_column(String(128), index=True)
|
||||
severity: Mapped[str] = mapped_column(String(16), index=True)
|
||||
title: Mapped[str] = mapped_column(String(256))
|
||||
summary: Mapped[str] = mapped_column(Text)
|
||||
details: Mapped[dict | None] = mapped_column(JSONB)
|
||||
raw: Mapped[dict | None] = mapped_column(JSONB)
|
||||
dedup_key: Mapped[str | None] = mapped_column(String(512), index=True)
|
||||
correlation_id: Mapped[str | None] = mapped_column(String(36))
|
||||
payload: Mapped[dict] = mapped_column(JSONB)
|
||||
|
||||
host: Mapped["Host"] = relationship(back_populates="events")
|
||||
@@ -0,0 +1,19 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class EventSeverityOverride(Base):
|
||||
__tablename__ = "event_severity_overrides"
|
||||
|
||||
event_type: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
severity: Mapped[str] = mapped_column(String(16), nullable=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class EventTypeVisibility(Base):
|
||||
"""Per event-type UI visibility (default: show in events lists)."""
|
||||
|
||||
__tablename__ = "event_type_visibility"
|
||||
|
||||
event_type: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
show_in_events: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
onupdate=func.now(),
|
||||
nullable=False,
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String, Text, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Host(Base):
|
||||
__tablename__ = "hosts"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
agent_instance_id: Mapped[str | None] = mapped_column(String(128), unique=True, index=True)
|
||||
hostname: Mapped[str] = mapped_column(String(255), index=True)
|
||||
display_name: Mapped[str | None] = mapped_column(String(255))
|
||||
os_family: Mapped[str] = mapped_column(String(32))
|
||||
os_version: Mapped[str | None] = mapped_column(String(128))
|
||||
product: Mapped[str] = mapped_column(String(64), index=True)
|
||||
product_version: Mapped[str | None] = mapped_column(String(64))
|
||||
ipv4: Mapped[str | None] = mapped_column(String(45))
|
||||
ipv6: Mapped[str | None] = mapped_column(String(45))
|
||||
tags: Mapped[list | None] = mapped_column(JSONB, default=list)
|
||||
inventory: Mapped[dict | None] = mapped_column(JSONB)
|
||||
inventory_updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
use_sac_mode: Mapped[str | None] = mapped_column(String(32))
|
||||
ssh_admin_ok: Mapped[bool | None] = mapped_column(nullable=True)
|
||||
ssh_admin_checked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
# Optional override for SSH/WinRM (home PCs, unique local admin). Empty → global Settings.
|
||||
mgmt_user: Mapped[str | None] = mapped_column(String(256))
|
||||
mgmt_password: Mapped[str | None] = mapped_column(Text)
|
||||
pending_agent_update: Mapped[bool] = mapped_column(default=False, server_default="false")
|
||||
pending_update_requested_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
pending_update_target_version: Mapped[str | None] = mapped_column(String(64))
|
||||
agent_config: Mapped[dict | None] = mapped_column(JSONB)
|
||||
agent_config_revision: Mapped[int] = mapped_column(default=0, server_default="0")
|
||||
agent_update_state: Mapped[str | None] = mapped_column(String(32))
|
||||
agent_update_last_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
agent_update_last_error: Mapped[str | None] = mapped_column(Text)
|
||||
remote_action: Mapped[dict | None] = mapped_column(JSONB)
|
||||
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
events: Mapped[list["Event"]] = relationship(back_populates="host")
|
||||
agent_commands: Mapped[list["AgentCommand"]] = relationship(back_populates="host")
|
||||
@@ -0,0 +1,21 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class LoginAttempt(Base):
|
||||
__tablename__ = "login_attempts"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
ip_address: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||||
username: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
success: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
server_default=func.now(),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class MobileDevice(Base):
|
||||
__tablename__ = "mobile_devices"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("sac_users.id", ondelete="CASCADE"), index=True)
|
||||
device_uuid: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
display_name: Mapped[str] = mapped_column(String(128), default="")
|
||||
platform: Mapped[str] = mapped_column(String(32), default="android")
|
||||
app_version: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
fcm_token: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
fcm_token_updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
enrollment_code_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("mobile_enrollment_codes.id", ondelete="SET NULL"),
|
||||
)
|
||||
enrolled_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
last_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
@@ -0,0 +1,23 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class MobileEnrollmentCode(Base):
|
||||
__tablename__ = "mobile_enrollment_codes"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
label: Mapped[str] = mapped_column(String(128), default="")
|
||||
code_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
code_prefix: Mapped[str] = mapped_column(String(16))
|
||||
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("sac_users.id", ondelete="SET NULL"))
|
||||
target_user_id: Mapped[int | None] = mapped_column(ForeignKey("sac_users.id", ondelete="SET NULL"))
|
||||
login_mode: Mapped[str] = mapped_column(String(16), default="password")
|
||||
max_uses: Mapped[int] = mapped_column(Integer, default=1)
|
||||
use_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
||||
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
@@ -0,0 +1,17 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class MobileRefreshToken(Base):
|
||||
__tablename__ = "mobile_refresh_tokens"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
device_id: Mapped[int] = mapped_column(ForeignKey("mobile_devices.id", ondelete="CASCADE"))
|
||||
token_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
@@ -0,0 +1,21 @@
|
||||
from sqlalchemy import Boolean, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
MOBILE_SETTINGS_ROW_ID = 1
|
||||
|
||||
LOGIN_MODE_PASSWORD = "password"
|
||||
LOGIN_MODE_CODE_ONLY = "code_only"
|
||||
LOGIN_MODES = frozenset({LOGIN_MODE_PASSWORD, LOGIN_MODE_CODE_ONLY})
|
||||
|
||||
|
||||
class MobileSettings(Base):
|
||||
"""Singleton: глобальные настройки мобильного клиента Seaca."""
|
||||
|
||||
__tablename__ = "mobile_settings"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
devices_allowed: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
max_devices_per_user: Mapped[int] = mapped_column(Integer, default=3)
|
||||
min_app_version: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
@@ -0,0 +1,30 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class NotificationChannel(Base):
|
||||
__tablename__ = "notification_channels"
|
||||
|
||||
channel: Mapped[str] = mapped_column(String(32), primary_key=True)
|
||||
enabled: Mapped[bool] = mapped_column(default=False)
|
||||
min_severity: Mapped[str] = mapped_column(String(16), default="warning")
|
||||
bot_token: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
chat_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
webhook_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
webhook_secret_header: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
webhook_secret: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
smtp_host: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
smtp_port: Mapped[int | None] = mapped_column(nullable=True)
|
||||
smtp_user: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
smtp_password: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
mail_from: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
mail_to: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
smtp_starttls: Mapped[bool] = mapped_column(default=True)
|
||||
smtp_ssl: Mapped[bool] = mapped_column(default=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class NotificationCooldown(Base):
|
||||
"""Последняя отправка оповещения по ключу (dedup_key / fingerprint)."""
|
||||
|
||||
__tablename__ = "notification_cooldown"
|
||||
|
||||
cooldown_key: Mapped[str] = mapped_column(String(512), primary_key=True)
|
||||
kind: Mapped[str] = mapped_column(String(16)) # event | problem
|
||||
last_notified_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
||||
@@ -0,0 +1,19 @@
|
||||
from sqlalchemy import Boolean, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
POLICY_ROW_ID = 1
|
||||
|
||||
|
||||
class NotificationPolicy(Base):
|
||||
"""Singleton: глобальное правило severity → каналы (F-NOT-02 урезанно)."""
|
||||
|
||||
__tablename__ = "notification_policy"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
min_severity: Mapped[str] = mapped_column(String(16), default="warning")
|
||||
use_telegram: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
use_webhook: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
use_email: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
use_mobile: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
@@ -0,0 +1,39 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Problem(Base):
|
||||
__tablename__ = "problems"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
host_id: Mapped[int | None] = mapped_column(ForeignKey("hosts.id"), index=True)
|
||||
title: Mapped[str] = mapped_column(String(256))
|
||||
summary: Mapped[str] = mapped_column(Text)
|
||||
severity: Mapped[str] = mapped_column(String(16), index=True)
|
||||
status: Mapped[str] = mapped_column(String(32), index=True, default="open")
|
||||
resolved_by: Mapped[str | None] = mapped_column(String(16))
|
||||
rule_id: Mapped[str | None] = mapped_column(String(64))
|
||||
fingerprint: Mapped[str] = mapped_column(String(128), index=True)
|
||||
event_count: Mapped[int] = mapped_column(Integer, default=1)
|
||||
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
host: Mapped["Host | None"] = relationship()
|
||||
event_links: Mapped[list["ProblemEvent"]] = relationship(back_populates="problem")
|
||||
|
||||
|
||||
class ProblemEvent(Base):
|
||||
__tablename__ = "problem_events"
|
||||
|
||||
problem_id: Mapped[int] = mapped_column(ForeignKey("problems.id", ondelete="CASCADE"), primary_key=True)
|
||||
event_id: Mapped[int] = mapped_column(ForeignKey("events.id", ondelete="CASCADE"), primary_key=True)
|
||||
|
||||
problem: Mapped["Problem"] = relationship(back_populates="event_links")
|
||||
event: Mapped["Event"] = relationship()
|
||||
@@ -0,0 +1,40 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
UI_SETTINGS_ROW_ID = 1
|
||||
|
||||
|
||||
class UiSettings(Base):
|
||||
"""Singleton: глобальные настройки интерфейса SAC."""
|
||||
|
||||
__tablename__ = "ui_settings"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
show_sidebar_system_stats: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
win_admin_user: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
win_admin_password: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
linux_admin_user: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
linux_admin_password: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
agent_update_mode: Mapped[str] = mapped_column(String(16), default="gpo", nullable=False)
|
||||
agent_update_fallback_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
agent_update_fallback_minutes: Mapped[int] = mapped_column(default=15, nullable=False)
|
||||
agent_recommended_rdp_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
agent_recommended_ssh_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
agent_min_rdp_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
agent_min_ssh_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
win_agent_update_script: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
agent_rdp_git_repo_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
agent_ssh_git_repo_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
agent_git_branch: Mapped[str] = mapped_column(String(64), default="main", nullable=False)
|
||||
agent_git_rdp_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
agent_git_ssh_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
agent_git_fetched_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
login_ip_whitelist: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
login_sync_fail2ban: Mapped[bool] = mapped_column(default=False, server_default="false", nullable=False)
|
||||
auto_rdp_flap_disconnect: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, server_default="false", nullable=False
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
|
||||
USER_ROLE_ADMIN = "admin"
|
||||
USER_ROLE_MONITOR = "monitor"
|
||||
USER_ROLES = frozenset({USER_ROLE_ADMIN, USER_ROLE_MONITOR})
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "sac_users"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
username: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
password_hash: Mapped[str] = mapped_column(String(128))
|
||||
role: Mapped[str] = mapped_column(String(16), default=USER_ROLE_MONITOR)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
Reference in New Issue
Block a user