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)