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