from datetime import datetime from enum import Enum from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class Protocol(str, Enum): WIREGUARD = "wireguard" AWG2 = "awg2" class UserRole(str, Enum): ADMIN = "admin" class AdminUser(Base): __tablename__ = "admin_users" id: Mapped[int] = mapped_column(Integer, primary_key=True) username: Mapped[str] = mapped_column(String(64), unique=True, index=True) email: Mapped[str] = mapped_column(String(255), unique=True) password_hash: Mapped[str] = mapped_column(String(255)) role: Mapped[str] = mapped_column(String(32), default=UserRole.ADMIN.value) is_active: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) class VpnServer(Base): __tablename__ = "vpn_servers" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(120)) protocol: Mapped[str] = mapped_column(String(32), index=True) public_host: Mapped[str] = mapped_column(String(255)) public_port: Mapped[int] = mapped_column(Integer) interface_name: Mapped[str] = mapped_column(String(32)) subnet: Mapped[str] = mapped_column(String(64)) dns: Mapped[str] = mapped_column(String(255), default="1.1.1.1") server_private_key: Mapped[str] = mapped_column(Text) server_public_key: Mapped[str] = mapped_column(Text) # SSH access to the VPS ssh_host: Mapped[str | None] = mapped_column(String(255), nullable=True) ssh_port: Mapped[int] = mapped_column(Integer, default=22) ssh_username: Mapped[str | None] = mapped_column(String(128), nullable=True) ssh_password: Mapped[str | None] = mapped_column(Text, nullable=True) ssh_private_key: Mapped[str | None] = mapped_column(Text, nullable=True) ssh_server_info: Mapped[str | None] = mapped_column(Text, nullable=True) ssh_reachable: Mapped[bool] = mapped_column(Boolean, default=False) ssh_last_checked: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) # AmneziaWG 2.0 obfuscation params (ignored for plain WireGuard) jc: Mapped[int | None] = mapped_column(Integer, nullable=True) jmin: Mapped[int | None] = mapped_column(Integer, nullable=True) jmax: Mapped[int | None] = mapped_column(Integer, nullable=True) s1: Mapped[int | None] = mapped_column(Integer, nullable=True) s2: Mapped[int | None] = mapped_column(Integer, nullable=True) h1: Mapped[int | None] = mapped_column(Integer, nullable=True) h2: Mapped[int | None] = mapped_column(Integer, nullable=True) h3: Mapped[int | None] = mapped_column(Integer, nullable=True) h4: Mapped[int | None] = mapped_column(Integer, nullable=True) is_enabled: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) clients: Mapped[list["VpnClient"]] = relationship(back_populates="server", cascade="all, delete-orphan") class VpnClient(Base): __tablename__ = "vpn_clients" __table_args__ = (UniqueConstraint("server_id", "name", name="uq_client_name_per_server"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) server_id: Mapped[int] = mapped_column(ForeignKey("vpn_servers.id", ondelete="CASCADE"), index=True) name: Mapped[str] = mapped_column(String(120)) private_key: Mapped[str] = mapped_column(Text) public_key: Mapped[str] = mapped_column(Text) preshared_key: Mapped[str] = mapped_column(Text) address: Mapped[str] = mapped_column(String(64)) allowed_ips: Mapped[str] = mapped_column(String(255), default="0.0.0.0/0, ::/0") is_enabled: Mapped[bool] = mapped_column(Boolean, default=True) notes: Mapped[str | None] = mapped_column(Text, nullable=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() ) server: Mapped[VpnServer] = relationship(back_populates="clients")