hiddifypanel 10.30.7.dev0__py3-none-any.whl → 10.30.8.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.
Files changed (36) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/auth.py +16 -0
  4. hiddifypanel/base.py +8 -1
  5. hiddifypanel/drivers/ssh_liberty_bridge_api.py +4 -0
  6. hiddifypanel/drivers/wireguard_api.py +34 -44
  7. hiddifypanel/drivers/xray_api.py +3 -3
  8. hiddifypanel/hutils/auth.py +1 -1
  9. hiddifypanel/models/admin.py +14 -8
  10. hiddifypanel/models/base_account.py +14 -6
  11. hiddifypanel/models/user.py +38 -25
  12. hiddifypanel/panel/cli.py +1 -27
  13. hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +3 -1
  14. hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +1 -0
  15. hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +1 -0
  16. hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +9 -20
  17. hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +14 -0
  18. hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +19 -14
  19. hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +1 -0
  20. hiddifypanel/panel/commercial/restapi/v2/admin/system_actions.py +27 -0
  21. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +12 -22
  22. hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +19 -1
  23. hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +1 -1
  24. hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +12 -0
  25. hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +5 -0
  26. hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +1 -1
  27. hiddifypanel/panel/commercial/restapi/v2/user/__init__.py +1 -1
  28. hiddifypanel/panel/hiddify.py +31 -0
  29. hiddifypanel/panel/init_db.py +1 -1
  30. hiddifypanel/templates/fake.html +316 -0
  31. {hiddifypanel-10.30.7.dev0.dist-info → hiddifypanel-10.30.8.dev0.dist-info}/METADATA +1 -1
  32. {hiddifypanel-10.30.7.dev0.dist-info → hiddifypanel-10.30.8.dev0.dist-info}/RECORD +36 -35
  33. {hiddifypanel-10.30.7.dev0.dist-info → hiddifypanel-10.30.8.dev0.dist-info}/WHEEL +1 -1
  34. {hiddifypanel-10.30.7.dev0.dist-info → hiddifypanel-10.30.8.dev0.dist-info}/LICENSE.md +0 -0
  35. {hiddifypanel-10.30.7.dev0.dist-info → hiddifypanel-10.30.8.dev0.dist-info}/entry_points.txt +0 -0
  36. {hiddifypanel-10.30.7.dev0.dist-info → hiddifypanel-10.30.8.dev0.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.30.7.dev0
1
+ 10.30.8.dev0
hiddifypanel/VERSION.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__='10.30.7.dev0'
1
+ __version__='10.30.8.dev0'
2
2
  from datetime import datetime
3
- __release_date__= datetime.strptime('2024-07-09','%Y-%m-%d')
3
+ __release_date__= datetime.strptime('2024-07-10','%Y-%m-%d')
hiddifypanel/auth.py CHANGED
@@ -98,8 +98,24 @@ def login_user(user: AdminUser | User, remember=False, duration=None, force=Fals
98
98
 
99
99
 
100
100
  def login_required(roles: set[Role] | None = None, node_auth: bool = False):
101
+
102
+ def decorator(func):
103
+ from flask import has_app_context, current_app
104
+ # Conditionally apply x if has_app_context() is true
105
+ if has_app_context():
106
+ func = current_app.doc(security='Hiddify-API-Key')(func)
107
+
108
+ # Always apply y
109
+ func = login_required2(roles, node_auth)(func)
110
+ return func
111
+ return decorator
112
+
113
+
114
+ def login_required2(roles: set[Role] | None = None, node_auth: bool = False):
101
115
  '''When both roles and node_auth is set, means authentication can be done by either uuid or unique_id'''
116
+
102
117
  def wrapper(fn):
118
+
103
119
  @wraps(fn)
104
120
  def decorated_view(*args, **kwargs):
105
121
  # print('xxxx', current_account)
hiddifypanel/base.py CHANGED
@@ -37,7 +37,7 @@ def init_logger(app, cli):
37
37
 
38
38
  def create_app(*args, cli=False, **config):
39
39
 
40
- app = APIFlask(__name__, static_url_path="/<proxy_path>/static/", instance_relative_config=True, version='2.0.0', title="Hiddify API",
40
+ app = APIFlask(__name__, static_url_path="/<proxy_path>/static/", instance_relative_config=True, version='2.2.0', title="Hiddify API",
41
41
  openapi_blueprint_url_prefix="/<proxy_path>/api", docs_ui='elements', json_errors=False, enable_openapi=not cli)
42
42
  # app = Flask(__name__, static_url_path="/<proxy_path>/static/", instance_relative_config=True)
43
43
 
@@ -72,6 +72,13 @@ def create_app(*args, cli=False, **config):
72
72
  app.config['SESSION_REDIS'] = redis_client
73
73
  app.config['SESSION_PERMANENT'] = True
74
74
  app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=10)
75
+ app.security_schemes = { # equals to use config SECURITY_SCHEMES
76
+ 'Hiddify-API-Key': {
77
+ 'type': 'apiKey',
78
+ 'in': 'header',
79
+ 'name': 'Hiddify-API-Key',
80
+ }
81
+ }
75
82
  Session(app)
76
83
 
77
84
  app.jinja_env.line_statement_prefix = '%'
@@ -40,6 +40,10 @@ class SSHLibertyBridgeApi(DriverABS):
40
40
  redis_client.save()
41
41
 
42
42
  def get_all_usage(self, users):
43
+ redis_client = self.get_ssh_redis_client()
44
+ allusage = redis_client.hgetall(USERS_USAGE)
45
+ redis_client.delete(USERS_USAGE)
46
+ return {u: int(allusage.get(u.uuid) or 0) for u in users}
43
47
  return {u: self.get_usage_imp(u.uuid) for u in users}
44
48
 
45
49
  def get_usage_imp(self, client_uuid: str, reset: bool = True) -> int:
@@ -4,40 +4,25 @@ import os
4
4
  from .abstract_driver import DriverABS
5
5
  from hiddifypanel.models import User, hconfig, ConfigEnum
6
6
  from hiddifypanel.panel.run_commander import Command, commander
7
+ import redis
8
+
9
+
10
+ USERS_USAGE = "wg:users-usage"
7
11
 
8
12
 
9
13
  class WireguardApi(DriverABS):
14
+ def get_redis_client(self):
15
+ if not hasattr(self, 'redis_client'):
16
+ self.redis_client = redis.from_url('unix:///opt/hiddify-manager/other/redis/run.sock?db=1')
17
+
18
+ return self.redis_client
19
+
10
20
  def is_enabled(self) -> bool:
11
21
  return hconfig(ConfigEnum.wireguard_enable)
12
-
13
- WG_LOCAL_USAGE_FILE_PATH = os.path.join('/opt/hiddify-manager/','hiddify-panel','wireguard_usages.json')
14
- OLD_WG_LOCAL_USAGE_FILE_PATH = os.path.join('/opt/hiddify-manager/','hiddify-panel','hiddify_usages.json')
15
22
 
16
23
  def __init__(self) -> None:
17
24
  super().__init__()
18
25
 
19
- if os.path.isfile(WireguardApi.OLD_WG_LOCAL_USAGE_FILE_PATH) and not os.path.isfile(WireguardApi.WG_LOCAL_USAGE_FILE_PATH):
20
- os.rename(WireguardApi.OLD_WG_LOCAL_USAGE_FILE_PATH,WireguardApi.WG_LOCAL_USAGE_FILE_PATH)
21
-
22
- if not self.is_usages_file_exists_and_json():
23
- self.init_empty_usages_file()
24
- # create empty local usage file
25
-
26
- def is_usages_file_exists_and_json(self) -> bool:
27
- if os.path.isfile(WireguardApi.WG_LOCAL_USAGE_FILE_PATH):
28
- try:
29
- # try to load it as a JSON
30
- self.__get_local_usage()
31
- return True
32
- except json.decoder.JSONDecodeError:
33
- os.remove(WireguardApi.WG_LOCAL_USAGE_FILE_PATH)
34
- return False
35
- return False
36
- def init_empty_usages_file(self):
37
- with open(WireguardApi.WG_LOCAL_USAGE_FILE_PATH, 'w+') as f:
38
- json.dump({}, f)
39
-
40
-
41
26
  def __get_wg_usages(self) -> dict:
42
27
  raw_output = commander(Command.update_wg_usage, run_in_background=False)
43
28
  data = {}
@@ -54,11 +39,13 @@ class WireguardApi(DriverABS):
54
39
  return data
55
40
 
56
41
  def __get_local_usage(self) -> dict:
57
- with open(WireguardApi.WG_LOCAL_USAGE_FILE_PATH, 'r') as f:
58
- data = json.load(f)
59
- return data
42
+ usage_data = self.get_redis_client() .get(USERS_USAGE)
43
+ if usage_data:
44
+ return json.loads(usage_data)
45
+
46
+ return {}
60
47
 
61
- def __sync_local_usages(self) -> dict:
48
+ def __sync_local_usages(self, users) -> dict:
62
49
  local_usage = self.__get_local_usage()
63
50
  wg_usage = self.__get_wg_usages()
64
51
  res = {}
@@ -66,16 +53,18 @@ class WireguardApi(DriverABS):
66
53
  for local_wg_pub in local_usage.copy().keys():
67
54
  if local_wg_pub not in wg_usage:
68
55
  del local_usage[local_wg_pub]
69
-
56
+ uuid_map = {u.wg_pub: u for u in users}
70
57
  for wg_pub, wg_usage in wg_usage.items():
58
+ user = uuid_map.get(wg_pub)
59
+ uuid = user.uuid if user else None
71
60
  if not local_usage.get(wg_pub):
72
- local_usage[wg_pub] = wg_usage
61
+ local_usage[wg_pub] = {"uuid": uuid, "usage": wg_usage}
73
62
  continue
74
- res[wg_pub] = self.calculate_reset(local_usage[wg_pub], wg_usage)
75
- local_usage[wg_pub] = wg_usage
63
+ res[wg_pub] = self.calculate_reset(local_usage[wg_pub]['usage'], wg_usage)
64
+ local_usage[wg_pub] = {"uuid": uuid, "usage": wg_usage}
65
+
66
+ self.get_redis_client().set(USERS_USAGE, json.dumps(local_usage))
76
67
 
77
- with open(WireguardApi.WG_LOCAL_USAGE_FILE_PATH, 'w') as f:
78
- json.dump(local_usage, f)
79
68
  return res
80
69
 
81
70
  def calculate_reset(self, last_usage: dict, current_usage: dict) -> dict:
@@ -94,15 +83,16 @@ class WireguardApi(DriverABS):
94
83
  if not hconfig(ConfigEnum.wireguard_enable):
95
84
  return {}
96
85
  usages = self.__get_wg_usages()
97
- wg_pubs = set(usages.keys())
98
-
99
- users = User.query.all()
100
- enabled = {}
101
- for u in users:
102
- if u.wg_pub in wg_pubs:
86
+ new_wg_pubs = set(usages.keys())
87
+ old_usages = self.__get_local_usage()
88
+ old_wg_pubs = set(old_usages.keys())
89
+ enabled = {u['uuid']: 1 for u in old_usages.values()}
90
+ not_included = new_wg_pubs - old_wg_pubs
91
+ if not_included:
92
+ users = User.query.filter(User.wg_pub.in_(not_included).all())
93
+ for u in users:
103
94
  enabled[u.uuid] = 1
104
- else:
105
- enabled[u.uuid] = 0
95
+
106
96
  return enabled
107
97
 
108
98
  def add_client(self, user):
@@ -114,7 +104,7 @@ class WireguardApi(DriverABS):
114
104
  def get_all_usage(self, users, reset=True):
115
105
  if not hconfig(ConfigEnum.wireguard_enable):
116
106
  return {}
117
- all_usages = self.__sync_local_usages()
107
+ all_usages = self.__sync_local_usages(users)
118
108
  res = {}
119
109
  for u in users:
120
110
  if use := all_usages.get(u.wg_pub):
@@ -56,12 +56,12 @@ class XrayApi(DriverABS):
56
56
  def get_inbound_tags(self):
57
57
  try:
58
58
  xray_client = self.get_xray_client()
59
- inbounds = [inb.name.split(">>>")[1] for inb in xray_client.stats_query('')]
59
+ inbounds = {inb.name.split(">>>")[1] for inb in xray_client.stats_query('inbound')}
60
60
  print(f"Success in get inbound tags {inbounds}")
61
61
  except Exception as e:
62
62
  print(f"error in get inbound tags {e}")
63
- inbounds = []
64
- return list(set(inbounds))
63
+ inbounds = {}
64
+ return list(inbounds)
65
65
 
66
66
  def add_client(self, user):
67
67
  uuid = user.uuid
@@ -6,7 +6,7 @@ from uuid import UUID
6
6
  def is_uuid_valid(uuid: str, version: int = 4) -> bool:
7
7
  try:
8
8
  uuid_obj = UUID(uuid, version=version)
9
- except ValueError:
9
+ except Exception:
10
10
  return False
11
11
  return str(uuid_obj) == uuid
12
12
 
@@ -1,5 +1,5 @@
1
1
  from enum import auto
2
- import uuid
2
+ from uuid import uuid4
3
3
  from flask import g
4
4
  from hiddifypanel.models.usage import DailyUsage
5
5
  from sqlalchemy import event, Column, Integer, Enum, Boolean, ForeignKey
@@ -84,6 +84,9 @@ class AdminUser(BaseAccount, SerializerMixin):
84
84
  uuid = str(uuid)
85
85
  account = AdminUser.query.filter(AdminUser.uuid == uuid).first()
86
86
  if not account and create:
87
+ from hiddifypanel import hutils
88
+ if not hutils.auth.is_uuid_valid(uuid):
89
+ uuid = str(uuid4())
87
90
  dbuser = AdminUser(uuid=uuid, name="unknown", parent_admin_id=AdminUser.current_admin_or_owner().id)
88
91
  db.session.add(dbuser)
89
92
  db.session.commit()
@@ -103,11 +106,14 @@ class AdminUser(BaseAccount, SerializerMixin):
103
106
  else:
104
107
  parent_admin = cls.by_uuid(parent, create=True)
105
108
  dbuser.parent_admin_id = parent_admin.id # type: ignore
106
-
107
- dbuser.mode = data.get('mode', AdminMode.agent)
108
- dbuser.can_add_admin = data.get('can_add_admin') or False
109
- dbuser.max_users = data.get('max_users', 100)
110
- dbuser.max_active_users = data.get('max_active_users', 100)
109
+ if data.get('mode') is not None:
110
+ dbuser.mode = data.get('mode', AdminMode.agent)
111
+ if data.get('can_add_admin') is not None:
112
+ dbuser.can_add_admin = data['can_add_admin']
113
+ if data.get('max_users') is not None:
114
+ dbuser.max_users = data['max_users']
115
+ if data.get('max_active_users') is not None:
116
+ dbuser.max_active_users = data['max_active_users']
111
117
  if commit:
112
118
  db.session.commit()
113
119
  return dbuser
@@ -158,10 +164,10 @@ class AdminUser(BaseAccount, SerializerMixin):
158
164
  return str(self.name)
159
165
 
160
166
  @staticmethod
161
- def get_super_admin():
167
+ def get_super_admin() -> "AdminUser":
162
168
  admin = AdminUser.by_id(1)
163
169
  if not admin:
164
- db.session.add(AdminUser(id=1, uuid=str(uuid.uuid4()), name="Owner", mode=AdminMode.super_admin, comment=""))
170
+ db.session.add(AdminUser(id=1, uuid=str(uuid4()), name="Owner", mode=AdminMode.super_admin, comment=""))
165
171
  db.session.commit()
166
172
 
167
173
  db_execute("update admin_user set id=1 where name='Owner'", commit=True)
@@ -61,13 +61,21 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
61
61
  return cls.query.filter(cls.username == username, cls.password == password).first()
62
62
 
63
63
  @classmethod
64
- def add_or_update(cls, commit: bool = True, **data):
65
- db_account = cls.by_uuid(data['uuid'], create=True)
66
- db_account.name = data.get('name', '')
67
- db_account.comment = data.get('comment', '')
64
+ def add_or_update(cls, commit: bool = True, old_uuid=None, **data):
65
+ db_account: BaseAccount = cls.by_uuid(old_uuid or data.get('uuid'), create=True)
68
66
  from hiddifypanel import hutils
69
- db_account.telegram_id = hutils.convert.to_int(data.get('telegram_id'))
70
- db_account.lang = data.get('lang')
67
+ if hutils.auth.is_uuid_valid(data.get('uuid')):
68
+ db_account.uuid = data['uuid']
69
+
70
+ if data.get('name') is not None:
71
+ db_account.name = data.get('name')
72
+
73
+ if data.get('comment') is not None:
74
+ db_account.comment = data.get('comment')
75
+ if data.get('telegram_id') is not None:
76
+ db_account.telegram_id = hutils.convert.to_int(data.get('telegram_id'))
77
+ if data.get('lang') is not None:
78
+ db_account.lang = data.get('lang')
71
79
  if commit:
72
80
  db.session.commit() # type: ignore
73
81
  return db_account
@@ -1,5 +1,6 @@
1
1
  import datetime
2
2
  from enum import auto
3
+ from uuid import uuid4
3
4
  from hiddifypanel.models.role import Role
4
5
  from dateutil import relativedelta
5
6
 
@@ -54,7 +55,8 @@ class UserDetail(db.Model, SerializerMixin):
54
55
 
55
56
  @property
56
57
  def devices(self):
57
- return [] if not self.connected_devices else self.connected_devices.split(",")
58
+ return []
59
+ # return [] if not self.connected_devices else self.connected_devices.split(",")
58
60
 
59
61
 
60
62
  class User(BaseAccount, SerializerMixin):
@@ -122,13 +124,14 @@ class User(BaseAccount, SerializerMixin):
122
124
  is_active = False
123
125
  elif self.remaining_days < 0:
124
126
  is_active = False
125
- elif len(self.devices) > max(3, self.max_ips):
126
- is_active = False
127
+ # elif len(self.devices) > max(3, self.max_ips):
128
+ # is_active = False
127
129
  return is_active
128
130
 
129
131
  @property
130
132
  def devices(self):
131
133
  res = {}
134
+ return res
132
135
  for detail in UserDetail.query.filter(UserDetail.user_id == self.id):
133
136
  for device in detail.devices:
134
137
  res[device] = 1
@@ -202,6 +205,10 @@ class User(BaseAccount, SerializerMixin):
202
205
  uuid = str(uuid)
203
206
  account = User.query.filter(User.uuid == uuid).first()
204
207
  if not account and create:
208
+ from hiddifypanel import hutils
209
+ if not hutils.auth.is_uuid_valid(uuid):
210
+ uuid = str(uuid4())
211
+
205
212
  dbuser = User(uuid=uuid, name="unknown", added_by=AdminUser.current_admin_or_owner().id)
206
213
  db.session.add(dbuser)
207
214
  db.session.commit()
@@ -216,11 +223,11 @@ class User(BaseAccount, SerializerMixin):
216
223
  @classmethod
217
224
  def add_or_update(cls, commit: bool = True, **data):
218
225
  from hiddifypanel import hutils
219
- dbuser = super().add_or_update(commit=commit, **data)
226
+ dbuser: User = super().add_or_update(commit=commit, **data)
220
227
  if data.get('added_by_uuid'):
221
228
  admin = AdminUser.by_uuid(data.get('added_by_uuid'), create=True) or AdminUser.current_admin_or_owner() # type: ignore
222
229
  dbuser.added_by = admin.id
223
- else:
230
+ elif not dbuser.added_by:
224
231
  dbuser.added_by = 1
225
232
 
226
233
  # if data.get('expiry_time', ''): #v4
@@ -230,45 +237,50 @@ class User(BaseAccount, SerializerMixin):
230
237
  # dbuser.start_date = last_reset_time
231
238
  # dbuser.package_days = (expiry_time - last_reset_time).days # type: ignore
232
239
  # el
233
- if 'package_days' in data:
240
+ if data.get('package_days') is not None:
234
241
  dbuser.package_days = data['package_days']
235
- if data.get('start_date', ''):
242
+
243
+ if data.get('start_date'):
236
244
  dbuser.start_date = hutils.convert.json_to_date(data['start_date'])
237
- else:
245
+ elif 'start_date' in data and data['start_date'] is None:
238
246
  dbuser.start_date = None
239
247
 
240
248
  if (c_GB := data.get('current_usage_GB')) is not None:
241
249
  dbuser.current_usage_GB = c_GB
242
250
  elif (c := data.get('current_usage')) is not None:
243
251
  dbuser.current_usage = c
244
- else:
252
+ elif dbuser.current_usage is None:
245
253
  dbuser.current_usage = 0
246
254
 
247
255
  if (l_GB := data.get('usage_limit_GB')) is not None:
248
256
  dbuser.usage_limit_GB = l_GB
249
257
  elif (l := data.get('usage_limit')) is not None:
250
258
  dbuser.usage_limit = l
251
- else:
259
+ elif dbuser.usage_limit_GB is None:
252
260
  dbuser.usage_limit_GB = 1000
253
261
 
254
- dbuser.enable = data.get('enable', True)
262
+ if data.get('enable') is not None:
263
+ dbuser.enable = data['enable']
255
264
 
256
- if data.get('ed25519_private_key', ''):
265
+ if data.get('ed25519_private_key', '') and data.get('ed25519_public_key', ''):
257
266
  dbuser.ed25519_private_key = data.get('ed25519_private_key', '')
258
267
  dbuser.ed25519_public_key = data.get('ed25519_public_key', '')
259
-
260
- dbuser.wg_pk = data.get('wg_pk', dbuser.wg_pk)
261
- dbuser.wg_pub = data.get('wg_pub', dbuser.wg_pub)
262
- dbuser.wg_psk = data.get('wg_psk', dbuser.wg_psk)
263
-
264
- mode = data.get('mode', UserMode.no_reset)
265
- if mode == 'disable':
266
- mode = UserMode.no_reset
267
- dbuser.enable = False
268
-
269
- dbuser.mode = mode
270
-
271
- dbuser.last_online = hutils.convert.json_to_time(data.get('last_online')) or datetime.datetime.min
268
+ if data.get('wg_pk') is not None:
269
+ dbuser.wg_pk = data['wg_pk']
270
+ if data.get('wg_pub') is not None:
271
+ dbuser.wg_pub = data['wg_pub']
272
+ if data.get('wg_psk') is not None:
273
+ dbuser.wg_psk = data['wg_psk']
274
+
275
+ if data.get('mode') is not None or dbuser.mode is None:
276
+ mode = data.get('mode', UserMode.no_reset)
277
+ if mode == 'disable':
278
+ mode = UserMode.no_reset
279
+ dbuser.enable = False
280
+ dbuser.mode = mode
281
+
282
+ if data.get('last_online') is not None:
283
+ dbuser.last_online = hutils.convert.json_to_time(data.get('last_online')) or datetime.datetime.min
272
284
  if commit:
273
285
  db.session.commit()
274
286
  return dbuser
@@ -305,6 +317,7 @@ class User(BaseAccount, SerializerMixin):
305
317
  'wg_pk': self.wg_pk,
306
318
  'wg_pub': self.wg_pub,
307
319
  'wg_psk': self.wg_psk,
320
+ 'is_active': self.is_active
308
321
  }
309
322
 
310
323
  # @staticmethod
hiddifypanel/panel/cli.py CHANGED
@@ -48,33 +48,7 @@ def backup():
48
48
 
49
49
 
50
50
  def all_configs():
51
- valid_users = [u.to_dict(dump_id=True) for u in User.query.filter((User.usage_limit > User.current_usage)).all() if u.is_active]
52
- host_child_ids = [c.id for c in Child.query.filter(Child.mode == ChildMode.virtual).all()]
53
- configs = {
54
- "users": valid_users,
55
- "domains": [u.to_dict(dump_ports=True, dump_child_id=True) for u in Domain.query.filter(Domain.child_id.in_(host_child_ids)).all() if "*" not in u.domain],
56
- # "hconfigs": get_hconfigs(json=True),
57
- "chconfigs": get_hconfigs_childs(host_child_ids, json=True)
58
- }
59
-
60
- def_user = None if len(User.query.all()) > 1 else User.query.filter(User.name == 'default').first()
61
- domains = Domain.query.all()
62
- sslip_domains = [d.domain for d in domains if "sslip.io" in d.domain]
63
-
64
- configs['chconfigs'][0]['first_setup'] = def_user is not None and len(sslip_domains) > 0
65
- server_ip = hutils.network.get_ip_str(4)
66
- owner = AdminUser.get_super_admin()
67
-
68
- configs['admin_path'] = hiddify.get_account_panel_link(owner, server_ip, is_https=False, prefere_path_only=True)
69
- configs['panel_links'] = []
70
- configs['panel_links'].append(hiddify.get_account_panel_link(owner, server_ip, is_https=False))
71
- configs['panel_links'].append(hiddify.get_account_panel_link(owner, server_ip))
72
- domains = Domain.get_domains()
73
-
74
- for d in domains:
75
- configs['panel_links'].append(hiddify.get_account_panel_link(owner, d.domain))
76
-
77
- print(json.dumps(configs, indent=4))
51
+ print(json.dumps(hiddify.all_configs_for_cli(), indent=4))
78
52
 
79
53
 
80
54
  def update_usage():
@@ -13,12 +13,14 @@ def init_app(app):
13
13
  from .admin_user_api import AdminUserApi
14
14
  from .admin_users_api import AdminUsersApi
15
15
  from .admin_log_api import AdminLogApi
16
+ from .system_actions import UpdateUserUsageApi, AllConfigsApi
16
17
  bp.add_url_rule('/me/', view_func=AdminInfoApi) # type: ignore
17
18
  bp.add_url_rule('/server_status/', view_func=AdminServerStatusApi) # type: ignore
18
19
  bp.add_url_rule('/admin_user/<uuid:uuid>/', view_func=AdminUserApi) # type: ignore
19
20
  bp.add_url_rule('/admin_user/', view_func=AdminUsersApi) # type: ignore
20
21
  bp.add_url_rule('/log/', view_func=AdminLogApi) # type: ignore
21
-
22
+ bp.add_url_rule('/update_user_usage/', view_func=UpdateUserUsageApi) # type: ignore
23
+ bp.add_url_rule('/all-configs/', view_func=AllConfigsApi) # type: ignore
22
24
  from .user_api import UserApi
23
25
  from .users_api import UsersApi
24
26
  bp.add_url_rule('/user/<uuid:uuid>/', view_func=UserApi) # type: ignore
@@ -15,6 +15,7 @@ class AdminInfoApi(MethodView):
15
15
 
16
16
  @app.output(AdminSchema) # type: ignore
17
17
  def get(self):
18
+ """Current Admin Info"""
18
19
  admin = g.account or abort(404, "user not found")
19
20
 
20
21
  dto = AdminSchema()
@@ -18,6 +18,7 @@ class AdminLogApi(MethodView):
18
18
  @app.output(fields.String(description="The html of the log", many=True)) # type: ignore
19
19
  @login_required({Role.super_admin})
20
20
  def post(self, data):
21
+ """System: View Log file"""
21
22
  file_name = data.get('file') or abort(400, "Parameter issue: 'file'")
22
23
  log_dir = f"{app.config['HIDDIFY_CONFIG_PATH']}log/system/"
23
24
  log_files = hutils.flask.list_dir_files(log_dir)
@@ -6,7 +6,7 @@ from hiddifypanel.auth import login_required
6
6
  from hiddifypanel.models import *
7
7
 
8
8
  from . import has_permission
9
- from .schema import AdminSchema, PutAdminSchema, PatchAdminSchema, SuccessfulSchema
9
+ from .schema import AdminSchema, PatchAdminSchema, SuccessfulSchema
10
10
 
11
11
 
12
12
  class AdminUserApi(MethodView):
@@ -14,27 +14,16 @@ class AdminUserApi(MethodView):
14
14
 
15
15
  @app.output(AdminSchema) # type: ignore
16
16
  def get(self, uuid):
17
+ """Admin: Get an admin"""
17
18
  admin = AdminUser.by_uuid(uuid) or abort(404, "Admin not found")
18
19
  if not has_permission(admin):
19
20
  abort(403, "you don't have permission to access this admin")
20
21
  return admin.to_schema() # type: ignore
21
22
 
22
- @app.input(PutAdminSchema, arg_name='data') # type: ignore
23
- @app.output(SuccessfulSchema) # type: ignore
24
- def put(self, uuid, data):
25
- if AdminUser.by_uuid(uuid):
26
- abort(400, "The admin exists")
27
- data['uuid'] = uuid
28
-
29
- if not data.get('added_by_uuid'):
30
- data['added_by_uuid'] = g.account.uuid
31
-
32
- _ = AdminUser.add_or_update(**data) or abort(502, "Unknown issue: Admin is not added")
33
- return {'status': 200, 'msg': 'ok'}
34
-
35
23
  @app.input(PatchAdminSchema, arg_name='data') # type: ignore
36
- @app.output(SuccessfulSchema) # type: ignore
24
+ @app.output(AdminSchema) # type: ignore
37
25
  def patch(self, uuid, data):
26
+ """Admin: Update an admin"""
38
27
  admin = AdminUser.by_uuid(uuid) or abort(404, "Admin not found")
39
28
  if not has_permission(admin):
40
29
  abort(403, "You don't have permission to access this admin")
@@ -44,15 +33,15 @@ class AdminUserApi(MethodView):
44
33
  continue
45
34
  if field not in data:
46
35
  data[field] = getattr(admin, field)
47
-
48
- _ = AdminUser.add_or_update(True, **data) or abort(502, "Unknown issue: Admin is not patched")
36
+ data['old_uuid'] = uuid
37
+ admin = AdminUser.add_or_update(True, **data) or abort(502, "Unknown issue: Admin is not patched")
49
38
  # the add_or_update doesn't update the uuid of AdminUser, so for now just delete old admin after adding new
50
- if admin.uuid != data['uuid']:
51
- admin.remove()
52
- return {'status': 200, 'msg': 'ok'}
39
+
40
+ return admins
53
41
 
54
42
  @app.output(SuccessfulSchema) # type: ignore
55
43
  def delete(self, uuid):
44
+ """Admin: Delete an admin"""
56
45
  admin = AdminUser.by_uuid(uuid) or abort(404, "Admin not found")
57
46
  if not has_permission(admin):
58
47
  abort(403, "You don't have permission to access this admin")
@@ -13,5 +13,19 @@ class AdminUsersApi(MethodView):
13
13
 
14
14
  @app.output(AdminSchema(many=True)) # type: ignore
15
15
  def get(self):
16
+ """Admin: Get all admins"""
16
17
  admins = AdminUser.query.filter(AdminUser.id.in_(g.account.recursive_sub_admins_ids())).all() or abort(404, "You have no admin")
17
18
  return [admin.to_schema() for admin in admins] # type: ignore
19
+
20
+ @app.input(AdminSchema, arg_name='data') # type: ignore
21
+ @app.output(AdminSchema) # type: ignore
22
+ def post(self, data):
23
+ """Admin: Create an admin"""
24
+ if 'uuid' in data and AdminUser.by_uuid(data['uuid']):
25
+ abort(400, "The admin exists")
26
+
27
+ if not data.get('added_by_uuid'):
28
+ data['added_by_uuid'] = g.account.uuid
29
+
30
+ admin = AdminUser.add_or_update(**data) or abort(502, "Unknown issue: Admin is not added")
31
+ return admin