hiddifypanel 10.20.3__py3-none-any.whl → 10.30.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/base.py +17 -8
- hiddifypanel/cache.py +2 -51
- hiddifypanel/drivers/wireguard_api.py +24 -5
- hiddifypanel/hutils/convert.py +1 -1
- hiddifypanel/hutils/flask.py +28 -2
- hiddifypanel/hutils/importer/xui.py +6 -7
- hiddifypanel/hutils/network/__init__.py +1 -0
- hiddifypanel/hutils/network/cf_api.py +84 -0
- hiddifypanel/hutils/network/net.py +26 -49
- hiddifypanel/hutils/node/child.py +25 -7
- hiddifypanel/hutils/node/parent.py +7 -7
- hiddifypanel/hutils/node/shared.py +19 -6
- hiddifypanel/hutils/proxy/clash.py +1 -1
- hiddifypanel/hutils/proxy/shared.py +1 -1
- hiddifypanel/hutils/proxy/singbox.py +2 -3
- hiddifypanel/hutils/proxy/xray.py +12 -10
- hiddifypanel/hutils/proxy/xrayjson.py +26 -49
- hiddifypanel/hutils/utils.py +47 -3
- hiddifypanel/models/__init__.py +1 -1
- hiddifypanel/models/admin.py +9 -2
- hiddifypanel/models/base_account.py +3 -1
- hiddifypanel/models/config.py +5 -7
- hiddifypanel/models/config_enum.py +18 -6
- hiddifypanel/models/domain.py +82 -118
- hiddifypanel/models/user.py +44 -24
- hiddifypanel/panel/admin/Actions.py +6 -11
- hiddifypanel/panel/admin/AdminstratorAdmin.py +3 -9
- hiddifypanel/panel/admin/Backup.py +5 -8
- hiddifypanel/panel/admin/Dashboard.py +3 -4
- hiddifypanel/panel/admin/DomainAdmin.py +20 -15
- hiddifypanel/panel/admin/ProxyAdmin.py +3 -10
- hiddifypanel/panel/admin/QuickSetup.py +1 -1
- hiddifypanel/panel/admin/SettingAdmin.py +7 -5
- hiddifypanel/panel/admin/Terminal.py +0 -1
- hiddifypanel/panel/admin/UserAdmin.py +4 -3
- hiddifypanel/panel/cli.py +36 -23
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +2 -4
- hiddifypanel/panel/commercial/restapi/v1/tgbot.py +7 -4
- hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +17 -13
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +4 -3
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +28 -10
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +2 -19
- hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +27 -4
- hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +28 -9
- hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -21
- hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +8 -4
- hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +19 -3
- hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +48 -42
- hiddifypanel/panel/commercial/telegrambot/Usage.py +1 -1
- hiddifypanel/panel/commercial/telegrambot/admin.py +1 -1
- hiddifypanel/panel/commercial/telegrambot/information.py +1 -1
- hiddifypanel/panel/common.py +5 -11
- hiddifypanel/panel/hiddify.py +9 -20
- hiddifypanel/panel/init_db.py +31 -13
- hiddifypanel/panel/usage.py +38 -9
- hiddifypanel/panel/user/user.py +52 -32
- hiddifypanel/templates/admin-layout.html +2 -2
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +80 -25
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +74 -20
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +60 -6
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +158 -78
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +60 -6
- hiddifypanel/translations.i18n/en.json +62 -22
- hiddifypanel/translations.i18n/fa.json +57 -17
- hiddifypanel/translations.i18n/pt.json +43 -3
- hiddifypanel/translations.i18n/ru.json +112 -72
- hiddifypanel/translations.i18n/zh.json +43 -3
- {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/METADATA +2 -1
- {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/RECORD +81 -81
- {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/WHEEL +1 -1
- hiddifypanel/panel/cf_api.py +0 -37
- {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/top_level.txt +0 -0
hiddifypanel/models/domain.py
CHANGED
@@ -1,22 +1,16 @@
|
|
1
1
|
from enum import auto
|
2
|
-
from typing import List
|
3
|
-
|
4
|
-
from urllib.parse import urlparse
|
5
|
-
|
2
|
+
from typing import Dict, List
|
6
3
|
from flask import request
|
7
|
-
|
8
4
|
from flask_babel import lazy_gettext as _
|
9
5
|
from sqlalchemy.orm import backref
|
10
|
-
from urllib.parse import urlparse
|
11
6
|
from strenum import StrEnum
|
7
|
+
from sqlalchemy_serializer import SerializerMixin
|
12
8
|
|
13
|
-
from hiddifypanel.database import db
|
14
9
|
|
10
|
+
from hiddifypanel.database import db
|
15
11
|
from hiddifypanel.models.config import hconfig
|
16
12
|
from .child import Child
|
17
13
|
from hiddifypanel.models.config_enum import ConfigEnum
|
18
|
-
from sqlalchemy.orm import backref
|
19
|
-
from sqlalchemy_serializer import SerializerMixin
|
20
14
|
|
21
15
|
|
22
16
|
class DomainType(StrEnum):
|
@@ -69,11 +63,11 @@ class Domain(db.Model, SerializerMixin):
|
|
69
63
|
'mode': self.mode,
|
70
64
|
'alias': self.alias,
|
71
65
|
'sub_link_only': self.sub_link_only,
|
72
|
-
'child_unique_id': self.child.unique_id if self.child else '',
|
66
|
+
'child_unique_id': self.child.unique_id if self.child else '', # type: ignore
|
73
67
|
'cdn_ip': self.cdn_ip,
|
74
68
|
'servernames': self.servernames,
|
75
69
|
'grpc': self.grpc,
|
76
|
-
'show_domains': [dd.domain for dd in self.show_domains],
|
70
|
+
'show_domains': [dd.domain for dd in self.show_domains], # type: ignore
|
77
71
|
}
|
78
72
|
if dump_child_id:
|
79
73
|
data['child_id'] = self.child_id
|
@@ -124,110 +118,80 @@ class Domain(db.Model, SerializerMixin):
|
|
124
118
|
# TODO: check validity of the range of the port
|
125
119
|
return int(hconfig(ConfigEnum.reality_port, self.child_id)) + self.port_index
|
126
120
|
|
127
|
-
|
128
|
-
def
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
dbdomain.child_id = child_id
|
205
|
-
|
206
|
-
dbdomain.mode = domain['mode']
|
207
|
-
if (str(domain.get('sub_link_only', False)).lower() == 'true'):
|
208
|
-
dbdomain.mode = DomainType.sub_link_only
|
209
|
-
dbdomain.cdn_ip = domain.get('cdn_ip', '')
|
210
|
-
dbdomain.alias = domain.get('alias', '')
|
211
|
-
dbdomain.grpc = domain.get('grpc', False)
|
212
|
-
dbdomain.servernames = domain.get('servernames', '')
|
213
|
-
show_domains = domain.get('show_domains', [])
|
214
|
-
dbdomain.show_domains = Domain.query.filter(Domain.domain.in_(show_domains)).all()
|
215
|
-
if commit:
|
216
|
-
db.session.commit()
|
217
|
-
|
218
|
-
|
219
|
-
def bulk_register_domains(domains, commit=True, remove=False, force_child_unique_id: str | None = None):
|
220
|
-
from hiddifypanel.panel import hiddify
|
221
|
-
child_ids = {}
|
222
|
-
for domain in domains:
|
223
|
-
child_id = hiddify.get_child(unique_id=force_child_unique_id)
|
224
|
-
child_ids[child_id] = 1
|
225
|
-
add_or_update_domain(commit=False, child_id=child_id, **domain)
|
226
|
-
if remove and len(child_ids):
|
227
|
-
dd = {d['domain']: 1 for d in domains}
|
228
|
-
for d in Domain.query.filter(Domain.child_id.in_(child_ids)):
|
229
|
-
if d.domain not in dd:
|
230
|
-
db.session.delete(d)
|
231
|
-
|
232
|
-
if commit:
|
233
|
-
db.session.commit()
|
121
|
+
@classmethod
|
122
|
+
def by_mode(cls, mode: DomainType) -> List['Domain']:
|
123
|
+
domains = Domain.query.filter(Domain.mode == mode).all()
|
124
|
+
if domains:
|
125
|
+
return [d.domain for d in domains]
|
126
|
+
return []
|
127
|
+
|
128
|
+
@classmethod
|
129
|
+
def modes_and_domains(cls) -> Dict[DomainType, List['Domain']]:
|
130
|
+
return {mode: cls.by_mode(mode) for mode in DomainType}
|
131
|
+
|
132
|
+
@classmethod
|
133
|
+
def by_domain(cls, domain: str) -> 'Domain | None':
|
134
|
+
return Domain.query.filter(Domain.domain == domain).first()
|
135
|
+
|
136
|
+
@classmethod
|
137
|
+
def get_panel_link(cls, child_id: int | None = None) -> str | None:
|
138
|
+
if child_id is None:
|
139
|
+
child_id = Child.current().id # type: ignore
|
140
|
+
domains = Domain.query.filter(Domain.mode.in_(
|
141
|
+
[DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]),
|
142
|
+
Domain.child_id == child_id
|
143
|
+
).all()
|
144
|
+
if not domains:
|
145
|
+
return None
|
146
|
+
return domains[0].domain
|
147
|
+
|
148
|
+
@classmethod
|
149
|
+
def get_domains(cls, always_add_ip=False, always_add_all_domains=False) -> List['Domain']:
|
150
|
+
from hiddifypanel import hutils
|
151
|
+
domains = []
|
152
|
+
domains = Domain.query.filter(Domain.mode == DomainType.sub_link_only, Domain.child_id == Child.current().id).all()
|
153
|
+
if not len(domains) or always_add_all_domains:
|
154
|
+
domains = Domain.query.filter(Domain.mode.notin_([DomainType.fake, DomainType.reality])).all()
|
155
|
+
|
156
|
+
if len(domains) == 0 and request:
|
157
|
+
domains = [Domain(domain=request.host)] # type: ignore
|
158
|
+
if len(domains) == 0 or always_add_ip:
|
159
|
+
domains += [Domain(domain=hutils.network.get_ip_str(4))] # type: ignore
|
160
|
+
return domains
|
161
|
+
|
162
|
+
@classmethod
|
163
|
+
def add_or_update(cls, commit=True, child_id=0, **domain):
|
164
|
+
dbdomain = Domain.query.filter(Domain.domain == domain['domain']).first()
|
165
|
+
if not dbdomain:
|
166
|
+
dbdomain = Domain(domain=domain['domain']) # type: ignore
|
167
|
+
db.session.add(dbdomain)
|
168
|
+
dbdomain.child_id = child_id
|
169
|
+
|
170
|
+
dbdomain.mode = domain['mode']
|
171
|
+
if (str(domain.get('sub_link_only', False)).lower() == 'true'):
|
172
|
+
dbdomain.mode = DomainType.sub_link_only
|
173
|
+
dbdomain.cdn_ip = domain.get('cdn_ip', '')
|
174
|
+
dbdomain.alias = domain.get('alias', '')
|
175
|
+
dbdomain.grpc = domain.get('grpc', False)
|
176
|
+
dbdomain.servernames = domain.get('servernames', '')
|
177
|
+
show_domains = domain.get('show_domains', [])
|
178
|
+
dbdomain.show_domains = Domain.query.filter(Domain.domain.in_(show_domains)).all()
|
179
|
+
if commit:
|
180
|
+
db.session.commit()
|
181
|
+
|
182
|
+
@classmethod
|
183
|
+
def bulk_register(cls, domains, commit=True, remove=False, force_child_unique_id: str | None = None):
|
184
|
+
from hiddifypanel.panel import hiddify
|
185
|
+
child_ids = {}
|
186
|
+
for domain in domains:
|
187
|
+
child_id = hiddify.get_child(unique_id=force_child_unique_id)
|
188
|
+
child_ids[child_id] = 1
|
189
|
+
cls.add_or_update(commit=False, child_id=child_id, **domain)
|
190
|
+
if remove and len(child_ids):
|
191
|
+
dd = {d['domain']: 1 for d in domains}
|
192
|
+
for d in Domain.query.filter(Domain.child_id.in_(child_ids)):
|
193
|
+
if d.domain not in dd:
|
194
|
+
db.session.delete(d)
|
195
|
+
|
196
|
+
if commit:
|
197
|
+
db.session.commit()
|
hiddifypanel/models/user.py
CHANGED
@@ -65,7 +65,7 @@ class User(BaseAccount, SerializerMixin):
|
|
65
65
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
66
66
|
last_online = db.Column(db.DateTime, nullable=False, default=datetime.datetime.min)
|
67
67
|
# removed
|
68
|
-
expiry_time = db.Column(db.Date, default=datetime.date.today() + relativedelta.relativedelta(months=6))
|
68
|
+
# expiry_time = db.Column(db.Date, default=datetime.date.today() + relativedelta.relativedelta(months=6))
|
69
69
|
usage_limit = db.Column(db.BigInteger, default=1000 * ONE_GIG, nullable=False)
|
70
70
|
package_days = db.Column(db.Integer, default=90, nullable=False)
|
71
71
|
mode = db.Column(db.Enum(UserMode), default=UserMode.no_reset, nullable=False)
|
@@ -138,15 +138,23 @@ class User(BaseAccount, SerializerMixin):
|
|
138
138
|
# print("start_date",user.start_date, "pack",package_mode_dic.get(user.mode,10000), "total",(datetime.date.today()-user.start_date).days)
|
139
139
|
# if user.mode==UserMode.daily:
|
140
140
|
# return 0
|
141
|
-
res = True
|
142
141
|
if not self.last_reset_time:
|
143
|
-
|
142
|
+
return True
|
144
143
|
elif not self.start_date or (datetime.date.today() - self.last_reset_time).days == 0:
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
return False
|
145
|
+
return ((datetime.date.today() - self.start_date).days % package_mode_dic.get(self.mode, 10000)) == 0
|
146
|
+
|
147
|
+
def reset_usage(self, commit: bool = False):
|
148
|
+
'''Resets the user usages'''
|
149
|
+
self.last_reset_time = datetime.date.today()
|
150
|
+
self.current_usage_GB = 0
|
151
|
+
|
152
|
+
# there's no usage of UserDetail yet, but we reset it too
|
153
|
+
# if ud := UserDetail.query.filter(UserDetail.user_id == self.id).first():
|
154
|
+
# ud.current_usage_GB = 0
|
148
155
|
|
149
|
-
|
156
|
+
if commit:
|
157
|
+
db.session.commit()
|
150
158
|
|
151
159
|
def days_to_reset(self):
|
152
160
|
"""
|
@@ -190,6 +198,8 @@ class User(BaseAccount, SerializerMixin):
|
|
190
198
|
|
191
199
|
@classmethod
|
192
200
|
def by_uuid(cls, uuid: str, create: bool = False) -> 'User':
|
201
|
+
if not isinstance(uuid, str):
|
202
|
+
uuid = str(uuid)
|
193
203
|
account = User.query.filter(User.uuid == uuid).first()
|
194
204
|
if not account and create:
|
195
205
|
dbuser = User(uuid=uuid, name="unknown", added_by=AdminUser.current_admin_or_owner().id)
|
@@ -213,38 +223,44 @@ class User(BaseAccount, SerializerMixin):
|
|
213
223
|
else:
|
214
224
|
dbuser.added_by = 1
|
215
225
|
|
216
|
-
if data.get('expiry_time', ''):
|
217
|
-
|
218
|
-
|
219
|
-
expiry_time = hutils.convert.json_to_date(data['expiry_time'])
|
220
|
-
dbuser.start_date = last_reset_time
|
221
|
-
dbuser.package_days = (expiry_time - last_reset_time).days # type: ignore
|
226
|
+
# if data.get('expiry_time', ''): #v4
|
227
|
+
# last_reset_time = hutils.convert.json_to_time(data.get('last_reset_time', '')) or datetime.date.today()
|
222
228
|
|
223
|
-
|
229
|
+
# expiry_time = hutils.convert.json_to_date(data['expiry_time'])
|
230
|
+
# dbuser.start_date = last_reset_time
|
231
|
+
# dbuser.package_days = (expiry_time - last_reset_time).days # type: ignore
|
232
|
+
# el
|
233
|
+
if 'package_days' in data:
|
224
234
|
dbuser.package_days = data['package_days']
|
225
235
|
if data.get('start_date', ''):
|
226
236
|
dbuser.start_date = hutils.convert.json_to_date(data['start_date'])
|
227
237
|
else:
|
228
238
|
dbuser.start_date = None
|
229
|
-
dbuser.current_usage_GB = data.get('current_usage_GB', 0)
|
230
239
|
|
231
|
-
|
240
|
+
if (c_GB := data.get('current_usage_GB')) is not None:
|
241
|
+
dbuser.current_usage_GB = c_GB
|
242
|
+
elif (c := data.get('current_usage')) is not None:
|
243
|
+
dbuser.current_usage = c
|
244
|
+
else:
|
245
|
+
dbuser.current_usage = 0
|
246
|
+
|
247
|
+
if (l_GB := data.get('usage_limit_GB')) is not None:
|
248
|
+
dbuser.usage_limit_GB = l_GB
|
249
|
+
elif (l := data.get('usage_limit')) is not None:
|
250
|
+
dbuser.usage_limit = l
|
251
|
+
else:
|
252
|
+
dbuser.usage_limit_GB = 1000
|
253
|
+
|
232
254
|
dbuser.enable = data.get('enable', True)
|
255
|
+
|
233
256
|
if data.get('ed25519_private_key', ''):
|
234
257
|
dbuser.ed25519_private_key = data.get('ed25519_private_key', '')
|
235
258
|
dbuser.ed25519_public_key = data.get('ed25519_public_key', '')
|
236
|
-
# if not dbuser.ed25519_private_key:
|
237
|
-
# priv, publ = hutils.crypto.get_ed25519_private_public_pair()
|
238
|
-
# dbuser.ed25519_private_key = priv
|
239
|
-
# dbuser.ed25519_public_key = publ
|
240
259
|
|
241
260
|
dbuser.wg_pk = data.get('wg_pk', dbuser.wg_pk)
|
242
261
|
dbuser.wg_pub = data.get('wg_pub', dbuser.wg_pub)
|
243
262
|
dbuser.wg_psk = data.get('wg_psk', dbuser.wg_psk)
|
244
263
|
|
245
|
-
# if not dbuser.wg_pk:
|
246
|
-
# dbuser.wg_pk, dbuser.wg_pub, dbuser.wg_psk = hutils.crypto.get_wg_private_public_psk_pair()
|
247
|
-
|
248
264
|
mode = data.get('mode', UserMode.no_reset)
|
249
265
|
if mode == 'disable':
|
250
266
|
mode = UserMode.no_reset
|
@@ -271,6 +287,9 @@ class User(BaseAccount, SerializerMixin):
|
|
271
287
|
from hiddifypanel import hutils
|
272
288
|
if dump_id:
|
273
289
|
base['id'] = self.id
|
290
|
+
if not base.get('lang'):
|
291
|
+
from hiddifypanel.models import hconfig, ConfigEnum
|
292
|
+
base['lang'] = hconfig(ConfigEnum.lang)
|
274
293
|
return {**base,
|
275
294
|
'last_online': hutils.convert.time_to_json(self.last_online) if convert_date else self.last_online,
|
276
295
|
'usage_limit_GB': self.usage_limit_GB,
|
@@ -278,7 +297,8 @@ class User(BaseAccount, SerializerMixin):
|
|
278
297
|
'mode': self.mode,
|
279
298
|
'start_date': hutils.convert.date_to_json(self.start_date)if convert_date else self.start_date,
|
280
299
|
'current_usage_GB': self.current_usage_GB,
|
281
|
-
'last_reset_time': hutils.convert.
|
300
|
+
'last_reset_time': hutils.convert.time_to_json(self.last_reset_time) if convert_date else self.last_reset_time,
|
301
|
+
# 'expiry_time': hutils.convert.date_to_json(self.expiry_time) if convert_date else self.expiry_time,
|
282
302
|
'added_by_uuid': self.admin.uuid if self.admin else None,
|
283
303
|
'ed25519_private_key': self.ed25519_private_key,
|
284
304
|
'ed25519_public_key': self.ed25519_public_key,
|
@@ -1,7 +1,5 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
import os
|
3
1
|
import urllib.request
|
4
|
-
|
2
|
+
import json
|
5
3
|
from flask_classful import FlaskView, route
|
6
4
|
from flask import render_template, request, redirect, g
|
7
5
|
from hiddifypanel.hutils.flask import hurl_for
|
@@ -62,8 +60,7 @@ class Actions(FlaskView):
|
|
62
60
|
if int(hconfig(ConfigEnum.db_version)) < 9:
|
63
61
|
return ("Please update your panel before this action.")
|
64
62
|
if hutils.node.is_child():
|
65
|
-
|
66
|
-
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
63
|
+
hutils.node.run_node_op_in_bg(hutils.node.child.sync_with_parent)
|
67
64
|
|
68
65
|
domain_changed = request.args.get("domain_changed", str(domain_changed)).lower() == "true"
|
69
66
|
complete_install = request.args.get("complete_install", str(complete_install)).lower() == "true"
|
@@ -81,7 +78,7 @@ class Actions(FlaskView):
|
|
81
78
|
admin_links = f"<h5 >{_('Admin Links')}</h5><ul>"
|
82
79
|
|
83
80
|
admin_links += f"<li><span class='badge badge-danger'>{_('Not Secure')}</span>: <a class='badge ltr share-link' href='{hiddify.get_account_panel_link(g.account, server_ip,is_https=False)}'>{hiddify.get_account_panel_link(g.account, server_ip,is_https=False)}</a></li>"
|
84
|
-
domains =
|
81
|
+
domains = Domain.get_domains()
|
85
82
|
# domains=[*domains,f'{server_ip}.sslip.io']
|
86
83
|
|
87
84
|
for d in domains:
|
@@ -182,12 +179,10 @@ class Actions(FlaskView):
|
|
182
179
|
|
183
180
|
@ login_required(roles={Role.super_admin})
|
184
181
|
def update_usage(self):
|
185
|
-
|
186
|
-
import json
|
187
|
-
|
182
|
+
color = 'white' if g.darkmode else 'black'
|
188
183
|
return render_template("result.html",
|
189
184
|
out_type="info",
|
190
|
-
out_msg=f'<pre class="ltr">{json.dumps(usage.update_local_usage(),indent=2)}</pre>',
|
185
|
+
out_msg=f'<pre class="ltr" style="color:{color};">{json.dumps(usage.update_local_usage(),indent=2)}</pre>',
|
191
186
|
log_file_url=None
|
192
187
|
)
|
193
188
|
|
@@ -197,4 +192,4 @@ def get_log_api_url():
|
|
197
192
|
|
198
193
|
|
199
194
|
def get_domains():
|
200
|
-
return [str(d.domain).replace("*", hutils.random.get_random_string(3, 6)) for d in
|
195
|
+
return [str(d.domain).replace("*", hutils.random.get_random_string(3, 6)) for d in Domain.get_domains(always_add_all_domains=True, always_add_ip=False)]
|
@@ -87,7 +87,7 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
87
87
|
|
88
88
|
def _ul_formatter(view, context, model, name):
|
89
89
|
|
90
|
-
return Markup(" ".join([hiddify.get_html_user_link(model, d) for d in
|
90
|
+
return Markup(" ".join([hiddify.get_html_user_link(model, d) for d in Domain.get_domains()]))
|
91
91
|
|
92
92
|
@property
|
93
93
|
def can_create(self):
|
@@ -216,12 +216,6 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
216
216
|
def on_model_delete(self, model):
|
217
217
|
model.remove()
|
218
218
|
|
219
|
-
def get_query_for_parent_admin(self):
|
220
|
-
# WHAT IS THIS?
|
221
|
-
admin_user_id = self.get_pk_value()
|
222
|
-
sub_admins_ids = set(recursive_sub_admins_ids(AdminUser.query.get(admin_user_id)))
|
223
|
-
return AdminUser.query.filter(AdminUser.id.in_(sub_admins_ids)).with_entities(AdminUser.id, AdminUser.name)
|
224
|
-
|
225
219
|
def on_form_prefill(self, form, id=None):
|
226
220
|
|
227
221
|
if g.account.mode != AdminMode.super_admin:
|
@@ -242,8 +236,8 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
242
236
|
|
243
237
|
def after_model_change(self, form, model, is_created):
|
244
238
|
if hutils.node.is_parent():
|
245
|
-
hutils.node.parent.
|
239
|
+
hutils.node.run_node_op_in_bg(hutils.node.parent.request_childs_to_sync)
|
246
240
|
|
247
241
|
def after_model_delete(self, model):
|
248
242
|
if hutils.node.is_parent():
|
249
|
-
hutils.node.parent.
|
243
|
+
hutils.node.run_node_op_in_bg(hutils.node.parent.request_childs_to_sync)
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
from flask import render_template, request, jsonify, redirect, g
|
1
|
+
from flask import render_template, request, jsonify, g
|
3
2
|
from flask_wtf.file import FileField, FileRequired
|
4
3
|
from flask_bootstrap import SwitchField
|
5
4
|
from flask_babel import gettext as _
|
@@ -25,12 +24,10 @@ class Backup(FlaskView):
|
|
25
24
|
|
26
25
|
# @route("/backupfile")
|
27
26
|
def backupfile(self):
|
28
|
-
response = jsonify(
|
29
|
-
|
30
|
-
)
|
31
|
-
|
32
|
-
domain = o.hostname
|
33
|
-
response.headers.add('Content-disposition', f'attachment; filename=hiddify-{domain}-{datetime.now()}.json')
|
27
|
+
response = jsonify(hiddify.dump_db_to_dict())
|
28
|
+
domain = urlparse(request.base_url).hostname
|
29
|
+
filename = f'hiddify-{domain}-{datetime.now()}.json'
|
30
|
+
response.headers.add('Content-disposition', f'attachment; filename={filename}')
|
34
31
|
|
35
32
|
return response
|
36
33
|
|
@@ -21,10 +21,9 @@ class Dashboard(FlaskView):
|
|
21
21
|
if hconfig(ConfigEnum.first_setup):
|
22
22
|
return redirect(hurl_for("admin.QuickSetup:index"))
|
23
23
|
|
24
|
-
if
|
24
|
+
if hutils.utils.is_panel_outdated():
|
25
25
|
hutils.flask.flash(_('This version of hiddify panel is outdated. Please update it from admin area.'), "danger") # type: ignore
|
26
|
-
|
27
|
-
# if hconfig(ConfigEnum.license):
|
26
|
+
|
28
27
|
childs = None
|
29
28
|
admin_id = request.args.get("admin_id") or g.account.id
|
30
29
|
if admin_id not in g.account.recursive_sub_admins_ids():
|
@@ -44,7 +43,7 @@ class Dashboard(FlaskView):
|
|
44
43
|
c.is_active = True
|
45
44
|
|
46
45
|
def_user = None if len(User.query.all()) > 1 else User.query.filter(User.name == 'default').first()
|
47
|
-
domains =
|
46
|
+
domains = Domain.get_domains()
|
48
47
|
sslip_domains = [d.domain for d in domains if "sslip.io" in d.domain]
|
49
48
|
|
50
49
|
if def_user and sslip_domains:
|
@@ -12,7 +12,7 @@ from hiddifypanel.panel.run_commander import Command, commander
|
|
12
12
|
from wtforms.validators import Regexp, ValidationError
|
13
13
|
|
14
14
|
from hiddifypanel.models import *
|
15
|
-
from hiddifypanel.panel import hiddify,
|
15
|
+
from hiddifypanel.panel import hiddify, custom_widgets
|
16
16
|
from .adminlte import AdminLTEModelView
|
17
17
|
from hiddifypanel import hutils
|
18
18
|
|
@@ -110,6 +110,9 @@ class DomainAdmin(AdminLTEModelView):
|
|
110
110
|
|
111
111
|
def _domain_ip(view, context, model, name):
|
112
112
|
dip = hutils.network.get_domain_ip(model.domain)
|
113
|
+
# The get_domain_ip function uses the socket library, which relies on the system DNS resolver. So it may sometimes use cached data, which is not desirable
|
114
|
+
if not dip:
|
115
|
+
dip = hutils.network.resolve_domain_with_api(model.domain)
|
113
116
|
myip = hutils.network.get_ip(4)
|
114
117
|
if myip == dip and model.mode == DomainType.direct:
|
115
118
|
badge_type = ''
|
@@ -144,6 +147,7 @@ class DomainAdmin(AdminLTEModelView):
|
|
144
147
|
# Pre-select the related domains in the checkbox list
|
145
148
|
# form.show_domains = [d.id for d in Domain.query.all()]
|
146
149
|
|
150
|
+
# TODO: refactor this function
|
147
151
|
def on_model_change(self, form, model, is_created):
|
148
152
|
model.domain = model.domain.lower()
|
149
153
|
|
@@ -171,16 +175,16 @@ class DomainAdmin(AdminLTEModelView):
|
|
171
175
|
raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
|
172
176
|
|
173
177
|
skip_check = "*" in model.domain
|
174
|
-
if hconfig(ConfigEnum.cloudflare) and model.mode
|
178
|
+
if hconfig(ConfigEnum.cloudflare) and model.mode not in [DomainType.fake, DomainType.relay, DomainType.reality]:
|
175
179
|
try:
|
176
180
|
proxied = model.mode in [DomainType.cdn, DomainType.auto_cdn_ip]
|
177
|
-
cf_api.
|
181
|
+
hutils.network.cf_api.add_or_update_dns_record(model.domain, str(ipv4_list[0]), "A", proxied=proxied)
|
178
182
|
if ipv6_list:
|
179
|
-
cf_api.
|
183
|
+
hutils.network.cf_api.add_or_update_dns_record(model.domain, str(ipv6_list[0]), "AAAA", proxied=proxied)
|
180
184
|
|
181
185
|
skip_check = True
|
182
186
|
except Exception as e:
|
183
|
-
raise ValidationError(__("
|
187
|
+
raise ValidationError(__("cloudflare.error") + f' {e}')
|
184
188
|
# elif model.mode==DomainType.auto_cdn_ip:
|
185
189
|
# if model.alias and not model.alias.replace("_", "").isalnum():
|
186
190
|
# hutils.flask.flash(__("Using alias with special charachters may cause problem in some clients like FairVPN."), 'warning')
|
@@ -233,19 +237,20 @@ class DomainAdmin(AdminLTEModelView):
|
|
233
237
|
for d in v.split(","):
|
234
238
|
if not d:
|
235
239
|
continue
|
236
|
-
if not hutils.network.is_domain_reality_friendly(d):
|
240
|
+
if not hutils.network.is_domain_reality_friendly(d): # the minimum requirement for the REALITY protocol is to have tls1.3 and h2
|
237
241
|
raise ValidationError(_("Domain is not REALITY friendly!") + f' {d}')
|
238
242
|
|
239
243
|
if not hutils.network.is_in_same_asn(d, ipv4_list[0]):
|
240
|
-
server_asn = hutils.network.
|
241
|
-
domain_asn = hutils.network.
|
244
|
+
server_asn = hutils.network.get_ip_asn(ipv4_list[0])
|
245
|
+
domain_asn = hutils.network.get_ip_asn(dip) # type: ignore
|
242
246
|
msg = _("selected domain for REALITY is not in the same ASN. To better use of the protocol, it is better to find a domain in the same ASN.") + \
|
243
247
|
(f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}" if server_asn or domain_asn else "")
|
244
248
|
hutils.flask.flash(msg, 'warning')
|
245
249
|
|
246
250
|
for d in model.servernames.split(","):
|
247
251
|
if not hutils.network.fallback_domain_compatible_with_servernames(model.domain, d):
|
248
|
-
|
252
|
+
msg = _("REALITY Fallback domain is not compaitble with server names!") + f' {d} != {model.domain}'
|
253
|
+
hutils.flask.flash(msg, 'warning')
|
249
254
|
|
250
255
|
if (model.cdn_ip):
|
251
256
|
try:
|
@@ -253,7 +258,7 @@ class DomainAdmin(AdminLTEModelView):
|
|
253
258
|
except BaseException:
|
254
259
|
raise ValidationError(_("Error in auto cdn format"))
|
255
260
|
|
256
|
-
old_db_domain =
|
261
|
+
old_db_domain = Domain.by_domain(model.domain)
|
257
262
|
if is_created or not old_db_domain or old_db_domain.mode != model.mode:
|
258
263
|
# return hiddify.reinstall_action(complete_install=False, domain_changed=True)
|
259
264
|
hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=True)
|
@@ -265,15 +270,16 @@ class DomainAdmin(AdminLTEModelView):
|
|
265
270
|
def on_model_delete(self, model):
|
266
271
|
if len(Domain.query.all()) <= 1:
|
267
272
|
raise ValidationError(f"at least one domain should exist")
|
268
|
-
|
273
|
+
if hconfig(ConfigEnum.cloudflare) and model.mode not in [DomainType.fake, DomainType.reality, DomainType.relay]:
|
274
|
+
if not hutils.network.cf_api.delete_dns_record(model.domain):
|
275
|
+
hutils.flask.flash(_('cf-delete.failed'), 'warning') # type: ignore
|
269
276
|
model.showed_by_domains = []
|
270
277
|
# db.session.commit()
|
271
278
|
hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=True)
|
272
279
|
|
273
280
|
def after_model_delete(self, model):
|
274
281
|
if hutils.node.is_child():
|
275
|
-
|
276
|
-
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
282
|
+
hutils.node.run_node_op_in_bg(hutils.node.child.sync_with_parent, *[hutils.node.child.SyncFields.domains])
|
277
283
|
|
278
284
|
def after_model_change(self, form, model, is_created):
|
279
285
|
if hconfig(ConfigEnum.first_setup):
|
@@ -281,8 +287,7 @@ class DomainAdmin(AdminLTEModelView):
|
|
281
287
|
if model.need_valid_ssl:
|
282
288
|
commander(Command.get_cert, domain=model.domain)
|
283
289
|
if hutils.node.is_child():
|
284
|
-
|
285
|
-
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
290
|
+
hutils.node.run_node_op_in_bg(hutils.node.child.sync_with_parent, *[hutils.node.child.SyncFields.domains])
|
286
291
|
|
287
292
|
def is_accessible(self):
|
288
293
|
if login_required(roles={Role.super_admin, Role.admin})(lambda: True)() != True:
|
@@ -36,6 +36,8 @@ class ProxyAdmin(FlaskView):
|
|
36
36
|
db.session.commit()
|
37
37
|
# print(cat,vs)
|
38
38
|
hutils.proxy.get_proxies.invalidate_all()
|
39
|
+
if hutils.node.is_child():
|
40
|
+
hutils.node.run_node_op_in_bg(hutils.node.child.sync_with_parent, *[hutils.node.child.SyncFields.hconfigs])
|
39
41
|
hiddify.check_need_reset(old_configs)
|
40
42
|
all_proxy_form = get_all_proxy_form(True)
|
41
43
|
|
@@ -57,8 +59,7 @@ class ProxyAdmin(FlaskView):
|
|
57
59
|
db.session.commit()
|
58
60
|
hutils.proxy.get_proxies.invalidate_all()
|
59
61
|
if hutils.node.is_child():
|
60
|
-
|
61
|
-
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
62
|
+
hutils.node.run_node_op_in_bg(hutils.node.child.sync_with_parent, *[hutils.node.child.SyncFields.proxies])
|
62
63
|
hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=False)
|
63
64
|
global_config_form = get_global_config_form(True)
|
64
65
|
else:
|
@@ -66,14 +67,6 @@ class ProxyAdmin(FlaskView):
|
|
66
67
|
|
67
68
|
return render_template('proxy.html', global_config_form=global_config_form, detailed_config_form=all_proxy_form)
|
68
69
|
|
69
|
-
import flask_babel
|
70
|
-
|
71
|
-
# form=HelloForm()
|
72
|
-
# # return render('config.html',form=form)
|
73
|
-
# return render_template('config.html',form=HelloForm())
|
74
|
-
form = get_config_form()
|
75
|
-
return render_template('config.html', form=form)
|
76
|
-
|
77
70
|
|
78
71
|
def get_global_config_form(empty=False):
|
79
72
|
boolconfigs = BoolConfig.query.all()
|