hiddifypanel 10.14.0__py3-none-any.whl → 10.15.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/auth.py +15 -4
- hiddifypanel/base.py +11 -3
- hiddifypanel/cache.py +43 -25
- hiddifypanel/database.py +9 -0
- hiddifypanel/drivers/singbox_api.py +2 -14
- hiddifypanel/drivers/xray_api.py +0 -4
- hiddifypanel/hutils/__init__.py +1 -0
- hiddifypanel/hutils/convert.py +13 -2
- hiddifypanel/hutils/crypto.py +21 -2
- hiddifypanel/hutils/flask.py +18 -4
- hiddifypanel/hutils/importer/xui.py +5 -2
- hiddifypanel/hutils/node/__init__.py +3 -0
- hiddifypanel/hutils/node/api_client.py +76 -0
- hiddifypanel/hutils/node/child.py +147 -0
- hiddifypanel/hutils/node/parent.py +100 -0
- hiddifypanel/hutils/node/shared.py +65 -0
- hiddifypanel/hutils/proxy/shared.py +15 -3
- hiddifypanel/models/__init__.py +2 -2
- hiddifypanel/models/admin.py +14 -2
- hiddifypanel/models/base_account.py +3 -3
- hiddifypanel/models/child.py +30 -16
- hiddifypanel/models/config.py +39 -15
- hiddifypanel/models/config_enum.py +55 -8
- hiddifypanel/models/domain.py +28 -20
- hiddifypanel/models/parent_domain.py +2 -2
- hiddifypanel/models/proxy.py +13 -4
- hiddifypanel/models/report.py +2 -3
- hiddifypanel/models/usage.py +2 -2
- hiddifypanel/models/user.py +13 -4
- hiddifypanel/panel/admin/Actions.py +4 -6
- hiddifypanel/panel/admin/AdminstratorAdmin.py +13 -2
- hiddifypanel/panel/admin/Dashboard.py +5 -10
- hiddifypanel/panel/admin/DomainAdmin.py +12 -11
- hiddifypanel/panel/admin/NodeAdmin.py +6 -2
- hiddifypanel/panel/admin/ProxyAdmin.py +4 -3
- hiddifypanel/panel/admin/SettingAdmin.py +60 -21
- hiddifypanel/panel/admin/UserAdmin.py +10 -2
- hiddifypanel/panel/admin/templates/index.html +1 -1
- hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
- hiddifypanel/panel/cli.py +16 -16
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +10 -5
- hiddifypanel/panel/commercial/__init__.py +7 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -35
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
- hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
- hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
- hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -73
- hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
- hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
- hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
- hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
- hiddifypanel/panel/common.py +21 -6
- hiddifypanel/panel/hiddify.py +9 -80
- hiddifypanel/panel/init_db.py +43 -12
- hiddifypanel/panel/usage.py +28 -15
- hiddifypanel/panel/user/templates/home/usage.html +1 -1
- hiddifypanel/panel/user/templates/new.html +1 -1
- hiddifypanel/static/css/custom.css +13 -0
- hiddifypanel/static/images/hiddify.png +0 -0
- hiddifypanel/static/new/assets/{index-bce9b1a6.js → index-ccb9873c.js} +65 -65
- hiddifypanel/templates/admin-layout.html +24 -40
- hiddifypanel/templates/fake.html +298 -0
- hiddifypanel/templates/master.html +23 -41
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +90 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +91 -1
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +98 -6
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +90 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +92 -2
- hiddifypanel/translations.i18n/en.json +56 -0
- hiddifypanel/translations.i18n/fa.json +57 -1
- hiddifypanel/translations.i18n/pt.json +63 -7
- hiddifypanel/translations.i18n/ru.json +56 -0
- hiddifypanel/translations.i18n/zh.json +58 -2
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/METADATA +47 -47
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/RECORD +104 -86
- hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
- hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
- hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
- /hiddifypanel/static/images/{hiddify1.png → hiddify-old.png} +0 -0
- /hiddifypanel/static/{new/assets/hiddify-logo-noroz-559c8dcb.png → images/hiddify2.png} +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/top_level.txt +0 -0
hiddifypanel/models/domain.py
CHANGED
@@ -4,7 +4,7 @@ from typing import List
|
|
4
4
|
from urllib.parse import urlparse
|
5
5
|
|
6
6
|
from flask import request
|
7
|
-
|
7
|
+
|
8
8
|
from flask_babel import lazy_gettext as _
|
9
9
|
from sqlalchemy.orm import backref
|
10
10
|
from urllib.parse import urlparse
|
@@ -40,7 +40,7 @@ ShowDomain = db.Table('show_domain',
|
|
40
40
|
)
|
41
41
|
|
42
42
|
|
43
|
-
class Domain(db.Model
|
43
|
+
class Domain(db.Model):
|
44
44
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
45
45
|
child_id = db.Column(db.Integer, db.ForeignKey('child.id'), default=0)
|
46
46
|
domain = db.Column(db.String(200), nullable=False, unique=False)
|
@@ -67,7 +67,7 @@ class Domain(db.Model, SerializerMixin):
|
|
67
67
|
'domain': self.domain.lower(),
|
68
68
|
'mode': self.mode,
|
69
69
|
'alias': self.alias,
|
70
|
-
|
70
|
+
'sub_link_only': self.sub_link_only,
|
71
71
|
'child_unique_id': self.child.unique_id if self.child else '',
|
72
72
|
'cdn_ip': self.cdn_ip,
|
73
73
|
'servernames': self.servernames,
|
@@ -84,6 +84,15 @@ class Domain(db.Model, SerializerMixin):
|
|
84
84
|
|
85
85
|
return data
|
86
86
|
|
87
|
+
@staticmethod
|
88
|
+
def from_schema(schema):
|
89
|
+
return schema.dump(Domain())
|
90
|
+
|
91
|
+
def to_schema(self):
|
92
|
+
domain_dict = self.to_dict()
|
93
|
+
from hiddifypanel.panel.commercial.restapi.v2.parent.schema import DomainSchema
|
94
|
+
return DomainSchema().load(domain_dict)
|
95
|
+
|
87
96
|
@property
|
88
97
|
def need_valid_ssl(self):
|
89
98
|
return self.mode in [DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]
|
@@ -137,14 +146,22 @@ def get_domain(domain):
|
|
137
146
|
return Domain.query.filter(Domain.domain == domain).first()
|
138
147
|
|
139
148
|
|
149
|
+
def get_panel_link(child_id: int | None = None) -> Domain | None:
|
150
|
+
if child_id is None:
|
151
|
+
child_id = Child.current().id
|
152
|
+
domains = Domain.query.filter(Domain.mode.in_(
|
153
|
+
[DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]),
|
154
|
+
Domain.child_id == child_id
|
155
|
+
).all()
|
156
|
+
if not domains:
|
157
|
+
return None
|
158
|
+
return domains[0]
|
159
|
+
|
160
|
+
|
140
161
|
def get_panel_domains(always_add_ip=False, always_add_all_domains=False) -> List[Domain]:
|
141
162
|
from hiddifypanel import hutils
|
142
163
|
domains = []
|
143
|
-
|
144
|
-
# from .parent_domain import ParentDomain
|
145
|
-
# domains = ParentDomain.query.all()
|
146
|
-
# else:
|
147
|
-
domains = Domain.query.filter(Domain.mode == DomainType.sub_link_only, Domain.child_id == Child.current.id).all()
|
164
|
+
domains = Domain.query.filter(Domain.mode == DomainType.sub_link_only, Domain.child_id == Child.current().id).all()
|
148
165
|
if not len(domains) or always_add_all_domains:
|
149
166
|
domains = Domain.query.filter(Domain.mode.notin_([DomainType.fake, DomainType.reality])).all()
|
150
167
|
|
@@ -156,11 +173,7 @@ def get_panel_domains(always_add_ip=False, always_add_all_domains=False) -> List
|
|
156
173
|
|
157
174
|
|
158
175
|
def get_proxy_domains(domain):
|
159
|
-
|
160
|
-
# from hiddifypanel.models.parent_domain import ParentDomain
|
161
|
-
# db_domain = ParentDomain.query.filter(ParentDomain.domain == domain).first() or ParentDomain(domain=domain, show_domains=[])
|
162
|
-
# else:
|
163
|
-
db_domain = Domain.query.filter(Domain.domain == domain, Domain.child_id == Child.current.id).first()
|
176
|
+
db_domain = Domain.query.filter(Domain.domain == domain, Domain.child_id == Child.current().id).first()
|
164
177
|
if not db_domain:
|
165
178
|
db_domain = Domain(domain=domain, mode=DomainType.direct, cdn_ip='', show_domains=[])
|
166
179
|
return get_proxy_domains_db(db_domain)
|
@@ -202,11 +215,11 @@ def add_or_update_domain(commit=True, child_id=0, **domain):
|
|
202
215
|
db.session.commit()
|
203
216
|
|
204
217
|
|
205
|
-
def bulk_register_domains(domains, commit=True, remove=False,
|
218
|
+
def bulk_register_domains(domains, commit=True, remove=False, force_child_unique_id: str | None = None):
|
206
219
|
from hiddifypanel.panel import hiddify
|
207
220
|
child_ids = {}
|
208
221
|
for domain in domains:
|
209
|
-
child_id = hiddify.get_child(unique_id=
|
222
|
+
child_id = hiddify.get_child(unique_id=force_child_unique_id)
|
210
223
|
child_ids[child_id] = 1
|
211
224
|
add_or_update_domain(commit=False, child_id=child_id, **domain)
|
212
225
|
if remove and len(child_ids):
|
@@ -215,10 +228,5 @@ def bulk_register_domains(domains, commit=True, remove=False, override_child_uni
|
|
215
228
|
if d.domain not in dd:
|
216
229
|
db.session.delete(d)
|
217
230
|
|
218
|
-
# if commit:
|
219
|
-
db.session.commit()
|
220
|
-
for domain in domains:
|
221
|
-
child_id = hiddify.get_child(unique_id=None)
|
222
|
-
add_or_update_domain(commit=False, child_id=child_id, **domain)
|
223
231
|
if commit:
|
224
232
|
db.session.commit()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# from hiddifypanel.models.domain import Domain
|
2
2
|
# from sqlalchemy.orm import backref
|
3
|
-
#
|
3
|
+
#
|
4
4
|
|
5
5
|
# from hiddifypanel.database import db
|
6
6
|
|
@@ -10,7 +10,7 @@
|
|
10
10
|
# )
|
11
11
|
|
12
12
|
|
13
|
-
# class ParentDomain(db.Model
|
13
|
+
# class ParentDomain(db.Model):
|
14
14
|
# id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
15
15
|
# domain = db.Column(db.String(200), nullable=False, unique=True)
|
16
16
|
# alias = db.Column(db.String(200), nullable=False, unique=False)
|
hiddifypanel/models/proxy.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
from strenum import StrEnum
|
3
3
|
from enum import auto
|
4
4
|
from sqlalchemy import Column, String, Integer, Boolean, Enum, ForeignKey
|
@@ -57,7 +57,7 @@ class ProxyL3(StrEnum):
|
|
57
57
|
custom = auto()
|
58
58
|
|
59
59
|
|
60
|
-
class Proxy(db.Model
|
60
|
+
class Proxy(db.Model): # type: ignore
|
61
61
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
62
62
|
child_id = Column(Integer, ForeignKey('child.id'), default=0)
|
63
63
|
name = Column(String(200), nullable=False, unique=False)
|
@@ -102,10 +102,19 @@ class Proxy(db.Model, SerializerMixin): # type: ignore
|
|
102
102
|
db.session.commit() # type: ignore
|
103
103
|
|
104
104
|
@staticmethod
|
105
|
-
def
|
105
|
+
def from_schema(schema):
|
106
|
+
return schema.dump(Proxy())
|
107
|
+
|
108
|
+
def to_schema(self):
|
109
|
+
proxy_dict = self.to_dict()
|
110
|
+
from hiddifypanel.panel.commercial.restapi.v2.parent.schema import ProxySchema
|
111
|
+
return ProxySchema().load(proxy_dict)
|
112
|
+
|
113
|
+
@staticmethod
|
114
|
+
def bulk_register(proxies, commit=True, force_child_unique_id: str | None = None):
|
106
115
|
from hiddifypanel.panel import hiddify
|
107
116
|
for proxy in proxies:
|
108
|
-
child_id = hiddify.get_child(unique_id=
|
117
|
+
child_id = hiddify.get_child(unique_id=force_child_unique_id)
|
109
118
|
Proxy.add_or_update(commit=False, child_id=child_id, **proxy)
|
110
119
|
if commit:
|
111
120
|
db.session.commit() # type: ignore
|
hiddifypanel/models/report.py
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
import datetime
|
2
2
|
|
3
|
-
from sqlalchemy_serializer import SerializerMixin
|
4
3
|
|
5
4
|
from hiddifypanel.database import db
|
6
5
|
|
7
6
|
|
8
|
-
class Report(db.Model
|
7
|
+
class Report(db.Model):
|
9
8
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
10
9
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), default=0, nullable=False)
|
11
10
|
asn_id = db.Column(db.String(200), nullable=False, unique=False)
|
@@ -19,7 +18,7 @@ class Report(db.Model, SerializerMixin):
|
|
19
18
|
details = db.relationship('ReportDetail', cascade="all,delete", backref='report', lazy='dynamic',)
|
20
19
|
|
21
20
|
|
22
|
-
class ReportDetail(db.Model
|
21
|
+
class ReportDetail(db.Model):
|
23
22
|
report_id = db.Column(db.Integer, db.ForeignKey('report.id'), primary_key=True, )
|
24
23
|
proxy_id = db.Column(db.Integer, db.ForeignKey('proxy.id'), primary_key=True, )
|
25
24
|
ping = db.Column(db.Integer, default=-1)
|
hiddifypanel/models/usage.py
CHANGED
@@ -3,12 +3,12 @@ from datetime import timedelta, date
|
|
3
3
|
|
4
4
|
from flask import g
|
5
5
|
from sqlalchemy import func
|
6
|
-
|
6
|
+
|
7
7
|
|
8
8
|
from hiddifypanel.database import db
|
9
9
|
|
10
10
|
|
11
|
-
class DailyUsage(db.Model
|
11
|
+
class DailyUsage(db.Model):
|
12
12
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
13
13
|
date = db.Column(db.Date, default=datetime.date.today(), index=True)
|
14
14
|
usage = db.Column(db.BigInteger, default=0, nullable=False)
|
hiddifypanel/models/user.py
CHANGED
@@ -2,7 +2,7 @@ import datetime
|
|
2
2
|
from enum import auto
|
3
3
|
from hiddifypanel.models.role import Role
|
4
4
|
from dateutil import relativedelta
|
5
|
-
|
5
|
+
|
6
6
|
from strenum import StrEnum
|
7
7
|
from sqlalchemy import event
|
8
8
|
|
@@ -35,7 +35,7 @@ package_mode_dic = {
|
|
35
35
|
}
|
36
36
|
|
37
37
|
|
38
|
-
class UserDetail(db.Model
|
38
|
+
class UserDetail(db.Model):
|
39
39
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
40
40
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), default=0, nullable=False)
|
41
41
|
child_id = db.Column(db.Integer, db.ForeignKey('child.id'), default=0, nullable=False)
|
@@ -121,12 +121,12 @@ class User(BaseAccount):
|
|
121
121
|
is_active = False
|
122
122
|
elif self.remaining_days < 0:
|
123
123
|
is_active = False
|
124
|
-
elif len(self.
|
124
|
+
elif len(self.devices) > max(3, self.max_ips):
|
125
125
|
is_active = False
|
126
126
|
return is_active
|
127
127
|
|
128
128
|
@property
|
129
|
-
def
|
129
|
+
def devices(self):
|
130
130
|
res = {}
|
131
131
|
for detail in UserDetail.query.filter(UserDetail.user_id == self.id):
|
132
132
|
for device in detail.devices:
|
@@ -256,6 +256,15 @@ class User(BaseAccount):
|
|
256
256
|
db.session.commit()
|
257
257
|
return dbuser
|
258
258
|
|
259
|
+
@staticmethod
|
260
|
+
def form_schema(schema):
|
261
|
+
return schema.dump(User())
|
262
|
+
|
263
|
+
def to_schema(self):
|
264
|
+
user_dict = self.to_dict()
|
265
|
+
from hiddifypanel.panel.commercial.restapi.v2.admin.user_api import UserSchema
|
266
|
+
return UserSchema().load(user_dict)
|
267
|
+
|
259
268
|
def to_dict(self, convert_date=True, dump_id=False) -> dict:
|
260
269
|
base = super().to_dict()
|
261
270
|
from hiddifypanel import hutils
|
@@ -61,11 +61,9 @@ class Actions(FlaskView):
|
|
61
61
|
def reinstall2(self, complete_install=True, domain_changed=False):
|
62
62
|
if int(hconfig(ConfigEnum.db_version)) < 9:
|
63
63
|
return ("Please update your panel before this action.")
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# except e as Exception:
|
68
|
-
# hutils.flask.flash(_('can not sync child with parent panel')+" "+e)
|
64
|
+
if hutils.node.is_child():
|
65
|
+
if not hutils.node.child.sync_with_parent():
|
66
|
+
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
69
67
|
|
70
68
|
domain_changed = request.args.get("domain_changed", str(domain_changed)).lower() == "true"
|
71
69
|
complete_install = request.args.get("complete_install", str(complete_install)).lower() == "true"
|
@@ -110,7 +108,7 @@ class Actions(FlaskView):
|
|
110
108
|
|
111
109
|
@login_required(roles={Role.super_admin})
|
112
110
|
def change_reality_keys(self):
|
113
|
-
key =
|
111
|
+
key = hutils.crypto.generate_x25519_keys()
|
114
112
|
set_hconfig(ConfigEnum.reality_private_key, key['private_key'])
|
115
113
|
set_hconfig(ConfigEnum.reality_public_key, key['public_key'])
|
116
114
|
hutils.flask.flash_config_success(restart_mode=ApplyMode.restart, domain_changed=False)
|
@@ -7,7 +7,9 @@ from .adminlte import AdminLTEModelView
|
|
7
7
|
from flask_babel import lazy_gettext as _
|
8
8
|
from wtforms.validators import Regexp
|
9
9
|
from flask_babel import gettext as __
|
10
|
-
from flask import
|
10
|
+
from flask import request # type: ignore
|
11
|
+
from markupsafe import Markup
|
12
|
+
|
11
13
|
from flask import g
|
12
14
|
import datetime
|
13
15
|
from wtforms import SelectField
|
@@ -37,7 +39,7 @@ class SubAdminsField(SelectField):
|
|
37
39
|
|
38
40
|
class AdminstratorAdmin(AdminLTEModelView):
|
39
41
|
column_hide_backrefs = False
|
40
|
-
column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment']
|
42
|
+
column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment',]
|
41
43
|
form_columns = ["name", 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'comment', "uuid"]
|
42
44
|
list_template = 'model/admin_list.html'
|
43
45
|
# column_editable_list = ['name']
|
@@ -215,6 +217,7 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
215
217
|
model.remove()
|
216
218
|
|
217
219
|
def get_query_for_parent_admin(self):
|
220
|
+
# WHAT IS THIS?
|
218
221
|
admin_user_id = self.get_pk_value()
|
219
222
|
sub_admins_ids = set(recursive_sub_admins_ids(AdminUser.query.get(admin_user_id)))
|
220
223
|
return AdminUser.query.filter(AdminUser.id.in_(sub_admins_ids)).with_entities(AdminUser.id, AdminUser.name)
|
@@ -236,3 +239,11 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
236
239
|
del form.max_users
|
237
240
|
del form.max_active_users
|
238
241
|
del form.can_add_admin
|
242
|
+
|
243
|
+
def after_model_change(self, form, model, is_created):
|
244
|
+
if hutils.node.is_parent():
|
245
|
+
hutils.node.parent.send_sync_req_to_childs()
|
246
|
+
|
247
|
+
def after_model_delete(self, model):
|
248
|
+
if hutils.node.is_parent():
|
249
|
+
hutils.node.parent.send_sync_req_to_childs()
|
@@ -18,9 +18,9 @@ class Dashboard(FlaskView):
|
|
18
18
|
|
19
19
|
@login_required(roles={Role.super_admin, Role.admin, Role.agent})
|
20
20
|
def index(self):
|
21
|
-
|
22
21
|
if hconfig(ConfigEnum.first_setup):
|
23
22
|
return redirect(hurl_for("admin.QuickSetup:index"))
|
23
|
+
|
24
24
|
if hiddifypanel.__release_date__ + datetime.timedelta(days=20) < datetime.datetime.now():
|
25
25
|
hutils.flask.flash(_('This version of hiddify panel is outdated. Please update it from admin area.'), "danger") # type: ignore
|
26
26
|
bot = None
|
@@ -34,20 +34,15 @@ class Dashboard(FlaskView):
|
|
34
34
|
user_query = User.query
|
35
35
|
if admin_id:
|
36
36
|
user_query = user_query.filter(User.added_by == admin_id)
|
37
|
-
if
|
37
|
+
if hutils.node.is_parent():
|
38
38
|
childs = Child.query.filter(Child.id != 0).all()
|
39
39
|
for c in childs:
|
40
40
|
c.is_active = False
|
41
41
|
for d in c.domains:
|
42
|
-
|
43
|
-
continue
|
44
|
-
remote = hiddify.get_account_panel_link(g.account, d.domain, child_id=c.id)
|
45
|
-
d.is_active = hutils.network.check_connection_to_remote(remote)
|
42
|
+
d.is_active = hutils.node.parent.is_child_domain_active(c, d)
|
46
43
|
if d.is_active:
|
47
44
|
c.is_active = True
|
48
45
|
|
49
|
-
# return render_template('parent_dash.html',childs=childs,bot=bot)
|
50
|
-
# try:
|
51
46
|
def_user = None if len(User.query.all()) > 1 else User.query.filter(User.name == 'default').first()
|
52
47
|
domains = get_panel_domains()
|
53
48
|
sslip_domains = [d.domain for d in domains if "sslip.io" in d.domain]
|
@@ -56,13 +51,13 @@ class Dashboard(FlaskView):
|
|
56
51
|
quick_setup = hurl_for("admin.QuickSetup:index")
|
57
52
|
hutils.flask.flash((_('It seems that you have not setup the system completely. <a class="btn btn-success" href="%(quick_setup)s">Click here</a> to complete setup.',
|
58
53
|
quick_setup=quick_setup)), 'warning') # type: ignore
|
59
|
-
if
|
54
|
+
if hutils.node.is_parent():
|
60
55
|
hutils.flask.flash(
|
61
56
|
_("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
|
62
57
|
elif len(sslip_domains):
|
63
58
|
hutils.flask.flash((_('It seems that you are using default domain (%(domain)s) which is not recommended.',
|
64
59
|
domain=sslip_domains[0])), 'warning') # type: ignore
|
65
|
-
if
|
60
|
+
if hutils.node.is_parent():
|
66
61
|
hutils.flask.flash(
|
67
62
|
_("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
|
68
63
|
elif def_user:
|
@@ -3,7 +3,9 @@ from hiddifypanel.auth import login_required, current_account
|
|
3
3
|
|
4
4
|
from hiddifypanel.models import *
|
5
5
|
import re
|
6
|
-
from flask import
|
6
|
+
from flask import g # type: ignore
|
7
|
+
from markupsafe import Markup
|
8
|
+
|
7
9
|
from flask_babel import gettext as __
|
8
10
|
from flask_babel import lazy_gettext as _
|
9
11
|
from hiddifypanel.panel.run_commander import Command, commander
|
@@ -229,7 +231,7 @@ class DomainAdmin(AdminLTEModelView):
|
|
229
231
|
if not d:
|
230
232
|
continue
|
231
233
|
if not hutils.network.is_domain_reality_friendly(d):
|
232
|
-
raise ValidationError(_("Domain is not REALITY friendly!")+f' {d}')
|
234
|
+
raise ValidationError(_("Domain is not REALITY friendly!") + f' {d}')
|
233
235
|
|
234
236
|
if not hutils.network.is_in_same_asn(d, ipv4_list[0]):
|
235
237
|
server_asn = hutils.network.get_ip_asn_name(ipv4_list[0])
|
@@ -240,7 +242,7 @@ class DomainAdmin(AdminLTEModelView):
|
|
240
242
|
|
241
243
|
for d in model.servernames.split(","):
|
242
244
|
if not hutils.network.fallback_domain_compatible_with_servernames(model.domain, d):
|
243
|
-
raise ValidationError(_("REALITY Fallback domain is not compaitble with server names!")+f' {d} != {model.domain}')
|
245
|
+
raise ValidationError(_("REALITY Fallback domain is not compaitble with server names!") + f' {d} != {model.domain}')
|
244
246
|
|
245
247
|
if (model.cdn_ip):
|
246
248
|
try:
|
@@ -266,19 +268,18 @@ class DomainAdmin(AdminLTEModelView):
|
|
266
268
|
hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=True)
|
267
269
|
|
268
270
|
def after_model_delete(self, model):
|
269
|
-
|
270
|
-
|
271
|
-
|
271
|
+
if hutils.node.is_child():
|
272
|
+
if not hutils.node.child.sync_with_parent():
|
273
|
+
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
272
274
|
|
273
275
|
def after_model_change(self, form, model, is_created):
|
274
276
|
if hconfig(ConfigEnum.first_setup):
|
275
277
|
set_hconfig(ConfigEnum.first_setup, False)
|
276
|
-
# if hconfig(ConfigEnum.parent_panel):
|
277
|
-
# hiddify_api.sync_child_to_parent()
|
278
278
|
if model.need_valid_ssl:
|
279
|
-
# hiddify.exec_command(f"sudo /opt/hiddify-manager/acme.sh/get_cert.sh {model.domain}")
|
280
|
-
# run get_cert.sh
|
281
279
|
commander(Command.get_cert, domain=model.domain)
|
280
|
+
if hutils.node.is_child():
|
281
|
+
if not hutils.node.child.sync_with_parent():
|
282
|
+
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
282
283
|
|
283
284
|
def is_accessible(self):
|
284
285
|
if login_required(roles={Role.super_admin, Role.admin})(lambda: True)() != True:
|
@@ -296,4 +297,4 @@ class DomainAdmin(AdminLTEModelView):
|
|
296
297
|
|
297
298
|
def get_query(self):
|
298
299
|
query = super().get_query()
|
299
|
-
return query.filter(Domain.child_id == Child.current.id)
|
300
|
+
return query.filter(Domain.child_id == Child.current().id)
|
@@ -4,7 +4,9 @@ from wtforms.validators import Regexp, ValidationError
|
|
4
4
|
from flask_babel import lazy_gettext as _
|
5
5
|
from .adminlte import AdminLTEModelView
|
6
6
|
from flask_babel import gettext as __
|
7
|
-
from flask import
|
7
|
+
from flask import g, request
|
8
|
+
from markupsafe import Markup
|
9
|
+
|
8
10
|
|
9
11
|
from hiddifypanel.auth import login_required
|
10
12
|
from hiddifypanel.panel import hiddify
|
@@ -40,7 +42,7 @@ class NodeAdmin(AdminLTEModelView):
|
|
40
42
|
def is_accessible(self):
|
41
43
|
if login_required(roles={Role.super_admin})(lambda: True)() != True:
|
42
44
|
return False
|
43
|
-
if Child.current.id != 0:
|
45
|
+
if Child.current().id != 0:
|
44
46
|
return False
|
45
47
|
return True
|
46
48
|
|
@@ -49,7 +51,9 @@ class NodeAdmin(AdminLTEModelView):
|
|
49
51
|
raise ValidationError(_("Remote nodes are not supported yet!"))
|
50
52
|
|
51
53
|
def after_model_change(self, form, model, is_created):
|
54
|
+
# deprecated
|
52
55
|
set_hconfig(ConfigEnum.is_parent, True)
|
56
|
+
set_hconfig(ConfigEnum.panel_mode, PanelMode.parent)
|
53
57
|
if is_created and model.mode == ChildMode.virtual:
|
54
58
|
# for k, v in get_hconfigs().items():
|
55
59
|
# set_hconfig(k, v, model.id)
|
@@ -56,9 +56,10 @@ class ProxyAdmin(FlaskView):
|
|
56
56
|
# print(cat,vs)
|
57
57
|
db.session.commit()
|
58
58
|
hutils.proxy.get_proxies.invalidate_all()
|
59
|
+
if hutils.node.is_child():
|
60
|
+
if not hutils.node.child.sync_with_parent():
|
61
|
+
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
59
62
|
hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=False)
|
60
|
-
# if hconfig(ConfigEnum.parent_panel):
|
61
|
-
# hiddify_api.sync_child_to_parent()
|
62
63
|
global_config_form = get_global_config_form(True)
|
63
64
|
else:
|
64
65
|
hutils.flask.flash((_('config.validation-error')), 'danger')
|
@@ -94,7 +95,7 @@ def get_global_config_form(empty=False):
|
|
94
95
|
|
95
96
|
|
96
97
|
def get_all_proxy_form(empty=False):
|
97
|
-
proxies = hutils.proxy.get_proxies(Child.current.id)
|
98
|
+
proxies = hutils.proxy.get_proxies(Child.current().id)
|
98
99
|
categories1 = sorted([c for c in {c.cdn: 1 for c in proxies}])
|
99
100
|
|
100
101
|
class DynamicForm(FlaskForm):
|
@@ -3,7 +3,9 @@ import flask_babel
|
|
3
3
|
import flask_babel
|
4
4
|
from flask_babel import lazy_gettext as _
|
5
5
|
# from flask_babelex import gettext as _
|
6
|
-
from flask import render_template,
|
6
|
+
from flask import render_template, g # type: ignore
|
7
|
+
from markupsafe import Markup
|
8
|
+
|
7
9
|
from hiddifypanel.hutils.flask import hurl_for
|
8
10
|
from flask import current_app as app
|
9
11
|
from hiddifypanel import hutils
|
@@ -20,6 +22,7 @@ from hiddifypanel.models import BoolConfig, StrConfig, ConfigEnum, hconfig, Conf
|
|
20
22
|
from hiddifypanel.models import *
|
21
23
|
from hiddifypanel.database import db
|
22
24
|
from hiddifypanel.panel import hiddify, custom_widgets
|
25
|
+
from hiddifypanel import __version__
|
23
26
|
|
24
27
|
|
25
28
|
class SettingAdmin(FlaskView):
|
@@ -36,26 +39,26 @@ class SettingAdmin(FlaskView):
|
|
36
39
|
reset_action = None
|
37
40
|
if form.validate_on_submit():
|
38
41
|
|
39
|
-
boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current.id).all()
|
42
|
+
boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current().id).all()
|
40
43
|
bool_types = {c.key: 'bool' for c in boolconfigs}
|
41
44
|
|
42
45
|
old_configs = get_hconfigs()
|
43
46
|
changed_configs = {}
|
44
47
|
|
45
|
-
for
|
48
|
+
for category, c_items in form.data.items(): # [c for c in ConfigEnum]:
|
46
49
|
|
47
|
-
if isinstance(
|
50
|
+
if isinstance(c_items, dict):
|
48
51
|
for k in ConfigEnum:
|
49
|
-
if k.name not in
|
52
|
+
if k.name not in c_items:
|
50
53
|
continue
|
51
|
-
v =
|
54
|
+
v = c_items[k.name]
|
52
55
|
if k.type == str:
|
53
56
|
if "_domain" in k or "_fakedomain" in k:
|
54
57
|
v = v.lower()
|
55
58
|
|
56
59
|
if "port" in k:
|
57
60
|
for p in v.split(","):
|
58
|
-
for k2, v2 in
|
61
|
+
for k2, v2 in c_items.items():
|
59
62
|
if "port" in k2 and k.name != k2 and p in v2:
|
60
63
|
hutils.flask.flash(_("Port is already used! in") + f" {k2} {k}", 'error')
|
61
64
|
return render_template('config.html', form=form)
|
@@ -73,32 +76,63 @@ class SettingAdmin(FlaskView):
|
|
73
76
|
hutils.flask.flash(_("ProxyPath is already used! use different proxy path"), 'error') # type: ignore
|
74
77
|
return render_template('config.html', form=form)
|
75
78
|
|
79
|
+
# validate parent_panel value
|
80
|
+
parent_apikey = ''
|
81
|
+
if p_p := changed_configs.get(ConfigEnum.parent_panel):
|
82
|
+
domain, proxy_path, uuid = hutils.flask.extract_parent_info_from_url(p_p)
|
83
|
+
if not domain or not proxy_path or not uuid or not hutils.node.is_panel_active(domain, proxy_path, uuid):
|
84
|
+
hutils.flask.flash(_('parent.invalid-parent-url'), 'danger') # type: ignore
|
85
|
+
return render_template('config.html', form=form)
|
86
|
+
else:
|
87
|
+
set_hconfig(ConfigEnum.parent_domain, domain)
|
88
|
+
set_hconfig(ConfigEnum.parent_admin_proxy_path, proxy_path)
|
89
|
+
parent_apikey = uuid
|
90
|
+
|
76
91
|
for k, v in changed_configs.items():
|
77
92
|
set_hconfig(k, v, commit=False)
|
78
93
|
|
79
94
|
db.session.commit()
|
80
95
|
flask_babel.refresh()
|
81
96
|
|
97
|
+
# set panel mode
|
98
|
+
p_mode = hconfig(ConfigEnum.panel_mode)
|
99
|
+
if p_mode != PanelMode.parent:
|
100
|
+
if hconfig(ConfigEnum.parent_panel):
|
101
|
+
if p_mode == PanelMode.standalone:
|
102
|
+
set_hconfig(ConfigEnum.panel_mode, PanelMode.child)
|
103
|
+
else:
|
104
|
+
if p_mode != PanelMode.standalone:
|
105
|
+
set_hconfig(ConfigEnum.panel_mode, PanelMode.standalone)
|
106
|
+
|
82
107
|
from hiddifypanel.panel.commercial.telegrambot import register_bot
|
83
108
|
register_bot(set_hook=True)
|
84
|
-
|
85
|
-
#
|
109
|
+
|
110
|
+
# sync with parent if needed
|
111
|
+
if hutils.node.is_child():
|
112
|
+
if hutils.node.child.is_registered():
|
113
|
+
if not hutils.node.child.sync_with_parent():
|
114
|
+
hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
|
115
|
+
else: # TODO: it's just for debuging
|
116
|
+
hutils.flask.flash(_('child.sync-success')) # type: ignore
|
117
|
+
else:
|
118
|
+
name = hconfig(ConfigEnum.unique_id)
|
119
|
+
parent_info = hutils.node.get_panel_info(hconfig(ConfigEnum.parent_domain), hconfig(ConfigEnum.parent_admin_proxy_path), parent_apikey)
|
120
|
+
if parent_info.get('version') != __version__:
|
121
|
+
hutils.flask.flash(_('node.diff-version'), 'danger') # type: ignore
|
122
|
+
if not hutils.node.child.register_to_parent(name, parent_apikey, mode=ChildMode.remote):
|
123
|
+
hutils.flask.flash(_('child.register-failed'), 'danger') # type: ignore
|
124
|
+
else: # TODO: it's just for debuging
|
125
|
+
hutils.flask.flash(_('child.register-success')) # type: ignore
|
126
|
+
|
86
127
|
reset_action = hiddify.check_need_reset(old_configs)
|
87
128
|
|
88
129
|
if old_configs[ConfigEnum.admin_lang] != hconfig(ConfigEnum.admin_lang):
|
89
|
-
|
90
130
|
form = get_config_form()
|
91
131
|
else:
|
92
132
|
hutils.flask.flash(_('config.validation-error'), 'danger') # type: ignore
|
93
133
|
|
94
134
|
return reset_action or render_template('config.html', form=form)
|
95
135
|
|
96
|
-
# # form=HelloForm()
|
97
|
-
# # # return render('config.html',form=form)
|
98
|
-
# # return render_template('config.html',form=HelloForm())
|
99
|
-
# form=get_config_form()
|
100
|
-
# return render_template('config.html',form=form)
|
101
|
-
|
102
136
|
def get_babel_string(self):
|
103
137
|
res = ""
|
104
138
|
strconfigs = StrConfig.query.all()
|
@@ -122,8 +156,8 @@ class SettingAdmin(FlaskView):
|
|
122
156
|
|
123
157
|
|
124
158
|
def get_config_form():
|
125
|
-
strconfigs = StrConfig.query.filter(StrConfig.child_id == Child.current.id).all()
|
126
|
-
boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current.id).all()
|
159
|
+
strconfigs = StrConfig.query.filter(StrConfig.child_id == Child.current().id).all()
|
160
|
+
boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current().id).all()
|
127
161
|
bool_types = {c.key: 'bool' for c in boolconfigs}
|
128
162
|
|
129
163
|
configs = [*boolconfigs, *strconfigs]
|
@@ -133,7 +167,7 @@ def get_config_form():
|
|
133
167
|
|
134
168
|
class DynamicForm(FlaskForm):
|
135
169
|
pass
|
136
|
-
is_parent =
|
170
|
+
is_parent = hutils.node.is_parent()
|
137
171
|
|
138
172
|
for cat in ConfigCategory:
|
139
173
|
if cat == 'hidden':
|
@@ -149,6 +183,9 @@ def get_config_form():
|
|
149
183
|
if not (c2 in configs_key):
|
150
184
|
continue
|
151
185
|
c = configs_key[c2]
|
186
|
+
if hutils.node.is_parent():
|
187
|
+
if c.key == ConfigEnum.parent_panel:
|
188
|
+
continue
|
152
189
|
extra_info = ''
|
153
190
|
if c.key in bool_types:
|
154
191
|
field = SwitchField(_(f'config.{c.key}.label'), default=c.value, description=_(f'config.{c.key}.description'))
|
@@ -207,7 +244,9 @@ def get_config_form():
|
|
207
244
|
field = wtf.SelectField(_(f"config.{c.key}.label"), choices=choices, description=_(f"config.{c.key}.description"), default=hconfig(c.key))
|
208
245
|
|
209
246
|
elif c.key == ConfigEnum.warp_sites:
|
210
|
-
validators = [wtf.validators.Length(max=2048)
|
247
|
+
validators = [wtf.validators.Length(max=2048),
|
248
|
+
wtf.validators.Regexp(r'^([\w.-]+)?(?:\n[\w.-]+)*$', re.IGNORECASE, _("config.invalid-pattern-for-warp-sites") + f' {c.key}')
|
249
|
+
]
|
211
250
|
render_kw = {'class': "ltr", 'maxlength': 2048}
|
212
251
|
field = wtf.TextAreaField(_(f'config.{c.key}.label'), validators, default=c.value,
|
213
252
|
description=_(f'config.{c.key}.description'), render_kw=render_kw)
|
@@ -229,7 +268,7 @@ def get_config_form():
|
|
229
268
|
if c.key != ConfigEnum.decoy_domain:
|
230
269
|
validators.append(wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain already used")))
|
231
270
|
validators.append(wtf.validators.NoneOf(
|
232
|
-
[cc.value.lower() for cc in StrConfig.query.filter(StrConfig.child_id == Child.current.id).all() if cc.key != c.key and "fakedomain" in cc.key and cc.key != ConfigEnum.decoy_domain], _("config.Domain already used")))
|
271
|
+
[cc.value.lower() for cc in StrConfig.query.filter(StrConfig.child_id == Child.current().id).all() if cc.key != c.key and "fakedomain" in cc.key and cc.key != ConfigEnum.decoy_domain], _("config.Domain already used")))
|
233
272
|
|
234
273
|
render_kw['required'] = ""
|
235
274
|
if len(c.value) < 3:
|