gitscope
← marzneshin / v0.1.0 / app/db/models.py
https://dev.kronoz.win/r/marzneshin.git
app/db/models.py · Python
Blame Raw
  1import os
  2from datetime import datetime
  3
  4from sqlalchemy import (BigInteger, Boolean, Column, DateTime, Enum,
  5                        Float, ForeignKey, Integer, String, Table,
  6                        UniqueConstraint)
  7from sqlalchemy.orm import relationship
  8from sqlalchemy.sql.expression import text
  9
 10from app.db.base import Base
 11from app.models.node import NodeStatus
 12from app.models.proxy import (InboundHostALPN, InboundHostFingerprint,
 13                              InboundHostSecurity, ProxyTypes)
 14from app.models.user import (ReminderType, UserDataLimitResetStrategy,
 15                             UserStatus)
 16
 17
 18class Admin(Base):
 19    __tablename__ = "admins"
 20
 21    id = Column(Integer, primary_key=True)
 22    username = Column(String(32), unique=True, index=True)
 23    hashed_password = Column(String(128))
 24    users = relationship("User", back_populates="admin")
 25    created_at = Column(DateTime, default=datetime.utcnow)
 26    is_sudo = Column(Boolean, default=False)
 27    password_reset_at = Column(DateTime)
 28
 29
 30inbounds_services = Table(
 31    "inbounds_services",
 32    Base.metadata,
 33    Column("inbound_id", ForeignKey("inbounds.id"), primary_key=True),
 34    Column("service_id", ForeignKey("services.id"), primary_key=True),
 35)
 36
 37users_services = Table(
 38    "users_services",
 39    Base.metadata,
 40    Column("user_id", ForeignKey("users.id"), primary_key=True),
 41    Column("service_id", ForeignKey("services.id"), primary_key=True),
 42)
 43
 44
 45class Service(Base):
 46    __tablename__ = "services"
 47    id = Column(Integer, primary_key=True)
 48    name = Column(String(64))
 49    # modifications = Column(String(1024))
 50    users = relationship("User", secondary=users_services, back_populates="services")
 51    inbounds = relationship("Inbound", secondary=inbounds_services, back_populates="services")
 52
 53
 54class User(Base):
 55    __tablename__ = "users"
 56
 57    id = Column(Integer, primary_key=True)
 58    username = Column(String(32), unique=True, index=True)
 59    key = Column(String(64), unique=True)
 60    services = relationship("Service", secondary=users_services, back_populates="users", lazy="joined")
 61    inbounds = relationship(
 62        "Inbound",
 63        secondary="join(users_services, inbounds_services, inbounds_services.c.service_id == users_services.c.service_id)"
 64                  ".join(Inbound, Inbound.id == inbounds_services.c.inbound_id)", viewonly=True,
 65        distinct_target_key=True,
 66        lazy="joined")
 67    # proxies = relationship("Proxy", back_populates="user", cascade="all, delete-orphan")
 68    status = Column(Enum(UserStatus), nullable=False, default=UserStatus.active)
 69    used_traffic = Column(BigInteger, default=0)
 70    node_usages = relationship("NodeUserUsage", back_populates="user", cascade="all,delete,delete-orphan",
 71                               lazy="joined")
 72    notification_reminders = relationship("NotificationReminder", back_populates="user",
 73                                          cascade="all,delete,delete-orphan")
 74    data_limit = Column(BigInteger)
 75    data_limit_reset_strategy = Column(
 76        Enum(UserDataLimitResetStrategy),
 77        nullable=False,
 78        default=UserDataLimitResetStrategy.no_reset,
 79    )
 80    ip_limit = Column(Integer, nullable=False, default=-1)
 81    settings = Column(String)
 82    usage_logs = relationship("UserUsageResetLogs", back_populates="user", lazy="joined")
 83    expire = Column(Integer)
 84    admin_id = Column(Integer, ForeignKey("admins.id"))
 85    admin = relationship("Admin", back_populates="users")
 86    sub_updated_at = Column(DateTime)
 87    sub_last_user_agent = Column(String(512))
 88    created_at = Column(DateTime, default=datetime.utcnow)
 89    note = Column(String(500))
 90    online_at = Column(DateTime)
 91    on_hold_expire_duration = Column(BigInteger)
 92    on_hold_timeout = Column(DateTime)
 93    edit_at = Column(DateTime)
 94
 95    @property
 96    def lifetime_used_traffic(self):
 97        return (
 98            sum([log.used_traffic_at_reset for log in self.usage_logs])
 99            + self.used_traffic
100        )
101
102    @property
103    def last_traffic_reset_time(self):
104        return self.usage_logs[-1].reset_at if self.usage_logs else self.created_at
105
106
107class UserUsageResetLogs(Base):
108    __tablename__ = "user_usage_logs"
109
110    id = Column(Integer, primary_key=True)
111    user_id = Column(Integer, ForeignKey("users.id"))
112    user = relationship("User", back_populates="usage_logs")
113    used_traffic_at_reset = Column(BigInteger, nullable=False)
114    reset_at = Column(DateTime, default=datetime.utcnow)
115
116
117class Inbound(Base):
118    __tablename__ = "inbounds"
119    __table_args__ = (
120        UniqueConstraint("node_id", "tag"),
121    )
122
123    id = Column(Integer, primary_key=True)
124    protocol = Column(Enum(ProxyTypes))
125    tag = Column(String(256), nullable=False)
126    config = Column(String(), nullable=False)
127    node_id = Column(Integer, ForeignKey("nodes.id"), index=True)
128    node = relationship("Node", back_populates="inbounds")
129    services = relationship("Service", secondary=inbounds_services, back_populates="inbounds")
130    hosts = relationship(
131        "InboundHost", back_populates="inbound", cascade="all, delete, delete-orphan"
132    )
133
134
135class InboundHost(Base):
136    __tablename__ = "hosts"
137
138    id = Column(Integer, primary_key=True)
139    remark = Column(String(256), nullable=False)
140    address = Column(String(256), nullable=False)
141    port = Column(Integer)
142    path = Column(String(256))
143    sni = Column(String(256))
144    host = Column(String(256))
145    security = Column(
146        Enum(InboundHostSecurity),
147        nullable=False,
148        default=InboundHostSecurity.inbound_default,
149    )
150    alpn = Column(
151        Enum(InboundHostALPN),
152        nullable=False,
153        default=InboundHostSecurity.none,
154        server_default=InboundHostSecurity.none.name
155    )
156    fingerprint = Column(
157        Enum(InboundHostFingerprint),
158        nullable=False,
159        default=InboundHostSecurity.none,
160        server_default=InboundHostSecurity.none.name
161    )
162
163    inbound_id = Column(Integer, ForeignKey("inbounds.id"), nullable=False)
164    inbound = relationship("Inbound", back_populates="hosts")
165    allowinsecure = Column(Boolean, default=False)
166    is_disabled = Column(Boolean, default=False)
167
168
169class System(Base):
170    __tablename__ = "system"
171
172    id = Column(Integer, primary_key=True)
173    uplink = Column(BigInteger, default=0)
174    downlink = Column(BigInteger, default=0)
175
176
177class JWT(Base):
178    __tablename__ = "jwt"
179
180    id = Column(Integer, primary_key=True)
181    secret_key = Column(
182        String(64), nullable=False, default=lambda: os.urandom(32).hex()
183    )
184
185
186class TLS(Base):
187    __tablename__ = "tls"
188
189    id = Column(Integer, primary_key=True)
190    key = Column(String(4096), nullable=False)
191    certificate = Column(String(2048), nullable=False)
192
193
194class Node(Base):
195    __tablename__ = "nodes"
196    __table_args__ = (
197        UniqueConstraint("address", "port"),
198    )
199    id = Column(Integer, primary_key=True)
200    name = Column(String(256), unique=True)
201    connection_backend = Column(String())
202    address = Column(String(256))
203    port = Column(Integer)
204    xray_version = Column(String(32))
205    inbounds = relationship("Inbound", back_populates="node", cascade="all, delete")
206    status = Column(Enum(NodeStatus), nullable=False, default=NodeStatus.unhealthy)
207    last_status_change = Column(DateTime, default=datetime.utcnow)
208    message = Column(String(1024))
209    created_at = Column(DateTime, default=datetime.utcnow)
210    uplink = Column(BigInteger, default=0)
211    downlink = Column(BigInteger, default=0)
212    user_usages = relationship("NodeUserUsage", back_populates="node", cascade="all, delete, delete-orphan")
213    usages = relationship("NodeUsage", back_populates="node", cascade="all, delete, delete-orphan")
214    usage_coefficient = Column(Float, nullable=False, server_default=text("1.0"), default=1)
215
216
217class NodeUserUsage(Base):
218    __tablename__ = "node_user_usages"
219    __table_args__ = (
220        UniqueConstraint('created_at', 'user_id', 'node_id'),
221    )
222
223    id = Column(Integer, primary_key=True)
224    created_at = Column(DateTime, nullable=False)  # one hour per record
225    user_id = Column(Integer, ForeignKey("users.id"))
226    user = relationship("User", back_populates="node_usages")
227    node_id = Column(Integer, ForeignKey("nodes.id"))
228    node = relationship("Node", back_populates="user_usages")
229    used_traffic = Column(BigInteger, default=0)
230
231
232class NodeUsage(Base):
233    __tablename__ = "node_usages"
234    __table_args__ = (
235        UniqueConstraint('created_at', 'node_id'),
236    )
237
238    id = Column(Integer, primary_key=True)
239    created_at = Column(DateTime, nullable=False)  # one hour per record
240    node_id = Column(Integer, ForeignKey("nodes.id"))
241    node = relationship("Node", back_populates="usages")
242    uplink = Column(BigInteger, default=0)
243    downlink = Column(BigInteger, default=0)
244
245
246class NotificationReminder(Base):
247    __tablename__ = "notification_reminders"
248
249    id = Column(Integer, primary_key=True)
250    user_id = Column(Integer, ForeignKey("users.id"))
251    user = relationship("User", back_populates="notification_reminders")
252    type = Column(Enum(ReminderType), nullable=False)
253    expires_at = Column(DateTime)
254    created_at = Column(DateTime, default=datetime.utcnow)