from datetime import datetime, timezone from enum import Enum from sqlalchemy import Boolean, DateTime, Enum as SqlEnum, ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from .database import Base class UserRole(str, Enum): """Роли пользователей панели.""" admin = "admin" client = "client" class GuestType(str, Enum): """Тип виртуализированной системы.""" vm = "vm" lxc = "lxc" class InstanceStatus(str, Enum): """Состояние инстанса в панели.""" creating = "creating" running = "running" stopped = "stopped" error = "error" deleting = "deleting" deleted = "deleted" class User(Base): """Пользователь панели с Argon2id-хешем пароля.""" __tablename__ = "users" id: Mapped[int] = mapped_column(Integer, primary_key=True) email: Mapped[str] = mapped_column(String(320), unique=True, index=True) password_hash: Mapped[str] = mapped_column(String(512)) role: Mapped[UserRole] = mapped_column( SqlEnum(UserRole), default=UserRole.client, nullable=False ) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False, ) instances: Mapped[list["Instance"]] = relationship(back_populates="owner") class Template(Base): """Тарифный шаблон VM или LXC, доступный клиентам.""" __tablename__ = "templates" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(64), unique=True, index=True) description: Mapped[str] = mapped_column(Text, default="") guest_type: Mapped[GuestType] = mapped_column(SqlEnum(GuestType), nullable=False) source_vmid: Mapped[int | None] = mapped_column(Integer, nullable=True) source_template: Mapped[str | None] = mapped_column(String(512), nullable=True) cores: Mapped[int] = mapped_column(Integer, default=1, nullable=False) memory_mb: Mapped[int] = mapped_column(Integer, default=1024, nullable=False) disk_gb: Mapped[int] = mapped_column(Integer, default=10, nullable=False) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False, ) instances: Mapped[list["Instance"]] = relationship(back_populates="template") class Instance(Base): """Инстанс VM/LXC, принадлежащий пользователю.""" __tablename__ = "instances" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(63)) vmid: Mapped[int | None] = mapped_column(Integer, unique=True, nullable=True) node: Mapped[str] = mapped_column(String(64)) guest_type: Mapped[GuestType] = mapped_column(SqlEnum(GuestType), nullable=False) status: Mapped[InstanceStatus] = mapped_column( SqlEnum(InstanceStatus), default=InstanceStatus.creating, nullable=False ) owner_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True) template_id: Mapped[int] = mapped_column(ForeignKey("templates.id"), index=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False, ) owner: Mapped[User] = relationship(back_populates="instances") template: Mapped[Template] = relationship(back_populates="instances")