From d651add8471ecde6e45310162ea7d6c13ca090b2 Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 13 Nov 2023 19:59:58 +0330 Subject: [PATCH 01/16] add: "api/v2/admin/me" & "api/v2/user/me" api --- hiddifypanel/panel/admin/__init__.py | 2 +- .../panel/commercial/restapi/v2/admin/DTO.py | 72 ++++++++++++++++++ .../commercial/restapi/v2/admin/__init__.py | 3 +- .../commercial/restapi/v2/admin/admin_user.py | 28 +++---- .../panel/commercial/restapi/v2/admin/user.py | 73 +------------------ .../commercial/restapi/v2/user/__init__.py | 8 +- 6 files changed, 93 insertions(+), 93 deletions(-) create mode 100644 hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py diff --git a/hiddifypanel/panel/admin/__init__.py b/hiddifypanel/panel/admin/__init__.py index ab8671534..3a1a97eba 100644 --- a/hiddifypanel/panel/admin/__init__.py +++ b/hiddifypanel/panel/admin/__init__.py @@ -65,7 +65,7 @@ def auto_route(): # admin_bp.add_url_rule('/admin/quicksetup/',endpoint="quicksetup",view_func=QuickSetup.index,methods=["GET"]) # admin_bp.add_url_rule('/admin/quicksetup/',endpoint="quicksetup-save", view_func=QuickSetup.save,methods=["POST"]) - app.add_url_rule("///admin/static//", endpoint="admin.static") # fix bug in admin with blueprint + #app.add_url_rule("///admin/static//", endpoint="admin.static") # fix bug in admin with blueprint flask_bp.debug = True app.register_blueprint(admin_bp) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py new file mode 100644 index 000000000..a049a8bf4 --- /dev/null +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py @@ -0,0 +1,72 @@ +from apiflask import Schema +from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time +from hiddifypanel.models import AdminMode,UserMode + +class AdminDTO(Schema): + name = String(required=True, description='The name of the admin') + comment = String(required=False, description='A comment related to the admin') + uuid = UUID(required=True, description='The unique identifier for the admin') + mode = Enum(AdminMode, required=True, description='The mode for the admin') + can_add_admin = Boolean(required=True, description='Whether the admin can add other admins') + parent_admin_uuid = UUID(description='The unique identifier for the parent admin', allow_none=True, + # validate=OneOf([p.uuid for p in AdminUser.query.all()]) + ) + telegram_id = Integer(required=True, description='The Telegram ID associated with the admin') + + + + +class UserDTO(Schema): + uuid = UUID(required=True, description="Unique identifier for the user") + name = String(required=True, description="Name of the user") + + usage_limit_GB = Float( + required=False, + description="The data usage limit for the user in gigabytes" + ) + package_days = Integer( + required=False, + description="The number of days in the user's package" + ) + mode = Enum(UserMode, + required=False, + description="The mode of the user's account, which dictates access level or type" + ) + last_online = Time( + format="%Y-%m-%d %H:%M:%S", + description="The last time the user was online, converted to a JSON-friendly format" + ) + start_date = Date( + format='%Y-%m-%d', + description="The start date of the user's package, in a JSON-friendly format" + ) + current_usage_GB = Float( + required=False, + description="The current data usage of the user in gigabytes" + ) + last_reset_time = Date( + format='%Y-%m-%d', + description="The last time the user's data usage was reset, in a JSON-friendly format" + ) + comment = String( + missing=None, + description="An optional comment about the user" + ) + added_by_uuid = UUID( + required=True, + description="UUID of the admin who added this user", + # validate=OneOf([p.uuid for p in AdminUser.query.all()]) + ) + telegram_id = Integer( + required=False, + description="The Telegram ID associated with the user" + ) + ed25519_private_key = String( + required=False, + description="If empty, it will be created automatically, The user's private key using the Ed25519 algorithm" + ) + ed25519_public_key = String( + required=False, + description="If empty, it will be created automatically,The user's public key using the Ed25519 algorithm" + ) + diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py b/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py index 3728c42e4..da94c2e53 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py @@ -11,7 +11,8 @@ def init_app(app): with app.app_context(): - from .admin_user import AdminUsersApi, AdminUserApi + from .admin_user import AdminUsersApi, AdminUserApi,AdminInfoApi + bp.add_url_rule('/me/',view_func=AdminInfoApi) bp.add_url_rule('/admin_user/', view_func=AdminUsersApi) bp.add_url_rule('/admin_user/', view_func=AdminUserApi) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py index 54eda197c..1f7d587f9 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py @@ -1,34 +1,28 @@ from flask import abort, jsonify, request from flask_restful import Resource # from flask_simplelogin import login_required -import datetime from hiddifypanel.models import * from urllib.parse import urlparse from hiddifypanel.panel import hiddify -from hiddifypanel.drivers import user_driver from flask.views import MethodView from flask import current_app as app -from apiflask import APIFlask, Schema, abort -from apiflask.validators import Length, OneOf -from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time +from flask import g +from apiflask import abort from hiddifypanel.models import * +from hiddifypanel.panel.commercial.restapi.v2.admin.DTO import * -class AdminDTO(Schema): - name = String(required=True, description='The name of the admin') - comment = String(required=False, description='A comment related to the admin') - uuid = UUID(required=True, description='The unique identifier for the admin') - mode = Enum(AdminMode, required=True, description='The mode for the admin') - can_add_admin = Boolean(required=True, description='Whether the admin can add other admins') - parent_admin_uuid = UUID(description='The unique identifier for the parent admin', allow_none=True, - # validate=OneOf([p.uuid for p in AdminUser.query.all()]) - ) - telegram_id = Integer(required=True, description='The Telegram ID associated with the admin') - - +class AdminInfoApi(MethodView): + decorators = [hiddify.super_admin] + @app.output(AdminDTO) + def get(self): + # in this case g.user_uuid is equal to admin uuid + admin_uuid = g.user_uuid + admin = get_admin_user_db(admin_uuid) or abort(404, "user not found") + return admin.to_dict() class AdminUsersApi(MethodView): decorators = [hiddify.super_admin] diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/user.py index 0b57b6180..f957d4ecc 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/user.py @@ -1,83 +1,16 @@ from flask import abort, jsonify, request -from flask_restful import Resource # from flask_simplelogin import login_required -import datetime from hiddifypanel.models import * -from urllib.parse import urlparse from hiddifypanel.panel import hiddify from hiddifypanel.drivers import user_driver -from flask_restful import Resource from hiddifypanel.panel import hiddify -from flask_classful import FlaskView, route from flask.views import MethodView from flask import current_app as app -from apiflask import APIFlask, Schema, abort -from apiflask.fields import Integer, String -from apiflask.validators import Length, OneOf - -from apiflask import APIFlask, Schema, abort -from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time -from apiflask.validators import Length, OneOf - -from hiddifypanel.models import * - - -class UserDTO(Schema): - uuid = UUID(required=True, description="Unique identifier for the user") - name = String(required=True, description="Name of the user") - - usage_limit_GB = Float( - required=False, - description="The data usage limit for the user in gigabytes" - ) - package_days = Integer( - required=False, - description="The number of days in the user's package" - ) - mode = Enum(UserMode, - required=False, - description="The mode of the user's account, which dictates access level or type" - ) - last_online = Time( - format="%Y-%m-%d %H:%M:%S", - description="The last time the user was online, converted to a JSON-friendly format" - ) - start_date = Date( - format='%Y-%m-%d', - description="The start date of the user's package, in a JSON-friendly format" - ) - current_usage_GB = Float( - required=False, - description="The current data usage of the user in gigabytes" - ) - last_reset_time = Date( - format='%Y-%m-%d', - description="The last time the user's data usage was reset, in a JSON-friendly format" - ) - comment = String( - missing=None, - description="An optional comment about the user" - ) - added_by_uuid = UUID( - required=True, - description="UUID of the admin who added this user", - # validate=OneOf([p.uuid for p in AdminUser.query.all()]) - ) - telegram_id = Integer( - required=False, - description="The Telegram ID associated with the user" - ) - ed25519_private_key = String( - required=False, - description="If empty, it will be created automatically, The user's private key using the Ed25519 algorithm" - ) - ed25519_public_key = String( - required=False, - description="If empty, it will be created automatically,The user's public key using the Ed25519 algorithm" - ) +from apiflask import abort +from hiddifypanel.panel.commercial.restapi.v2.admin.DTO import * class UsersApi(MethodView): decorators = [hiddify.super_admin] @@ -91,7 +24,7 @@ def get(self): @app.output(UserDTO) def put(self, data): hiddify.add_or_update_user(**data) - user = user_by_uuid(uuid) or abort(502, "unknown issue! user is not added") + user = user_by_uuid(data['uuid']) or abort(502, "unknown issue! user is not added") user_driver.add_client(user) hiddify.quick_apply_users() return user.to_dict(False) diff --git a/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py b/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py index b77f1a05a..cd44f4d75 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py +++ b/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py @@ -13,9 +13,9 @@ def init_app(app): with app.app_context(): from .user import InfoAPI,MTProxiesAPI,AllConfigsAPI,ShortAPI - bp.add_url_rule("info", view_func=InfoAPI) - bp.add_url_rule("mtproxies", view_func=MTProxiesAPI) - bp.add_url_rule("all-configs", view_func=AllConfigsAPI) - bp.add_url_rule("short", view_func=ShortAPI) + bp.add_url_rule("/me/", view_func=InfoAPI) + bp.add_url_rule("/mtproxies/", view_func=MTProxiesAPI) + bp.add_url_rule("/all-configs/", view_func=AllConfigsAPI) + bp.add_url_rule("/short/", view_func=ShortAPI) app.register_blueprint(bp) From eb3b96deb53acdfce348b2cacc0514b0f1a6c8af Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 13 Nov 2023 22:57:10 +0330 Subject: [PATCH 02/16] create database tables with flask_migrate --- hiddifypanel/base.py | 3 + hiddifypanel/panel/database.py | 8 +- hiddifypanel/panel/init_db.py | 58 ++++----- migrations/README | 1 + migrations/alembic.ini | 50 ++++++++ migrations/env.py | 113 ++++++++++++++++++ migrations/script.py.mako | 24 ++++ migrations/versions/f6dd7b788254_.py | 169 +++++++++++++++++++++++++++ 8 files changed, 396 insertions(+), 30 deletions(-) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/f6dd7b788254_.py diff --git a/hiddifypanel/base.py b/hiddifypanel/base.py index 621222681..8a89dd169 100644 --- a/hiddifypanel/base.py +++ b/hiddifypanel/base.py @@ -36,6 +36,9 @@ def create_app(cli=False, **config): hiddifypanel.panel.database.init_app(app) with app.app_context(): + hiddifypanel.panel.database.init_migration(app) + hiddifypanel.panel.database.migrate() + hiddifypanel.panel.database.upgrade() init_db() hiddifypanel.panel.common.init_app(app) diff --git a/hiddifypanel/panel/database.py b/hiddifypanel/panel/database.py index eb1731767..a4147b762 100644 --- a/hiddifypanel/panel/database.py +++ b/hiddifypanel/panel/database.py @@ -1,6 +1,7 @@ from flask_sqlalchemy import SQLAlchemy from sqlalchemy_utils import UUIDType - +from flask_migrate import Migrate,init,upgrade,migrate +import os db = SQLAlchemy() db.UUID = UUIDType @@ -12,3 +13,8 @@ def init_app(app): # db.create_all(app) # app.jinja_env.globals['get_locale'] = get_locale +def init_migration(app): + migrate = Migrate(app,db) + if os.path.isdir(migrate.directory): + return + init() diff --git a/hiddifypanel/panel/init_db.py b/hiddifypanel/panel/init_db.py index 070d9c17f..eed183148 100644 --- a/hiddifypanel/panel/init_db.py +++ b/hiddifypanel/panel/init_db.py @@ -17,7 +17,7 @@ def init_db(): - db.create_all() + #db.create_all() hconfig.invalidate_all() get_hconfigs.invalidate_all() db_version = int(hconfig(ConfigEnum.db_version) or 0) @@ -31,33 +31,33 @@ def init_db(): execute(f'update admin_user set mode="agent" where mode = "slave"') execute(f'update admin_user set mode="super_admin" where id=1') execute(f'DELETE from proxy where transport = "h1"') - add_column(Domain.grpc) - add_column(ParentDomain.alias) - add_column(User.ed25519_private_key) - add_column(User.ed25519_public_key) - add_column(User.start_date) - add_column(User.package_days) - add_column(User.telegram_id) - add_column(Child.unique_id) - add_column(Domain.alias) - add_column(Domain.sub_link_only) - add_column(Domain.child_id) - add_column(Proxy.child_id) - add_column(User.added_by) - add_column(User.max_ips) - add_column(AdminUser.parent_admin_id) - add_column(AdminUser.can_add_admin) - add_column(AdminUser.max_active_users) - add_column(AdminUser.max_users) - add_column(BoolConfig.child_id) - add_column(StrConfig.child_id) - add_column(DailyUsage.admin_id) - add_column(DailyUsage.child_id) - add_column(User.monthly) - add_column(User.enable) - add_column(Domain.cdn_ip) - add_column(Domain.servernames) - add_column(User.lang) + # add_column(Domain.grpc) + # add_column(ParentDomain.alias) + # add_column(User.ed25519_private_key) + # add_column(User.ed25519_public_key) + # add_column(User.start_date) + # add_column(User.package_days) + # add_column(User.telegram_id) + # add_column(Child.unique_id) + # add_column(Domain.alias) + # add_column(Domain.sub_link_only) + # add_column(Domain.child_id) + # add_column(Proxy.child_id) + # add_column(User.added_by) + # add_column(User.max_ips) + # add_column(AdminUser.parent_admin_id) + # add_column(AdminUser.can_add_admin) + # add_column(AdminUser.max_active_users) + # add_column(AdminUser.max_users) + # add_column(BoolConfig.child_id) + # add_column(StrConfig.child_id) + # add_column(DailyUsage.admin_id) + # add_column(DailyUsage.child_id) + # add_column(User.monthly) + # add_column(User.enable) + # add_column(Domain.cdn_ip) + # add_column(Domain.servernames) + # add_column(User.lang) if len(Domain.query.all()) != 0 and BoolConfig.query.count() == 0: execute(f'DROP TABLE bool_config') @@ -407,7 +407,7 @@ def _v10(): all_configs = get_hconfigs() execute("ALTER TABLE `str_config` RENAME TO `str_config_old`") execute("ALTER TABLE `bool_config` RENAME TO `bool_config_old`") - db.create_all() + #db.create_all() rows = [] for c, v in all_configs.items(): if c.type() == bool: diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..0e0484415 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..ec9d45c26 --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,50 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic,flask_migrate + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 000000000..4c9709271 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,113 @@ +import logging +from logging.config import fileConfig + +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except (TypeError, AttributeError): + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option('sqlalchemy.url', get_engine_url()) +target_db = current_app.extensions['migrate'].db + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=get_metadata(), literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + conf_args = current_app.extensions['migrate'].configure_args + if conf_args.get("process_revision_directives") is None: + conf_args["process_revision_directives"] = process_revision_directives + + connectable = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_metadata(), + **conf_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/f6dd7b788254_.py b/migrations/versions/f6dd7b788254_.py new file mode 100644 index 000000000..e2ccc349b --- /dev/null +++ b/migrations/versions/f6dd7b788254_.py @@ -0,0 +1,169 @@ +"""empty message + +Revision ID: f6dd7b788254 +Revises: +Create Date: 2023-11-13 22:45:51.073693 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f6dd7b788254' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('admin_user', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('name', sa.String(length=512), nullable=False), + sa.Column('mode', sa.Enum('super_admin', 'admin', 'agent', name='adminmode'), nullable=False), + sa.Column('can_add_admin', sa.Boolean(), nullable=False), + sa.Column('max_users', sa.Integer(), nullable=False), + sa.Column('max_active_users', sa.Integer(), nullable=False), + sa.Column('comment', sa.String(length=512), nullable=True), + sa.Column('telegram_id', sa.String(length=512), nullable=True), + sa.Column('parent_admin_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['parent_admin_id'], ['admin_user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('uuid') + ) + op.create_table('child', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('unique_id', sa.String(length=200), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('unique_id') + ) + op.create_table('parent_domain', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('domain', sa.String(length=200), nullable=False), + sa.Column('alias', sa.String(length=200), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('domain') + ) + op.create_table('bool_config', + sa.Column('child_id', sa.Integer(), nullable=False), + sa.Column('key', sa.Enum('ssh_server_redis_url', 'ssh_server_port', 'ssh_server_enable', 'first_setup', 'core_type', 'warp_enable', 'warp_mode', 'warp_plus_code', 'warp_sites', 'dns_server', 'reality_fallback_domain', 'reality_server_names', 'reality_short_ids', 'reality_private_key', 'reality_public_key', 'reality_port', 'restls1_2_domain', 'restls1_3_domain', 'show_usage_in_sublink', 'cloudflare', 'license', 'country', 'package_mode', 'utls', 'telegram_bot_token', 'is_parent', 'parent_panel', 'unique_id', 'last_hash', 'cdn_forced_host', 'lang', 'admin_lang', 'admin_secret', 'tls_ports', 'http_ports', 'kcp_ports', 'kcp_enable', 'decoy_domain', 'proxy_path', 'firewall', 'netdata', 'http_proxy_enable', 'block_iran_sites', 'allow_invalid_sni', 'auto_update', 'speed_test', 'only_ipv4', 'shared_secret', 'telegram_enable', 'telegram_adtag', 'telegram_lib', 'telegram_fakedomain', 'v2ray_enable', 'torrent_block', 'ssfaketls_enable', 'ssfaketls_fakedomain', 'shadowtls_enable', 'shadowtls_fakedomain', 'tuic_enable', 'tuic_port', 'hysteria_enable', 'hysteria_port', 'ssr_enable', 'ssr_fakedomain', 'vmess_enable', 'domain_fronting_domain', 'domain_fronting_http_enable', 'domain_fronting_tls_enable', 'db_version', 'branding_title', 'branding_site', 'branding_freetext', 'not_found', 'path_vmess', 'path_vless', 'path_trojan', 'path_v2ray', 'path_ss', 'path_ws', 'path_tcp', 'path_grpc', name='configenum'), nullable=False), + sa.Column('value', sa.Boolean(), nullable=True), + sa.ForeignKeyConstraint(['child_id'], ['child.id'], ), + sa.PrimaryKeyConstraint('child_id', 'key') + ) + op.create_table('daily_usage', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('date', sa.Date(), nullable=True), + sa.Column('usage', sa.BigInteger(), nullable=False), + sa.Column('online', sa.Integer(), nullable=False), + sa.Column('admin_id', sa.Integer(), nullable=False), + sa.Column('child_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['admin_id'], ['admin_user.id'], ), + sa.ForeignKeyConstraint(['child_id'], ['child.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('domain', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('child_id', sa.Integer(), nullable=True), + sa.Column('domain', sa.String(length=200), nullable=False), + sa.Column('alias', sa.String(length=200), nullable=True), + sa.Column('sub_link_only', sa.Boolean(), nullable=False), + sa.Column('mode', sa.Enum('direct', 'sub_link_only', 'cdn', 'auto_cdn_ip', 'relay', 'reality', 'old_xtls_direct', 'worker', 'fake', name='domaintype'), nullable=False), + sa.Column('cdn_ip', sa.Text(length=2000), nullable=True), + sa.Column('grpc', sa.Boolean(), nullable=True), + sa.Column('servernames', sa.String(length=1000), nullable=True), + sa.ForeignKeyConstraint(['child_id'], ['child.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('proxy', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('child_id', sa.Integer(), nullable=True), + sa.Column('name', sa.String(length=200), nullable=False), + sa.Column('enable', sa.Boolean(), nullable=False), + sa.Column('proto', sa.Enum('vless', 'trojan', 'vmess', 'ss', 'v2ray', 'ssr', 'ssh', 'tuic', 'hysteria2', name='proxyproto'), nullable=False), + sa.Column('l3', sa.Enum('tls', 'tls_h2', 'tls_h2_h1', 'h3_quic', 'reality', 'http', 'kcp', 'ssh', 'custom', name='proxyl3'), nullable=False), + sa.Column('transport', sa.Enum('h2', 'grpc', 'XTLS', 'faketls', 'shadowtls', 'restls1_2', 'restls1_3', 'WS', 'tcp', 'ssh', 'custom', name='proxytransport'), nullable=False), + sa.Column('cdn', sa.Enum('CDN', 'direct', 'Fake', name='proxycdn'), nullable=False), + sa.ForeignKeyConstraint(['child_id'], ['child.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + op.create_table('str_config', + sa.Column('child_id', sa.Integer(), nullable=False), + sa.Column('key', sa.Enum('ssh_server_redis_url', 'ssh_server_port', 'ssh_server_enable', 'first_setup', 'core_type', 'warp_enable', 'warp_mode', 'warp_plus_code', 'warp_sites', 'dns_server', 'reality_fallback_domain', 'reality_server_names', 'reality_short_ids', 'reality_private_key', 'reality_public_key', 'reality_port', 'restls1_2_domain', 'restls1_3_domain', 'show_usage_in_sublink', 'cloudflare', 'license', 'country', 'package_mode', 'utls', 'telegram_bot_token', 'is_parent', 'parent_panel', 'unique_id', 'last_hash', 'cdn_forced_host', 'lang', 'admin_lang', 'admin_secret', 'tls_ports', 'http_ports', 'kcp_ports', 'kcp_enable', 'decoy_domain', 'proxy_path', 'firewall', 'netdata', 'http_proxy_enable', 'block_iran_sites', 'allow_invalid_sni', 'auto_update', 'speed_test', 'only_ipv4', 'shared_secret', 'telegram_enable', 'telegram_adtag', 'telegram_lib', 'telegram_fakedomain', 'v2ray_enable', 'torrent_block', 'ssfaketls_enable', 'ssfaketls_fakedomain', 'shadowtls_enable', 'shadowtls_fakedomain', 'tuic_enable', 'tuic_port', 'hysteria_enable', 'hysteria_port', 'ssr_enable', 'ssr_fakedomain', 'vmess_enable', 'domain_fronting_domain', 'domain_fronting_http_enable', 'domain_fronting_tls_enable', 'db_version', 'branding_title', 'branding_site', 'branding_freetext', 'not_found', 'path_vmess', 'path_vless', 'path_trojan', 'path_v2ray', 'path_ss', 'path_ws', 'path_tcp', 'path_grpc', name='configenum'), nullable=False), + sa.Column('value', sa.String(length=2048), nullable=True), + sa.ForeignKeyConstraint(['child_id'], ['child.id'], ), + sa.PrimaryKeyConstraint('child_id', 'key') + ) + op.create_table('user', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('name', sa.String(length=512), nullable=False), + sa.Column('last_online', sa.DateTime(), nullable=False), + sa.Column('expiry_time', sa.Date(), nullable=True), + sa.Column('usage_limit', sa.BigInteger(), nullable=False), + sa.Column('package_days', sa.Integer(), nullable=False), + sa.Column('mode', sa.Enum('no_reset', 'monthly', 'weekly', 'daily', name='usermode'), nullable=False), + sa.Column('monthly', sa.Boolean(), nullable=True), + sa.Column('start_date', sa.Date(), nullable=True), + sa.Column('current_usage', sa.BigInteger(), nullable=False), + sa.Column('last_reset_time', sa.Date(), nullable=True), + sa.Column('comment', sa.String(length=512), nullable=True), + sa.Column('telegram_id', sa.String(length=512), nullable=True), + sa.Column('added_by', sa.Integer(), nullable=True), + sa.Column('max_ips', sa.Integer(), nullable=False), + sa.Column('enable', sa.Boolean(), nullable=False), + sa.Column('lang', sa.Enum('en', 'fa', 'ru', 'pt', 'zh', name='lang'), nullable=True), + sa.Column('ed25519_private_key', sa.String(length=500), nullable=True), + sa.Column('ed25519_public_key', sa.String(length=100), nullable=True), + sa.ForeignKeyConstraint(['added_by'], ['admin_user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('uuid') + ) + op.create_table('show_domain', + sa.Column('domain_id', sa.Integer(), nullable=False), + sa.Column('related_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['domain_id'], ['domain.id'], ), + sa.ForeignKeyConstraint(['related_id'], ['domain.id'], ), + sa.PrimaryKeyConstraint('domain_id', 'related_id'), + info={'bind_key': None} + ) + op.create_table('show_domain_parent', + sa.Column('domain_id', sa.Integer(), nullable=False), + sa.Column('related_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['domain_id'], ['parent_domain.id'], ), + sa.ForeignKeyConstraint(['related_id'], ['domain.id'], ), + sa.PrimaryKeyConstraint('domain_id', 'related_id'), + info={'bind_key': None} + ) + op.create_table('user_detail', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('child_id', sa.Integer(), nullable=False), + sa.Column('last_online', sa.DateTime(), nullable=False), + sa.Column('current_usage', sa.BigInteger(), nullable=False), + sa.Column('connected_ips', sa.String(length=512), nullable=False), + sa.ForeignKeyConstraint(['child_id'], ['child.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('user_detail') + op.drop_table('show_domain_parent') + op.drop_table('show_domain') + op.drop_table('user') + op.drop_table('str_config') + op.drop_table('proxy') + op.drop_table('domain') + op.drop_table('daily_usage') + op.drop_table('bool_config') + op.drop_table('parent_domain') + op.drop_table('child') + op.drop_table('admin_user') + # ### end Alembic commands ### From 5f4fb508a494777baeb8393c7b4c717c796efee7 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 14 Nov 2023 15:55:06 +0330 Subject: [PATCH 03/16] fix: flask_migrate common errors --- hiddifypanel/panel/database.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/hiddifypanel/panel/database.py b/hiddifypanel/panel/database.py index a4147b762..9348a10fb 100644 --- a/hiddifypanel/panel/database.py +++ b/hiddifypanel/panel/database.py @@ -1,12 +1,13 @@ from flask_sqlalchemy import SQLAlchemy from sqlalchemy_utils import UUIDType -from flask_migrate import Migrate,init,upgrade,migrate +import flask_migrate +from flask_migrate import upgrade +import re import os db = SQLAlchemy() db.UUID = UUIDType - def init_app(app): app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True db.init_app(app) @@ -14,7 +15,22 @@ def init_app(app): # db.create_all(app) # app.jinja_env.globals['get_locale'] = get_locale def init_migration(app): - migrate = Migrate(app,db) + migrate = flask_migrate.Migrate(app,db) if os.path.isdir(migrate.directory): return - init() + flask_migrate.init() +def migrate(): + try_again = False + try: + # run flask_migrate.migrate function without its decorator to catch function error in try statement + flask_migrate.migrate.__wrapped__() + except Exception as err: + err_str = str(err) + if err_str == 'Target database is not up to date.': + flask_migrate.stamp() + elif err_str.startswith("Can't locate revision identified by"): + rev_id = re.findall(" '(.*)'$",err_str)[0].replace("'","'").strip() + flask_migrate.revision(rev_id=rev_id) + finally: + if try_again: + flask_migrate.migrate() \ No newline at end of file From 7aa8b4ddd55ca71cb1c6a67228152b9f9cffd0e5 Mon Sep 17 00:00:00 2001 From: Sarina Date: Thu, 16 Nov 2023 06:26:58 +0330 Subject: [PATCH 04/16] fix: bug in user 'me' api --- hiddifypanel/panel/admin/__init__.py | 2 +- hiddifypanel/panel/commercial/restapi/v2/user/user.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hiddifypanel/panel/admin/__init__.py b/hiddifypanel/panel/admin/__init__.py index 3a1a97eba..ab8671534 100644 --- a/hiddifypanel/panel/admin/__init__.py +++ b/hiddifypanel/panel/admin/__init__.py @@ -65,7 +65,7 @@ def auto_route(): # admin_bp.add_url_rule('/admin/quicksetup/',endpoint="quicksetup",view_func=QuickSetup.index,methods=["GET"]) # admin_bp.add_url_rule('/admin/quicksetup/',endpoint="quicksetup-save", view_func=QuickSetup.save,methods=["POST"]) - #app.add_url_rule("///admin/static//", endpoint="admin.static") # fix bug in admin with blueprint + app.add_url_rule("///admin/static//", endpoint="admin.static") # fix bug in admin with blueprint flask_bp.debug = True app.register_blueprint(admin_bp) diff --git a/hiddifypanel/panel/commercial/restapi/v2/user/user.py b/hiddifypanel/panel/commercial/restapi/v2/user/user.py index 17330b49f..c2ca4adf0 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/user/user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/user/user.py @@ -46,7 +46,7 @@ def get(self): dto.brand_title = hconfig(ConfigEnum.branding_title) dto.brand_icon_url = "" dto.doh = f"https://{urlparse(request.base_url).hostname}/{g.proxy_path}/dns/dns-query" - dto.lang = c['user'].lang if c['user'].lang else hconfig(ConfigEnum.lang) + dto.lang = c['user'].lang return dto @app.input(UserInfoChangableDTO,arg_name='data') @@ -65,8 +65,6 @@ def patch(self,data): if data['language']: user = user_by_uuid(g.user_uuid) - if user.lang is None: - user.lang = hconfig(ConfigEnum.lang) if user.lang != data['language']: user.lang = data['language'] db.session.commit() From 3ef1c2e50979a6f49a194383d76a3f2dd29a00c1 Mon Sep 17 00:00:00 2001 From: Sarina Date: Thu, 16 Nov 2023 10:03:07 +0330 Subject: [PATCH 05/16] add: "lang" field to admin '/me/' api DTO --- .../panel/commercial/restapi/v2/admin/DTO.py | 3 ++- .../commercial/restapi/v2/admin/admin_user.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py index a049a8bf4..b8abb9190 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py @@ -1,6 +1,6 @@ from apiflask import Schema from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time -from hiddifypanel.models import AdminMode,UserMode +from hiddifypanel.models import AdminMode,UserMode,Lang class AdminDTO(Schema): name = String(required=True, description='The name of the admin') @@ -12,6 +12,7 @@ class AdminDTO(Schema): # validate=OneOf([p.uuid for p in AdminUser.query.all()]) ) telegram_id = Integer(required=True, description='The Telegram ID associated with the admin') + lang = Enum(Lang,required=True) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py index 1f7d587f9..3ce6dba38 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py @@ -22,7 +22,17 @@ def get(self): # in this case g.user_uuid is equal to admin uuid admin_uuid = g.user_uuid admin = get_admin_user_db(admin_uuid) or abort(404, "user not found") - return admin.to_dict() + + dto = AdminDTO() + dto.name = admin.name + dto.comment = admin.comment + dto.uuid = admin.uuid + dto.mode = admin.mode + dto.can_add_admin = admin.can_add_admin + dto.parent_admin_uuid = AdminUser.query.filter(AdminUser.id == admin.parent_admin_id).first().uuid or 'None' + dto.telegram_id = admin.telegram_id + dto.lang = Lang(hconfig(ConfigEnum.admin_lang)) + return dto class AdminUsersApi(MethodView): decorators = [hiddify.super_admin] @@ -59,4 +69,4 @@ def patch(self, uuid, data): def delete(self, uuid): admin = get_admin_user_db(uuid) or abort(404, "admin not found") admin.remove() - return {'status': 200, 'msg': 'ok'} + return {'status': 200, 'msg': 'ok'} \ No newline at end of file From ef61358aa4b71a3c1aa11e59992467f0c9738e12 Mon Sep 17 00:00:00 2001 From: Sarina Date: Fri, 17 Nov 2023 06:31:15 +0330 Subject: [PATCH 06/16] add: 'api/v2/admin/server_status/ api --- .../panel/commercial/restapi/v2/admin/DTO.py | 6 ++++-- .../commercial/restapi/v2/admin/__init__.py | 3 ++- .../commercial/restapi/v2/admin/admin_user.py | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py index b8abb9190..362b55e04 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py @@ -1,5 +1,5 @@ from apiflask import Schema -from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time +from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time,Dict from hiddifypanel.models import AdminMode,UserMode,Lang class AdminDTO(Schema): @@ -14,7 +14,9 @@ class AdminDTO(Schema): telegram_id = Integer(required=True, description='The Telegram ID associated with the admin') lang = Enum(Lang,required=True) - +class ServerStatus(Schema): + stats = Dict() + usage_history = Dict() class UserDTO(Schema): diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py b/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py index da94c2e53..c59d2352f 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py @@ -11,8 +11,9 @@ def init_app(app): with app.app_context(): - from .admin_user import AdminUsersApi, AdminUserApi,AdminInfoApi + from .admin_user import AdminUsersApi, AdminUserApi,AdminInfoApi,AdminServerStatus bp.add_url_rule('/me/',view_func=AdminInfoApi) + bp.add_url_rule('/server_status/',view_func=AdminServerStatus) bp.add_url_rule('/admin_user/', view_func=AdminUsersApi) bp.add_url_rule('/admin_user/', view_func=AdminUserApi) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py index 3ce6dba38..f11882e33 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py @@ -69,4 +69,18 @@ def patch(self, uuid, data): def delete(self, uuid): admin = get_admin_user_db(uuid) or abort(404, "admin not found") admin.remove() - return {'status': 200, 'msg': 'ok'} \ No newline at end of file + return {'status': 200, 'msg': 'ok'} + +class AdminServerStatus(MethodView): + decorators = [hiddify.super_admin] + + @app.output(ServerStatus) + def get(self): + dto = ServerStatus() + dto.stats = { + 'system': hiddify.system_stats(), + 'top5': hiddify.top_processes() + } + admin_id = request.args.get("admin_id") or g.admin.id + dto.usage_history = get_daily_usage_stats(admin_id) + return dto \ No newline at end of file From e2b03eb4922c30f00fab8d8466c8855670284507 Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 20 Nov 2023 19:20:09 +0330 Subject: [PATCH 07/16] fix: security issue --- hiddifypanel/panel/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hiddifypanel/panel/common.py b/hiddifypanel/panel/common.py index 6d70475b1..88038c845 100644 --- a/hiddifypanel/panel/common.py +++ b/hiddifypanel/panel/common.py @@ -59,8 +59,8 @@ def set_default_path_values(spec): for parameter in operation['parameters']: if parameter['name'] == 'proxy_path': parameter['schema'] = {'type': 'string', 'default': g.proxy_path} - elif parameter['name'] == 'user_secret': - parameter['schema'] = {'type': 'string', 'default': g.user_uuid} + # elif parameter['name'] == 'user_secret': + # parameter['schema'] = {'type': 'string', 'default': g.user_uuid} return spec @app.url_defaults From 9d85aa38505caf3ef668e5e49f27a318002ae029 Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 20 Nov 2023 19:52:31 +0330 Subject: [PATCH 08/16] chg: change AdminDTO to AdminSchema --- .../panel/commercial/restapi/v2/admin/DTO.py | 2 +- .../commercial/restapi/v2/admin/admin_user.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py index 362b55e04..8987d7580 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py @@ -2,7 +2,7 @@ from apiflask.fields import Integer, String, UUID, Boolean, Enum, Float, Date, Time,Dict from hiddifypanel.models import AdminMode,UserMode,Lang -class AdminDTO(Schema): +class AdminSchema(Schema): name = String(required=True, description='The name of the admin') comment = String(required=False, description='A comment related to the admin') uuid = UUID(required=True, description='The unique identifier for the admin') diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py index f11882e33..4dc6f922d 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py @@ -17,13 +17,13 @@ class AdminInfoApi(MethodView): decorators = [hiddify.super_admin] - @app.output(AdminDTO) + @app.output(AdminSchema) def get(self): # in this case g.user_uuid is equal to admin uuid admin_uuid = g.user_uuid admin = get_admin_user_db(admin_uuid) or abort(404, "user not found") - dto = AdminDTO() + dto = AdminSchema() dto.name = admin.name dto.comment = admin.comment dto.uuid = admin.uuid @@ -36,13 +36,13 @@ def get(self): class AdminUsersApi(MethodView): decorators = [hiddify.super_admin] - @app.output(AdminDTO(many=True)) + @app.output(AdminSchema(many=True)) def get(self): admins = AdminUser.query.all() or abort(502, "WTF!") return [admin.to_dict() for admin in admins] - @app.input(AdminDTO, arg_name='data') - @app.output(AdminDTO) + @app.input(AdminSchema, arg_name='data') + @app.output(AdminSchema) def put(self, data): # data = request.json # uuid = data.get('uuid') or abort(422, "Parameter issue: 'uuid'") @@ -54,13 +54,13 @@ def put(self, data): class AdminUserApi(MethodView): decorators = [hiddify.super_admin] - @app.output(AdminDTO) + @app.output(AdminSchema) def get(self, uuid): admin = get_admin_user_db(uuid) or abort(404, "user not found") return admin.to_dict() - @app.input(AdminDTO, arg_name='data') + @app.input(AdminSchema, arg_name='data') def patch(self, uuid, data): data['uuid'] = uuid hiddify.add_or_update_admin(**data) From 7fc913af901f5ca33e2e97e5a8753b9ed2cfabe3 Mon Sep 17 00:00:00 2001 From: Sarina Date: Mon, 20 Nov 2023 19:53:12 +0330 Subject: [PATCH 09/16] new: add license to api --- hiddifypanel/base.py | 25 ++++++++++++++++++++++--- hiddifypanel/panel/common.py | 6 +++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/hiddifypanel/base.py b/hiddifypanel/base.py index 8a89dd169..8c29d00e6 100644 --- a/hiddifypanel/base.py +++ b/hiddifypanel/base.py @@ -14,12 +14,31 @@ def create_app(cli=False, **config): + app = APIFlask(__name__, static_url_path="//static/", instance_relative_config=True, version='2.0.0', title="Hiddify API", openapi_blueprint_url_prefix="///api", docs_ui='elements', json_errors=False, enable_openapi=True) # app = Flask(__name__, static_url_path="//static/", instance_relative_config=True) app.wsgi_app = ProxyFix( app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1 ) + app.servers = { + 'name': 'current', + 'url': '', + } + app.info = { + 'description': 'Hiddify is a free and open source software. It is as it is.', + 'termsOfService': 'http://hiddify.com', + 'contact': { + 'name': 'API Support', + 'url': 'http://www.hiddify.com/support', + 'email': 'panel@hiddify.com' + }, + 'license': { + 'name': 'Creative Commons Zero v1.0 Universal', + 'url': 'https://github.com/hiddify/Hiddify-Manager/blob/main/LICENSE' + } + } + for c, v in dotenv_values(os.environ.get("HIDDIFY_CFG_PATH", 'app.cfg')).items(): if v.isdecimal(): v = int(v) @@ -36,9 +55,9 @@ def create_app(cli=False, **config): hiddifypanel.panel.database.init_app(app) with app.app_context(): - hiddifypanel.panel.database.init_migration(app) - hiddifypanel.panel.database.migrate() - hiddifypanel.panel.database.upgrade() + # hiddifypanel.panel.database.init_migration(app) + # hiddifypanel.panel.database.migrate() + # hiddifypanel.panel.database.upgrade() init_db() hiddifypanel.panel.common.init_app(app) diff --git a/hiddifypanel/panel/common.py b/hiddifypanel/panel/common.py index 88038c845..fd86be0fb 100644 --- a/hiddifypanel/panel/common.py +++ b/hiddifypanel/panel/common.py @@ -12,8 +12,8 @@ from sys import version as python_version from platform import platform - -def init_app(app): +from apiflask import APIFlask +def init_app(app:APIFlask): app.jinja_env.globals['ConfigEnum'] = ConfigEnum app.jinja_env.globals['DomainType'] = DomainType app.jinja_env.globals['UserMode'] = UserMode @@ -60,7 +60,7 @@ def set_default_path_values(spec): if parameter['name'] == 'proxy_path': parameter['schema'] = {'type': 'string', 'default': g.proxy_path} # elif parameter['name'] == 'user_secret': - # parameter['schema'] = {'type': 'string', 'default': g.user_uuid} + # parameter['schema'] = {'type': 'string', 'default': g.user_uuid} return spec @app.url_defaults From 6000dcd0754a723350758b6598fcd02f26212725 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 01:02:16 +0330 Subject: [PATCH 10/16] fix: bug in apiflask --- hiddifypanel/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hiddifypanel/base.py b/hiddifypanel/base.py index 8c29d00e6..eb966bb11 100644 --- a/hiddifypanel/base.py +++ b/hiddifypanel/base.py @@ -49,7 +49,7 @@ def create_app(cli=False, **config): app.jinja_env.line_statement_prefix = '%' app.jinja_env.filters['b64encode'] = hiddify.do_base_64 - + app.view_functions['admin.static']={}#fix bug in apiflask app.is_cli = cli flask_bootstrap.Bootstrap4(app) From a89f2371364ee0478f88d51ef3d2688ec433c1b4 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 02:10:24 +0330 Subject: [PATCH 11/16] new: better exception handling --- hiddifypanel/panel/common.py | 49 ++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/hiddifypanel/panel/common.py b/hiddifypanel/panel/common.py index fd86be0fb..e47ae2f3c 100644 --- a/hiddifypanel/panel/common.py +++ b/hiddifypanel/panel/common.py @@ -2,17 +2,18 @@ import uuid import user_agents -from flask import abort, render_template, request, jsonify +from flask import render_template, request, jsonify from flask import g, send_from_directory, session, Markup from jinja2 import Environment, FileSystemLoader - +from flask_babelex import gettext as _ import hiddifypanel from hiddifypanel.models import * from hiddifypanel.panel import hiddify, github_issue_generator from sys import version as python_version from platform import platform +from werkzeug.exceptions import HTTPException as WerkzeugHTTPException -from apiflask import APIFlask +from apiflask import APIFlask,HTTPError,abort def init_app(app:APIFlask): app.jinja_env.globals['ConfigEnum'] = ConfigEnum app.jinja_env.globals['DomainType'] = DomainType @@ -22,8 +23,41 @@ def init_app(app:APIFlask): @app.errorhandler(Exception) def internal_server_error(e): + # print(request.headers) + last_version = hiddify.get_latest_release_version('hiddifypanel') # TODO: add dev update check + if "T" in hiddifypanel.__version__: + has_update = False + else: + has_update = "dev" not in hiddifypanel.__version__ and f'{last_version}' != hiddifypanel.__version__ + + + if not request.accept_mimetypes.accept_html: + if has_update: + return jsonify({ + 'message':'This version of Hiddify Panel is outdated. please update it from admin area.', + }),500 + + return jsonify({'message':str(e), + 'detail':[f'{filename}:{line} {function}: {text}' for filename, line, function, text in traceback.extract_tb(e.__traceback__)], + 'version':hiddifypanel.__version__, + }),500 + + + trace = traceback.format_exc() + + # Create github issue link + issue_link = generate_github_issue_link_for_500_error(e, trace) + + - if not hasattr(e, 'code') or e.code == 500: + return render_template('500.html', error=e, trace=trace, has_update=has_update, last_version=last_version,issue_link= issue_link), 500 + + @app.errorhandler(HTTPError) + def internal_server_error(e): + # print(request.headers) + if not request.accept_mimetypes.accept_html: + return app.error_callback(e) + if e.status_code == 500: trace = traceback.format_exc() # Create github issue link @@ -36,10 +70,10 @@ def internal_server_error(e): has_update = "dev" not in hiddifypanel.__version__ and f'{last_version}' != hiddifypanel.__version__ return render_template('500.html', error=e, trace=trace, has_update=has_update, last_version=last_version,issue_link= issue_link), 500 - # if e.code in [400,401,403]: - # return render_template('access-denied.html',error=e), e.code + # if e.status_code in [400,401,403]: + # return render_template('access-denied.html',error=e), e.status_code - return render_template('error.html', error=e), e.code + return render_template('error.html', error=e), e.status_code def generate_github_issue_link(title, issue_body): opts = { @@ -105,6 +139,7 @@ def pull_secret_code(endpoint, values): g.pwa = session.get('pwa', False) g.user_agent = user_agents.parse(request.user_agent.string) + if g.user_agent.is_bot: abort(400, "invalid") g.proxy_path = values.pop('proxy_path', None) if values else None From b0ca6398db7d2d52c075aeeb8ecd69a4b52a1ad0 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 02:16:00 +0330 Subject: [PATCH 12/16] chg: use apiflask abort --- hiddifypanel/panel/admin/Dashboard.py | 3 ++- hiddifypanel/panel/admin/ProxyAdmin.py | 3 ++- hiddifypanel/panel/admin/SettingAdmin.py | 3 ++- hiddifypanel/panel/admin/UserAdmin.py | 3 ++- hiddifypanel/panel/commercial/restapi/v1/resources.py | 3 ++- hiddifypanel/panel/commercial/restapi/v1/tgbot.py | 3 ++- hiddifypanel/panel/commercial/restapi/v1/tgmsg.py | 3 ++- hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py | 2 +- hiddifypanel/panel/commercial/restapi/v2/admin/user.py | 2 +- hiddifypanel/panel/user/user.py | 3 ++- 10 files changed, 18 insertions(+), 10 deletions(-) diff --git a/hiddifypanel/panel/admin/Dashboard.py b/hiddifypanel/panel/admin/Dashboard.py index 5cbac2755..ee005ff15 100644 --- a/hiddifypanel/panel/admin/Dashboard.py +++ b/hiddifypanel/panel/admin/Dashboard.py @@ -2,7 +2,8 @@ import os import re -from flask import render_template, url_for, request, jsonify, g, redirect, abort +from flask import render_template, url_for, request, jsonify, g, redirect +from apiflask import abort from flask_babelex import lazy_gettext as _ from flask_classful import FlaskView, route diff --git a/hiddifypanel/panel/admin/ProxyAdmin.py b/hiddifypanel/panel/admin/ProxyAdmin.py index 095360482..902247064 100644 --- a/hiddifypanel/panel/admin/ProxyAdmin.py +++ b/hiddifypanel/panel/admin/ProxyAdmin.py @@ -11,7 +11,8 @@ import re -from flask import render_template, current_app, Markup, url_for, abort +from flask import render_template, current_app, Markup, url_for +from apiflask import abort from hiddifypanel.panel.hiddify import flash from hiddifypanel.models import User, Domain, DomainType, StrConfig, ConfigEnum, get_hconfigs from hiddifypanel.panel.database import db diff --git a/hiddifypanel/panel/admin/SettingAdmin.py b/hiddifypanel/panel/admin/SettingAdmin.py index cb95ed6ea..2ff715223 100644 --- a/hiddifypanel/panel/admin/SettingAdmin.py +++ b/hiddifypanel/panel/admin/SettingAdmin.py @@ -13,7 +13,8 @@ from hiddifypanel.panel.hiddify import get_random_domains import re -from flask import render_template, current_app, Markup, url_for, abort, g +from flask import render_template, current_app, Markup, url_for, g +from apiflask import abort from hiddifypanel.panel.hiddify import flash from hiddifypanel.models import * from hiddifypanel.panel.database import db diff --git a/hiddifypanel/panel/admin/UserAdmin.py b/hiddifypanel/panel/admin/UserAdmin.py index 9691bc72c..e339a67dc 100644 --- a/hiddifypanel/panel/admin/UserAdmin.py +++ b/hiddifypanel/panel/admin/UserAdmin.py @@ -2,7 +2,8 @@ from hiddifypanel.panel.database import db import datetime from hiddifypanel.models import * -from flask import Markup, g, request, url_for, abort +from flask import Markup, g, request, url_for +from apiflask import abort from wtforms.validators import Regexp, ValidationError import re import uuid diff --git a/hiddifypanel/panel/commercial/restapi/v1/resources.py b/hiddifypanel/panel/commercial/restapi/v1/resources.py index c82e63ab9..e2f7bdef9 100644 --- a/hiddifypanel/panel/commercial/restapi/v1/resources.py +++ b/hiddifypanel/panel/commercial/restapi/v1/resources.py @@ -1,4 +1,5 @@ -from flask import abort, jsonify, request +from flask import jsonify, request +from apiflask import abort from flask_restful import Resource # from flask_simplelogin import login_required import datetime diff --git a/hiddifypanel/panel/commercial/restapi/v1/tgbot.py b/hiddifypanel/panel/commercial/restapi/v1/tgbot.py index 2050acec2..a5c8cc964 100644 --- a/hiddifypanel/panel/commercial/restapi/v1/tgbot.py +++ b/hiddifypanel/panel/commercial/restapi/v1/tgbot.py @@ -1,6 +1,7 @@ import time import telebot -from flask import abort, request +from flask import request +from apiflask import abort from flask_restful import Resource from hiddifypanel.models import * diff --git a/hiddifypanel/panel/commercial/restapi/v1/tgmsg.py b/hiddifypanel/panel/commercial/restapi/v1/tgmsg.py index 3844d0c6c..bca618f9f 100644 --- a/hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +++ b/hiddifypanel/panel/commercial/restapi/v1/tgmsg.py @@ -1,4 +1,5 @@ -from flask import abort, jsonify, request +from flask import jsonify, request +from apiflask import abort from flask_restful import Resource # from flask_simplelogin import login_required import datetime diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py index 4dc6f922d..c57588c1d 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/admin_user.py @@ -1,4 +1,4 @@ -from flask import abort, jsonify, request +from flask import jsonify, request from flask_restful import Resource # from flask_simplelogin import login_required from hiddifypanel.models import * diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/user.py index f957d4ecc..5429fd96c 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/user.py @@ -1,4 +1,4 @@ -from flask import abort, jsonify, request +from flask import jsonify, request # from flask_simplelogin import login_required from hiddifypanel.models import * from hiddifypanel.panel import hiddify diff --git a/hiddifypanel/panel/user/user.py b/hiddifypanel/panel/user/user.py index 4c236afbc..45194dba7 100644 --- a/hiddifypanel/panel/user/user.py +++ b/hiddifypanel/panel/user/user.py @@ -1,5 +1,6 @@ -from flask import abort, render_template, request, Response, g, url_for, jsonify, flash +from flask import render_template, request, Response, g, url_for, jsonify, flash +from apiflask import abort from wtforms.validators import Regexp, ValidationError import urllib import uuid From b0752d85ab9435ef8b9db4c28718aa2b51639cb3 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 10:45:55 +0330 Subject: [PATCH 13/16] Api things - change dto suffixes to Schema - add apps api - add expire_in field in short api --- .../panel/commercial/restapi/v2/admin/DTO.py | 2 +- .../panel/commercial/restapi/v2/admin/user.py | 10 +- .../panel/commercial/restapi/v2/user/DTO.py | 56 ++- .../commercial/restapi/v2/user/__init__.py | 3 +- .../panel/commercial/restapi/v2/user/user.py | 338 +++++++++++++++++- hiddifypanel/utils.py | 5 +- 6 files changed, 386 insertions(+), 28 deletions(-) diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py index 8987d7580..39051c5c4 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/DTO.py @@ -19,7 +19,7 @@ class ServerStatus(Schema): usage_history = Dict() -class UserDTO(Schema): +class UserSchema(Schema): uuid = UUID(required=True, description="Unique identifier for the user") name = String(required=True, description="Name of the user") diff --git a/hiddifypanel/panel/commercial/restapi/v2/admin/user.py b/hiddifypanel/panel/commercial/restapi/v2/admin/user.py index 5429fd96c..5e4c69566 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/admin/user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/admin/user.py @@ -15,13 +15,13 @@ class UsersApi(MethodView): decorators = [hiddify.super_admin] - @app.output(UserDTO(many=True)) + @app.output(UserSchema(many=True)) def get(self): users = User.query.all() or abort(502, "WTF!") return [user.to_dict(False) for user in users] - @app.input(UserDTO, arg_name="data") - @app.output(UserDTO) + @app.input(UserSchema, arg_name="data") + @app.output(UserSchema) def put(self, data): hiddify.add_or_update_user(**data) user = user_by_uuid(data['uuid']) or abort(502, "unknown issue! user is not added") @@ -33,12 +33,12 @@ def put(self, data): class UserApi(MethodView): decorators = [hiddify.super_admin] - @app.output(UserDTO) + @app.output(UserSchema) def get(self, uuid): user = user_by_uuid(uuid) or abort(404, "user not found") return user.to_dict(False) - @app.input(UserDTO, arg_name="data") + @app.input(UserSchema, arg_name="data") def patch(self, uuid, data): data = request.json uuid = data.get('uuid') or abort(422, "Parameter issue: 'uuid'") diff --git a/hiddifypanel/panel/commercial/restapi/v2/user/DTO.py b/hiddifypanel/panel/commercial/restapi/v2/user/DTO.py index 870bf4432..b8dc083c9 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/user/DTO.py +++ b/hiddifypanel/panel/commercial/restapi/v2/user/DTO.py @@ -1,12 +1,13 @@ from apiflask import Schema -from apiflask.fields import Integer, String, Float, URL, Enum +from apiflask.fields import Integer, String, Float, URL, Enum,List,Dict,Nested,Time from apiflask.validators import Length, OneOf from strenum import StrEnum from enum import auto +from enum import Enum as StdEnum from hiddifypanel.models import Lang -class ProfileDTO(Schema): +class ProfileSchema(Schema): profile_title = String(required=True) profile_url = URL(required=True) profile_usage_current = Float(required=True) @@ -22,7 +23,7 @@ class ProfileDTO(Schema): doh = URL() lang = Enum(Lang,required=True) -class ConfigDTO(Schema): +class ConfigSchema(Schema): name = String(required=True) domain = String(required=True) link = String(required=True) @@ -31,15 +32,56 @@ class ConfigDTO(Schema): security = String(required=True) type = String(required=True) -class MtproxyDTO(Schema): +class MtproxySchema(Schema): link = String(required=True) title = String(required=True) -class ShortDTO(Schema): +class ShortSchema(Schema): short = String(required=True) full_url = String(required=True) + expire_in = Integer(required=True) -class UserInfoChangableDTO(Schema): +class UserInfoChangableSchema(Schema): language = Enum(Lang,required=False) - telegram_id = Integer(required=False) \ No newline at end of file + telegram_id = Integer(required=False) + + +#region App Api DTOs +class AppInstallType(StdEnum): + GOOGLE_PLAY = auto() + APP_STORE = auto() + APPIMAGE = auto() + SNAPCRAFT = auto() + MICROSOFT_STORE = auto() + APK = auto() + DMG = auto() + SETUP = auto() + PORTABLE = auto() + OTHER = auto() + +class AppInstall(Schema): + title = String() + type = Enum(AppInstallType) + url = URL() +# class DeeplinkType(Enum): +# general = auto() +# all_sites = auto() +# blocked_sites = auto() +# foreign_sites = auto() +class AppSchema(Schema): + title = String(required=True) + description = String() + icon_url = URL() + guide_url = URL() + deeplink = URL() + install = List(Nested(AppInstall())) + +# this class is not a Data Transfer Object, It's just an enum +class Platform(StdEnum): + android = auto() + ios = auto() + windows = auto() + linux = auto() + mac = auto() +#endregion \ No newline at end of file diff --git a/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py b/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py index cd44f4d75..ba62ca20f 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py +++ b/hiddifypanel/panel/commercial/restapi/v2/user/__init__.py @@ -12,10 +12,11 @@ def init_app(app): with app.app_context(): - from .user import InfoAPI,MTProxiesAPI,AllConfigsAPI,ShortAPI + from .user import InfoAPI,MTProxiesAPI,AllConfigsAPI,ShortAPI,AppAPI bp.add_url_rule("/me/", view_func=InfoAPI) bp.add_url_rule("/mtproxies/", view_func=MTProxiesAPI) bp.add_url_rule("/all-configs/", view_func=AllConfigsAPI) bp.add_url_rule("/short/", view_func=ShortAPI) + bp.add_url_rule('/apps/',view_func=AppAPI) app.register_blueprint(bp) diff --git a/hiddifypanel/panel/commercial/restapi/v2/user/user.py b/hiddifypanel/panel/commercial/restapi/v2/user/user.py index c2ca4adf0..9ccc7cf77 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/user/user.py +++ b/hiddifypanel/panel/commercial/restapi/v2/user/user.py @@ -1,4 +1,5 @@ from urllib.parse import urlparse +import httpagentparser from flask_restful import Resource from hiddifypanel.models.config import StrConfig, hconfig from hiddifypanel.models.config_enum import ConfigEnum @@ -6,19 +7,25 @@ from hiddifypanel.models.user import days_to_reset, user_by_uuid from hiddifypanel.panel import hiddify from hiddifypanel.panel.database import db +import requests from flask_classful import FlaskView, route from flask.views import MethodView - +from flask import url_for from flask import current_app as app from flask import g, request from apiflask import APIFlask, Schema, abort from apiflask.fields import Integer, String, Float, URL,Dict from apiflask.validators import Length, OneOf +from flask_babelex import lazy_gettext as _ +from urllib.parse import quote_plus + +from enum import Enum,auto from hiddifypanel.panel.commercial.restapi.v2.user.DTO import * from hiddifypanel.panel.user import link_maker from hiddifypanel.panel.user.user import get_common_data +from hiddifypanel.utils import get_latest_release_url,do_base_64 @@ -28,11 +35,11 @@ class InfoAPI(MethodView): decorators = [hiddify.user_auth] - @app.output(ProfileDTO) + @app.output(ProfileSchema) def get(self): c = get_common_data(g.user_uuid, 'new') - dto = ProfileDTO() + dto = ProfileSchema() dto.profile_title = c['profile_title'] dto.profile_url = f"https://{urlparse(request.base_url).hostname}/{g.proxy_path}/{g.user_uuid}/#{g.user.name}" dto.profile_usage_current = g.user.current_usage_GB @@ -49,7 +56,7 @@ def get(self): dto.lang = c['user'].lang return dto - @app.input(UserInfoChangableDTO,arg_name='data') + @app.input(UserInfoChangableSchema,arg_name='data') def patch(self,data): if data['telegram_id']: try: @@ -72,7 +79,7 @@ def patch(self,data): class MTProxiesAPI(MethodView): decorators = [hiddify.user_auth] - @app.output(MtproxyDTO(many=True)) + @app.output(MtproxySchema(many=True)) def get(self): # get domains c = get_common_data(g.user_uuid, 'new') @@ -84,7 +91,7 @@ def get(self): hexuuid = hconfig(ConfigEnum.shared_secret, d.child_id).replace('-', '') telegram_faketls_domain_hex = hconfig(ConfigEnum.telegram_fakedomain, d.child_id).encode('utf-8').hex() server_link = f'tg://proxy?server={d.domain}&port=443&secret=ee{hexuuid}{telegram_faketls_domain_hex}' - dto = MtproxyDTO() + dto = MtproxySchema() dto.title = d.alias or d.domain dto.link = server_link dtos.append(dto) @@ -95,10 +102,10 @@ def get(self): class AllConfigsAPI(MethodView): decorators = [hiddify.user_auth] - @app.output(ConfigDTO(many=True)) + @app.output(ConfigSchema(many=True)) def get(self): def create_item(name, type, domain, protocol, transport, security, link): - dto = ConfigDTO() + dto = ConfigSchema() dto.name = name dto.type = type dto.domain = domain @@ -187,12 +194,317 @@ def create_item(name, type, domain, protocol, transport, security, link): class ShortAPI(MethodView): decorators = [hiddify.user_auth] - @app.output(ShortDTO) + @app.output(ShortSchema) def get(self): - short = hiddify.add_short_link( - "/"+hconfig(ConfigEnum.proxy_path)+"/"+g.user.uuid+"/") + expire_in = 5 + short = hiddify.add_short_link("/"+hconfig(ConfigEnum.proxy_path)+"/"+g.g.user_uuid+"/",expire_in) full_url = f"https://{urlparse(request.base_url).hostname}/{short}" - dto = ShortDTO() + dto = ShortSchema() dto.full_url = full_url dto.short = short - return dto \ No newline at end of file + dto.expire_in = expire_in + return dto + +class AppAPI(MethodView): + decorators = [hiddify.user_auth] + + def __init__(self) -> None: + super().__init__() + self.hiddify_github_repo = 'https://github.com/hiddify' + req_platfrom = request.args.get('platform') + + # fill platform(os) + self.platform = '' + if req_platfrom: + if req_platfrom.lower() not in Platform.__members__: + abort(400,'Your selected platform is invalid!') + self.platform = req_platfrom.lower() + else: + self.platform = httpagentparser.detect(request.user_agent.string)['os']['name'].lower() + + all_apps = request.args.get('all') + self.all_apps = True if all_apps and all_apps.lower() == 'true' else False + + self.user_panel_url = f"https://{urlparse(request.base_url).hostname}/{g.proxy_path}/{g.user_uuid}/" + self.user_panel_encoded_url = quote_plus(self.user_panel_url) + c = get_common_data(g.user_uuid, 'new') + self.subscription_link_url = f"{self.user_panel_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}" + self.subscription_link_encoded_url = do_base_64(self.subscription_link_url) + domain = c['db_domain'].alias or c['db_domain'].domain + self.profile_title = c['profile_title'] + # self.clash_all_sites = f"https://{domain}/{g.proxy_path}/{g.user_uuid}/clash/all.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_all_{domain}-{c['mode']}" + # self.clash_foreign_sites = f"https://{domain}/{g.proxy_path}/{g.user_uuid}/clash/normal.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_normal_{domain}-{c['mode']}" + # self.clash_blocked_sites = f"https://{domain}/{g.proxy_path}/{g.user_uuid}/clash/lite.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_lite_{c['db_domain'].alias or c['db_domain'].domain}-{c['mode']}" + # self.clash_meta_all_sites = f"https://{domain}/{g.proxy_path}/{g.user_uuid}/clash/meta/all.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_mall_{domain}-{c['mode']}" + # self.clash_meta_foreign_sites = f"https://{domain}/{g.proxy_path}/{g.user_uuid}/clash/meta/normal.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_mnormal_{domain}-{c['mode']}" + self.clash_meta_blocked_sites = f"https://{domain}/{g.proxy_path}/{g.user_uuid}/clash/meta/lite.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_mlite_{domain}-{c['mode']}" + @app.output(AppSchema(many=True)) + def get(self): + # output data + apps_data = [] + + if self.all_apps: + all_apps_dto = self.__get_all_apps_dto() + return all_apps_dto + + match self.platform: + case 'android': + hiddify_next_dto = self.__get_hiddify_next_app_dto() + hiddifyng_dto = self.__get_hiddifyng_app_dto() + v2rayng_dto = self.__get_v2rayng_app_dto() + hiddify_android_dto = self.__get_hiddify_android_app_dto() + apps_data += ([hiddify_next_dto,hiddifyng_dto,v2rayng_dto,hiddify_android_dto]) + case 'windows': + hiddify_next_dto = self.__get_hiddify_next_app_dto() + hiddifyng_dto = self.__get_hiddifyng_app_dto() + v2rayng_dto = self.__get_v2rayng_app_dto() + hiddify_clash_dto = self.__get_hiddify_clash_app_dto() + hiddifyn_dto = self.__get_hiddifyn_app_dto() + apps_data += ([hiddify_next_dto,hiddifyng_dto,hiddify_clash_dto,hiddifyn_dto]) + case 'ios': + stash_dto = self.__get_stash_app_dto() + shadowrocket_dto = self.__get_shadowrocket_app_dto() + foxray_dto = self.__get_foxray_app_dto() + streisand_dto = self.__get_streisand_app_dto() + loon_dto = self.__get_loon_app_dto() + apps_data += ([stash_dto,shadowrocket_dto,foxray_dto,streisand_dto,loon_dto]) + case 'linux': + hiddify_clash_dto = self.__get_hiddify_clash_app_dto() + hiddify_next_dto = self.__get_hiddify_next_app_dto() + hiddifyn_dto = self.__get_hiddifyn_app_dto() + apps_data += ([hiddify_next_dto,hiddify_clash_dto,hiddifyn_dto]) + case 'mac': + hiddify_clash_dto = self.__get_hiddify_clash_app_dto() + hiddify_next_dto = self.__get_hiddify_next_app_dto() + apps_data += ([hiddify_next_dto,hiddify_clash_dto]) + + return apps_data + def __get_all_apps_dto(self): + hiddifyn_app_dto = self.__get_hiddifyn_app_dto() + v2rayng_app_dto = self.__get_v2rayng_app_dto() + hiddifyng_app_dto = self.__get_hiddifyng_app_dto() + hiddify_android_app_dto = self.__get_hiddify_android_app_dto() + foxray_app_dto = self.__get_foxray_app_dto() + shadowrocket_app_dto = self.__get_shadowrocket_app_dto() + streisand_app_dto = self.__get_streisand_app_dto() + loon_app_dto = self.__get_loon_app_dto() + stash_app_dto = self.__get_stash_app_dto() + hiddify_clash_app_dto = self.__get_hiddify_clash_app_dto() + hiddify_next_app_dto = self.__get_hiddify_next_app_dto() + return [ + hiddifyn_app_dto,v2rayng_app_dto,hiddifyng_app_dto,hiddify_android_app_dto, + foxray_app_dto,shadowrocket_app_dto,streisand_app_dto, + loon_app_dto,stash_app_dto,hiddify_clash_app_dto,hiddify_next_app_dto + ] + def __get_app_icon_url(self,app_name): + base = f'https://{urlparse(request.base_url).hostname}' + url = '' + if app_name == _('app.hiddify.next.title'): + url = base + url_for('static',filename='apps-icon/hiddify_next.ico') + elif app_name == _('app.hiddifyn.title'): + url = base + url_for('static',filename='apps-icon/hiddifyn.ico') + elif app_name == _('app.v2rayng.title'): + url = base + url_for('static',filename='apps-icon/v2rayng.ico') + elif app_name == _('app.hiddifyng.title'): + url = base + url_for('static',filename='apps-icon/hiddifyng.ico') + elif app_name == _('app.hiddify-android.title'): + url = base + url_for('static',filename='apps-icon/hiddify_android.ico') + elif app_name == _('app.foxray.title'): + url = base + url_for('static',filename='apps-icon/foxray.ico') + elif app_name == _('app.shadowrocket.title'): + url = base + url_for('static',filename='apps-icon/shadowrocket.ico') + elif app_name == _('app.streisand.title'): + url = base + url_for('static',filename='apps-icon/streisand.ico') + elif app_name == _('app.loon.title'): + url = base + url_for('static',filename='apps-icon/loon.ico') + elif app_name == _('app.stash.title'): + url = base + url_for('static',filename='apps-icon/stash.ico') + elif app_name == _('app.hiddify.clash.title'): + url = base + url_for('static',filename='apps-icon/hiddify_clash.ico') + + return url + + def __get_app_install_dto(self,install_type:AppInstallType,url,title=''): + install_dto = AppInstall() + install_dto.title = title + install_dto.type = install_type + install_dto.url = url + return install_dto + + def __get_hiddifyn_app_dto(self): + dto = AppSchema() + dto.title = _('app.hiddifyn.title') + dto.description = _('app.hiddifyn.description') + dto.icon_url = self.__get_app_icon_url(_('app.hiddifyn.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=o9L2sI2T53Q' + dto.deeplink = f'hiddify://install-sub?url={self.subscription_link_url}' + + ins_url = f'{self.hiddify_github_repo}/HiddifyN/releases/latest/download/HiddifyN.zip' + dto.install = [self.__get_app_install_dto(AppInstallType.APK,ins_url)] + return dto + + def __get_v2rayng_app_dto(self): + dto = AppSchema() + dto.title = _('app.v2rayng.title') + dto.description = _('app.v2rayng.description') + dto.icon_url = self.__get_app_icon_url(_('app.v2rayng.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=6HncctDHXVs' + dto.deeplink = f'v2rayng://install-sub/?url={self.user_panel_encoded_url}' + + # make v2rayng latest version url download + latest_url, version = get_latest_release_url(f'https://github.com/2dust/v2rayNG/') + ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/v2rayNG_{version}.apk' + dto.install = [self.__get_app_install_dto(AppInstallType.APK,ins_url),] + return dto + + def __get_hiddifyng_app_dto(self): + dto = AppSchema() + dto.title = _('app.hiddifyng.title') + dto.description = _('app.hiddifyng.description') + dto.icon_url = self.__get_app_icon_url(_('app.hiddifyng.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=qDbI72J-INM' + dto.deeplink = f'hiddify://install-sub/?url={self.user_panel_encoded_url}' + + latest_url,version = get_latest_release_url(f'{self.hiddify_github_repo}/HiddifyNG/') + ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyNG.apk' + dto.install = [self.__get_app_install_dto(AppInstallType.APK,ins_url),] + return dto + + def __get_hiddify_android_app_dto(self): + dto = AppSchema() + dto.title = _('app.hiddify-android.title') + dto.description = _('app.hiddify-android.description') + dto.icon_url = self.__get_app_icon_url(_('app.hiddify-android.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=mUTfYd1_UCM' + dto.deeplink = f'clash://install-config/?url={self.user_panel_encoded_url}' + latest_url,version = get_latest_release_url(f'{self.hiddify_github_repo}/HiddifyClashAndroid/') + ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/hiddify-{version}-meta-alpha-universal-release.apk' + dto.install = [self.__get_app_install_dto(AppInstallType.APK,ins_url),] + return dto + + def __get_foxray_app_dto(self): + dto = AppSchema() + dto.title = _('app.foxray.title') + dto.description = _('app.foxray.description') + dto.icon_url = self.__get_app_icon_url(_('app.foxray.title')) + dto.guide_url = '' + dto.deeplink = f'https://yiguo.dev/sub/add/?url={do_base_64(self.subscription_link_encoded_url)}#{self.profile_title}' + + ins_url = 'https://apps.apple.com/us/app/foxray/id6448898396' + dto.install = [self.__get_app_install_dto(AppInstallType.APP_STORE,ins_url),] + return dto + + def __get_shadowrocket_app_dto(self): + dto = AppSchema() + dto.title = _('app.shadowrocket.title') + dto.description = _('app.shadowrocket.description') + dto.icon_url = self.__get_app_icon_url(_('app.shadowrocket.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=F2bC_mtbYmQ' + dto.deeplink = f'sub://{do_base_64(self.user_panel_url)}' + + ins_url = 'https://apps.apple.com/us/app/shadowrocket/id932747118' + dto.install = [self.__get_app_install_dto(AppInstallType.APP_STORE,ins_url),] + return dto + def __get_streisand_app_dto(self): + dto = AppSchema() + dto.title = _('app.streisand.title') + dto.description = _('app.streisand.description') + dto.icon_url = self.__get_app_icon_url(_('app.streisand.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=jaMkZTLH2QY' + dto.deeplink = f'streisand://import/{self.user_panel_url}#{self.profile_title}' + ins_url = 'https://apps.apple.com/app/id6450534064' + dto.install = [self.__get_app_install_dto(AppInstallType.APP_STORE,ins_url),] + return dto + def __get_loon_app_dto(self): + dto = AppSchema() + dto.title = _('app.loon.title') + dto.description = _('app.loon.description') + dto.icon_url = self.__get_app_icon_url(_('app.loon.title')) + dto.guide_url = '' + dto.deeplink = f'loon://import?nodelist={self.user_panel_encoded_url}' + ins_url = 'https://apps.apple.com/app/id1373567447' + dto.install = [self.__get_app_install_dto(AppInstallType.APP_STORE,ins_url)] + def __get_stash_app_dto(self): + dto = AppSchema() + dto.title = _('app.stash.title') + dto.description = _('app.stash.description') + dto.icon_url = self.__get_app_icon_url(_('app.stash.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=D0Xv54nRSY8' + dto.deeplink = f'clash://install-config/?url={self.user_panel_encoded_url}' + + ins_url = 'https://apps.apple.com/us/app/stash-rule-based-proxy/id1596063349' + dto.install = [self.__get_app_install_dto(AppInstallType.APP_STORE,ins_url),] + return dto + + def __get_hiddify_clash_app_dto(self): + dto = AppSchema() + dto.title = _('app.hiddify.clash.title') + dto.description = _('app.hiddify.clash.description') + dto.icon_url = self.__get_app_icon_url(_('app.hiddify.clash.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=omGIz97mbzM' + dto.deeplink = f'clash://install-config/?url={self.user_panel_encoded_url}' + + # make hiddify clash latest version url download + latest_url, version = get_latest_release_url(f'{self.hiddify_github_repo}/hiddifydesktop') + version = version.replace('v','') + ins_url = '' + ins_type = None + match self.platform: + case 'windows': + ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}_x64_en-US.msi' + ins_type = AppInstallType.SETUP + case 'linux': + ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/hiddify-clash-desktop_{version}_amd64.AppImage' + ins_type = AppInstallType.APPIMAGE + case 'mac': + ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}x64.dmg' + ins_type = AppInstallType.DMG + + dto.install = [self.__get_app_install_dto(ins_type,ins_url)] + return dto + + def __get_hiddify_next_app_dto(self): + dto = AppSchema() + dto.title = _('app.hiddify.next.title') + dto.description = _('app.hiddify.next.description') + dto.icon_url = self.__get_app_icon_url(_('app.hiddify.next.title')) + dto.guide_url = 'https://www.youtube.com/watch?v=vUaA1AEUy1s' + dto.deeplink = f'hiddify://install-config/?url={self.user_panel_encoded_url}' + + # availabe installatoin types + installation_types = [] + match self.platform: + case 'adnroid': + installation_types = [AppInstallType.APK,AppInstallType.GOOGLE_PLAY] + case 'windows': + installation_types = [AppInstallType.SETUP,AppInstallType.PORTABLE] + case 'linux': + installation_types = [AppInstallType.APPIMAGE] + case 'mac': + installation_types = [AppInstallType.DMG] + + install_dtos = [] + for install_type in installation_types: + install_dto = AppInstall() + ins_url = '' + match install_type: + case AppInstallType.APK: + ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/hiddify-android-universal.apk' + case AppInstallType.GOOGLE_PLAY: + ins_url = 'https://play.google.com/store/apps/details?id=app.hiddify.com' + case AppInstallType.SETUP: + ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/hiddify-windows-x64-setup.zip' + case AppInstallType.PORTABLE: + ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/hiddify-windows-x64-portable.zip' + case AppInstallType.APPIMAGE: + ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/hiddify-linux-x64.zip' + case AppInstallType.DMG: + ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/hiddify-macos-universal.zip' + + install_dto = self.__get_app_install_dto(install_type,ins_url) + install_dtos.append(install_dto) + + dto.install = install_dtos + return dto + + \ No newline at end of file diff --git a/hiddifypanel/utils.py b/hiddifypanel/utils.py index 52c112234..2d1916b46 100644 --- a/hiddifypanel/utils.py +++ b/hiddifypanel/utils.py @@ -44,7 +44,10 @@ def static_url_for(**values): orig = url_for("static", **values) return orig.split("user_secret")[0] - +def get_latest_release_url(repo): + latest_url = requests.get(f'{repo}/releases/latest').url.strip() + version = latest_url.split('tag/')[1].strip() + return (latest_url,version) def get_latest_release_version(repo_name): try: url = f"https://github.com/hiddify/{repo_name}/releases/latest" From 415586cd01031ff460e337454733b49276ffff94 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 10:48:44 +0330 Subject: [PATCH 14/16] add: client apps icon for apps api --- hiddifypanel/static/apps-icon/foxray.ico | Bin 0 -> 8243 bytes .../static/apps-icon/hiddify_android.ico | Bin 0 -> 34179 bytes hiddifypanel/static/apps-icon/hiddify_clash.ico | Bin 0 -> 37408 bytes hiddifypanel/static/apps-icon/hiddify_next.ico | 1 + hiddifypanel/static/apps-icon/hiddifyn.ico | Bin 0 -> 2372 bytes hiddifypanel/static/apps-icon/hiddifyng.ico | Bin 0 -> 4100 bytes hiddifypanel/static/apps-icon/loon.ico | Bin 0 -> 4413 bytes hiddifypanel/static/apps-icon/shadowrocket.ico | Bin 0 -> 23251 bytes hiddifypanel/static/apps-icon/stash.ico | Bin 0 -> 10142 bytes hiddifypanel/static/apps-icon/streisand.ico | Bin 0 -> 2466 bytes hiddifypanel/static/apps-icon/v2rayng.ico | Bin 0 -> 4050 bytes 11 files changed, 1 insertion(+) create mode 100644 hiddifypanel/static/apps-icon/foxray.ico create mode 100644 hiddifypanel/static/apps-icon/hiddify_android.ico create mode 100644 hiddifypanel/static/apps-icon/hiddify_clash.ico create mode 100644 hiddifypanel/static/apps-icon/hiddify_next.ico create mode 100644 hiddifypanel/static/apps-icon/hiddifyn.ico create mode 100644 hiddifypanel/static/apps-icon/hiddifyng.ico create mode 100644 hiddifypanel/static/apps-icon/loon.ico create mode 100644 hiddifypanel/static/apps-icon/shadowrocket.ico create mode 100644 hiddifypanel/static/apps-icon/stash.ico create mode 100644 hiddifypanel/static/apps-icon/streisand.ico create mode 100644 hiddifypanel/static/apps-icon/v2rayng.ico diff --git a/hiddifypanel/static/apps-icon/foxray.ico b/hiddifypanel/static/apps-icon/foxray.ico new file mode 100644 index 0000000000000000000000000000000000000000..fc00f62a669a2e3580430bc1de8e4029da58f127 GIT binary patch literal 8243 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 M#NRKSgOu_!Ku^@!9 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ᑺ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 literal 0 HcmV?d00001 diff --git a/hiddifypanel/static/apps-icon/hiddify_next.ico b/hiddifypanel/static/apps-icon/hiddify_next.ico new file mode 100644 index 000000000..e76434d6d --- /dev/null +++ b/hiddifypanel/static/apps-icon/hiddify_next.ico @@ -0,0 +1 @@ +{"payload":{"allShortcutsEnabled":false,"fileTree":{"assets/images":{"items":[{"name":"source","path":"assets/images/source","contentType":"directory"},{"name":"logo.svg","path":"assets/images/logo.svg","contentType":"file"},{"name":"tray_icon.ico","path":"assets/images/tray_icon.ico","contentType":"file"},{"name":"tray_icon.png","path":"assets/images/tray_icon.png","contentType":"file"},{"name":"tray_icon_connected.ico","path":"assets/images/tray_icon_connected.ico","contentType":"file"},{"name":"tray_icon_disconnected.ico","path":"assets/images/tray_icon_disconnected.ico","contentType":"file"}],"totalCount":6},"assets":{"items":[{"name":"core","path":"assets/core","contentType":"directory"},{"name":"fonts","path":"assets/fonts","contentType":"directory"},{"name":"images","path":"assets/images","contentType":"directory"},{"name":"translations","path":"assets/translations","contentType":"directory"}],"totalCount":4},"":{"items":[{"name":".github","path":".github","contentType":"directory"},{"name":".vscode","path":".vscode","contentType":"directory"},{"name":"android","path":"android","contentType":"directory"},{"name":"assets","path":"assets","contentType":"directory"},{"name":"docs","path":"docs","contentType":"directory"},{"name":"ios","path":"ios","contentType":"directory"},{"name":"lib","path":"lib","contentType":"directory"},{"name":"libcore","path":"libcore","contentType":"submodule","submoduleUrl":"/hiddify/hiddify-next-core/tree/2f865d5c8a11966ab11e777bc0b67a999fa07126","submoduleDisplayName":"libcore @ 2f865d5"},{"name":"linux","path":"linux","contentType":"directory"},{"name":"macos","path":"macos","contentType":"directory"},{"name":"snap","path":"snap","contentType":"directory"},{"name":"test","path":"test","contentType":"directory"},{"name":"windows","path":"windows","contentType":"directory"},{"name":".gitchangelog.rc","path":".gitchangelog.rc","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":".gitmodules","path":".gitmodules","contentType":"file"},{"name":".metadata","path":".metadata","contentType":"file"},{"name":".prettierrc","path":".prettierrc","contentType":"file"},{"name":".release_notes.tpl","path":".release_notes.tpl","contentType":"file"},{"name":"CHANGELOG.md","path":"CHANGELOG.md","contentType":"file"},{"name":"CODE_OF_CONDUCT.md","path":"CODE_OF_CONDUCT.md","contentType":"file"},{"name":"CONTRIBUTING.md","path":"CONTRIBUTING.md","contentType":"file"},{"name":"LICENSE.md","path":"LICENSE.md","contentType":"file"},{"name":"Makefile","path":"Makefile","contentType":"file"},{"name":"README.md","path":"README.md","contentType":"file"},{"name":"README_cn.md","path":"README_cn.md","contentType":"file"},{"name":"README_fa.md","path":"README_fa.md","contentType":"file"},{"name":"README_ru.md","path":"README_ru.md","contentType":"file"},{"name":"analysis_options.yaml","path":"analysis_options.yaml","contentType":"file"},{"name":"appcast.xml","path":"appcast.xml","contentType":"file"},{"name":"build.yaml","path":"build.yaml","contentType":"file"},{"name":"dependencies.properties","path":"dependencies.properties","contentType":"file"},{"name":"distribute_options.yaml","path":"distribute_options.yaml","contentType":"file"},{"name":"project.inlang.json","path":"project.inlang.json","contentType":"file"},{"name":"pubspec.lock","path":"pubspec.lock","contentType":"file"},{"name":"pubspec.yaml","path":"pubspec.yaml","contentType":"file"}],"totalCount":36}},"fileTreeProcessingTime":18.137275000000002,"foldersToFetch":[],"reducedMotionEnabled":null,"repo":{"id":643504282,"defaultBranch":"main","name":"hiddify-next","ownerLogin":"hiddify","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2023-05-21T11:30:14.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/126981719?v=4","public":true,"private":false,"isOrgOwned":true},"symbolsExpanded":false,"treeExpanded":true,"refInfo":{"name":"main","listCacheKey":"v0:1700410666.0","canEdit":false,"refType":"branch","currentOid":"82bbcf34074584405780f244a83f0541d5cea903"},"path":"assets/images/logo.svg","currentUser":null,"blob":{"rawLines":["","","","","","","","","","","",""],"stylingDirectives":null,"csv":null,"csvError":null,"dependabotInfo":{"showConfigurationBanner":false,"configFilePath":null,"networkDependabotPath":"/hiddify/hiddify-next/network/updates","dismissConfigurationNoticePath":"/settings/dismiss-notice/dependabot_configuration_notice","configurationNoticeDismissed":null,"repoAlertsPath":"/hiddify/hiddify-next/security/dependabot","repoSecurityAndAnalysisPath":"/hiddify/hiddify-next/settings/security_analysis","repoOwnerIsOrg":true,"currentUserCanAdminRepo":false},"displayName":"logo.svg","displayUrl":"https://viewscreen.githubusercontent.com/view/svg?browser=unknown_browser&bypass_fastly=true&color_mode=auto&commit=82bbcf34074584405780f244a83f0541d5cea903&device=unknown_device&docs_host=https%3A%2F%2Fdocs.github.com&enc_url=68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f686964646966792f686964646966792d6e6578742f383262626366333430373435383434303537383066323434613833663035343164356365613930332f6173736574732f696d616765732f6c6f676f2e737667&logged_in=false&nwo=hiddify%2Fhiddify-next&path=assets%2Fimages%2Flogo.svg&platform=unknown_platform&repository_id=643504282&repository_type=Repository&version=0","headerInfo":{"blobSize":"1.6 KB","deleteInfo":{"deleteTooltip":"You must be signed in to make or propose changes"},"editInfo":{"editTooltip":"You must be signed in to make or propose changes"},"ghDesktopPath":"https://desktop.github.com","gitLfsPath":null,"onBranch":true,"shortPath":"831b40c","siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Fhiddify%2Fhiddify-next%2Fblob%2Fmain%2Fassets%2Fimages%2Flogo.svg","isCSV":false,"isRichtext":false,"toc":null,"lineInfo":{"truncatedLoc":"12","truncatedSloc":"12"},"mode":"file"},"image":false,"isCodeownersFile":null,"isPlain":false,"isValidLegacyIssueTemplate":false,"issueTemplateHelpUrl":"https://docs.github.com/articles/about-issue-and-pull-request-templates","issueTemplate":null,"discussionTemplate":null,"language":"SVG","languageID":337,"large":false,"loggedIn":false,"newDiscussionPath":"/hiddify/hiddify-next/discussions/new","newIssuePath":"/hiddify/hiddify-next/issues/new","planSupportInfo":{"repoIsFork":null,"repoOwnedByCurrentUser":null,"requestFullPath":"/hiddify/hiddify-next/blob/main/assets/images/logo.svg","showFreeOrgGatedFeatureMessage":null,"showPlanSupportBanner":null,"upgradeDataAttributes":null,"upgradePath":null},"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_dockerfile","dismissStackNoticePath":"/settings/dismiss-notice/publish_stack_from_file","releasePath":"/hiddify/hiddify-next/releases/new?marketplace=true","showPublishActionBanner":false,"showPublishStackBanner":false},"rawBlobUrl":"https://github.com/hiddify/hiddify-next/raw/main/assets/images/logo.svg","renderImageOrRaw":false,"richText":null,"renderedFileInfo":{"identityUUID":"ea4ffdc6-dd34-41e7-8f45-95e221158cfe","renderFileType":"svg","size":1639},"shortPath":null,"tabSize":8,"topBannersInfo":{"overridingGlobalFundingFile":false,"globalPreferredFundingPath":null,"repoOwner":"hiddify","repoName":"hiddify-next","showInvalidCitationWarning":false,"citationHelpUrl":"https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files","showDependabotConfigurationBanner":false,"actionsOnboardingTip":null},"truncated":false,"viewable":true,"workflowRedirectUrl":null,"symbols":{"timedOut":false,"notAnalyzed":true,"symbols":[]}},"copilotInfo":null,"copilotAccessAllowed":false,"csrf_tokens":{"/hiddify/hiddify-next/branches":{"post":"PkehDM5W7ZUD5JvtWfmF2n4Cvf_e06ULO0onIBHI2yeCjeLjicgZkb_AOgUKlzxpPzg2fPLtVeqIGNrZPACW_A"},"/repos/preferences":{"post":"o44zgEoA00uTlp5U17C9C8mwOC4MYNjD5jyO7no2DSO0N49kPTviHthqLeHstUXWdxv-TT-OuBdro7OQCji66w"}}},"title":"hiddify-next/assets/images/logo.svg at main · hiddify/hiddify-next"} \ No newline at end of file diff --git a/hiddifypanel/static/apps-icon/hiddifyn.ico b/hiddifypanel/static/apps-icon/hiddifyn.ico new file mode 100644 index 0000000000000000000000000000000000000000..5d2c61b940b5a1bb64bcfd8658d49857b48f98df GIT binary patch literal 2372 zcmcguc{tQv8=mZ(X>DI%s1dJS>t3nL{`24vs#|s{`}}Z`yjCZveV!djJanwiFC+b-(J7GBVYp4 zE}7BxTUp}AE8dC+`+^?TJ32`YnXh4Sb#Ytlh)w+d^nz*{yVRZ)Pu+^H8&p+QmAy5H z`K}Doh70Wy5fPG*5CMZFU@)+$DeTrQQ_wB~Na%k=kaKf;muFkkRG235B#ZjeYgkUQ z5zBL_If6Ee-uTK@3;e8)8OVijIGjr?Yd({uP2!%cFzPHyS}s1iwG?7|{9Ervt{}a< zC`q%Uq}K95)`bwmZz|w`G@po~ICUt#%5Y%5vO}Oo;pbNQ*eCaw1h~K z$Cu(o@thib`gHO31V!1yv1(>ley{S?V>Pmb25s^n6`AmA++*jQvn{4MhSWEEUWo9| zw!lmMF1;aJKG|!l^Y<6$pHYf`&|MA3L7kd3&O*Wpz& zytc(-`V+0h8e1-NyNcrW!eQW!u|bD7F(P^0NUnps5Fy~{RYYPF_&y;NiJKZ}i$c5D z&NrD$!N9^KpB(~~OumLlT-}FCcDsSdBJ<~+0VSr@(RF=(IG#fMR(HUv9Xf19&{JoU z;Bs8g@5dTVCH3H|g=jVS=kd_kIi72-;G>uq+}EI%02I7QI8U?4#Wv)l-o3-A6AN;$ zdjX|b!1f}Iu5|+n*3Q+rS}-pEVh~>*vCpny_a| z82J3o#RI7HVOK_Vj~}i&ik+4eYk8R=1*>8IVv(x+$xfE&XNvhP>le0qZZggfD@FL-~n#jqN6<+N0KjJYjpoTxj zRC8KNF4yMa%t`NStDD0o4#rKn%na83fte*tVu!y(J{D7(8lrZ=mD<(#r7(!Tzu%3g zOJ#rv=Z@#^{-Z_ip#F3m1WUSbNqkH*P_j=K#}8+oRk(`0+h5cueGPJ7g%;yuAJOlG zlP~?5E^zk_xh9X|eOzw&AY9A^%K0*;H zD?aoYGzD_Ki&*XplMJTay6dI}raNipV1#@SqDpp9iOOk3#?zSp7E#Zr6P*U-~t%vYky@hZrqv0Xr*Pg*}iw|N;dUt zD-8@tI7t;QxLtH?yh>=l2!`Do&qFKvJZps@zs{>CqpHaEuCQJ8BfEsWuz#AS)@pwtj1N;ULrwW(Y zKYUmpv5G+3b5LDG(*lTsw9=uPSnQFJaWOX3@L8t9xen(YqFP_)r0%`9<9nsPTK~OJ zgg#TN-$!v#)NMbNGya{&VrRO%zw;*?GOCF4qh}*d>yMV2sXerMYaK;J2fh_J0E53&zPz}HI(`-SL~*N+!)9fxsdUpt*y5)Q7T1wG@(*Vh(uZT&}eA?|8)TW=`y~U>;i$dx2Y+# VHp%jWTA(`u*;qPQl$d+p{u8j3H-rEH literal 0 HcmV?d00001 diff --git a/hiddifypanel/static/apps-icon/hiddifyng.ico b/hiddifypanel/static/apps-icon/hiddifyng.ico new file mode 100644 index 0000000000000000000000000000000000000000..d1105266d914ac096354a1198da65b1cf988ccd6 GIT binary patch literal 4100 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 literal 0 HcmV?d00001 diff --git a/hiddifypanel/static/apps-icon/loon.ico b/hiddifypanel/static/apps-icon/loon.ico new file mode 100644 index 0000000000000000000000000000000000000000..cf8efff1cb37c63c9ec452e5ffd45d774d9f227f GIT binary patch literal 4413 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 literal 0 HcmV?d00001 diff --git a/hiddifypanel/static/apps-icon/stash.ico b/hiddifypanel/static/apps-icon/stash.ico new file mode 100644 index 0000000000000000000000000000000000000000..d4493525cc73cc5c557812411c632e66e1518d9a GIT binary patch literal 10142 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 M07*qoM6N<$g8X>kpa1{> literal 0 HcmV?d00001 diff --git a/hiddifypanel/static/apps-icon/streisand.ico b/hiddifypanel/static/apps-icon/streisand.ico new file mode 100644 index 0000000000000000000000000000000000000000..1214b32c90259db2f90a33976db193e65ec7346a GIT binary patch literal 2466 zcmbuB`8(T*6URTf#IZ`0mPphQRcGs{)>#zYl+wf*p(KQopzf=&%TnD`5H_k8m8~nJ zN)f86j@H%MEn3>Jj-bm{+7#7g+pOXh@TpddQv{{=`M0|;s0EN~VA(gwf?5JVd!=?B;V00AJN|IGTYfux`? zI7Aw(2!J3EFcb=f|E~xFL!bZ*A*G~c>WG8y#gmj(w8=WUW=>vlSqUHj4BqAZ3)#&u zkQ5lY3qk;35CjYZLtzluKjmEx07bx*_G+2pNZO84%4SYF7;=1;d0Z3TtLMeMes(PbLOh{HGHrzXsNDC>aWyl`lbiMh_+iBENI5lCXyyDlD`U2oh*9=0ehrb*g-g-9o##7)?~*vV5t|N(S_}&>LyWV~mct zu2b`0hCE!0m4`=XkE$8jmuH1m^4^a$Y>W9}j&X{2=WT9MB0!lE;KEKG;?=`M=#__C z&zq7brlT{G9|{I!qV0#*)`BiQ;jEQHyRF)O?KShJSf@yhuTZ~>#WmVc4R2iQa%-AY zZ4V?3kPRL+M+PgsDVzB=32t~3dw^}fP{Mmv`JwN!sN1xL-mGx{YvGD*r(bvffX8@t zaGd+Gj$tD@#Xci*_3fL2Ixdw0&24ws4n23;Gv4mGV`MBGGn%CRadoU!Puqe5ZW8_M zAJExb5|uUviX_0FejA@3$#v$q9YQb6Fh$ZP zHr!8luS7&ga|KUd6@+%^q14oWecRmh^wk&RV7u3cM|Ar+WIn>%q99-jD!c_cI!ErFgE<;F<=qHQO$Q#e5-AWgVpYthwecNr3# zR3tiyc=6J>`Td;X(K1)&ZH$2_nhIKmU>D_ssk+7-)TF~tktGtK~pSa;uRzsCP z!W&Am`>APsjqA#*3|iST_#}uZ^&zc`Kyw8>*2@nVebwi2r4{SQycYUCbF46X%a^qz z$O*1%$_WNHl)4TCWobWj8msNKzIpl)=!E}=$e8w6o(|(#^gaiYhG~`-IwjfeRZIE| zQvo)$?~BReHMjg+nepia@o64OL-P@GEPxCkZjg8hx#LD6o z4u=MtuXMys)Gj!)Ept!F=W9UkY(f>|-f&;HpOAH+@u(+HeF&n>M@oR=!d0~3XlnB~ zr@b|6n*^`Th~K(P5KCQ6p>tRF!>(`!5#9*~wVW2GVI<}dSydL3A68-e2WL7!#zapS zWP3)~eZ(61*x&i187gLx_=^bg*UPl)&!ot`UbxWb#XG@QEvk6;Wah#YQ$x#SgaL%T zs^$GPc-ZHk)rCn)xK$c?qJ0m;3$HGTx`E zcQMPmZI&{S$xeTfxKiZoGjozt^h0d7ed6}Gv!7Q>!~o;b@7GZ{&0@#z>x<3v74+6r zJV_NAPpCvZkEO=eg=!n&`6?rKg^afW%i=vtHE(;2TLPnX7fU}^RcU4eM=O-8iHpY6 zlLl7w81(32G+KY@-IN44yv3H}cK;4IA|sr+Y#SX(({WuIJ0ZY*GDD<~o6ET`qrJ0@T06)I8?7@qCFxkBDwe zN(|F#+dEafC;?PDRUSkRYD6AO*t9x6Bo3GixODZyU#4%Iz8{WjRb#Cfr?TsS=*zuFRwU}R?v66Q{A=-(RUhuW(5w>v4!7d9YUR`{RU z7i9ztP|mzVw+JWOp`1inMuHr1GLQC|%&$!9?y~dUphs zdD{km#?P~a(CW5kA}o|m$e^f(#m2+&7d7bha`aG-SDFPQ1Q%BbV!BqYMFI?buHx^z zUek9hFe7ct^#Zi+xxWTu;m%-><`|cUz1TYI1^Z;wi<&FeZO>g&(8xJh73l@2e*YT1 z%6GAtct1Z8MtIV_C;4k7THdh6#lT?qC>h4oZ!YJQ&5# zriSz&;MPpnXF=-SrJiLHAOW>aZmZ>VAFp8~dgEM_@x0qzLAktSR@`?>tNsLLr?CiC pU`TNJHau{S#rna8_SeiLAQ0ck&5st}oJ$o4Z0tS&|8$kS{u^}kP-y@F literal 0 HcmV?d00001 diff --git a/hiddifypanel/static/apps-icon/v2rayng.ico b/hiddifypanel/static/apps-icon/v2rayng.ico new file mode 100644 index 0000000000000000000000000000000000000000..2535105dd4dfc636b31e65c9451be485d464848a GIT binary patch literal 4050 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 Sw*xNe47yr|nx8cs@&5zOY0b<4 literal 0 HcmV?d00001 From 0b5ed4c14b3473769385dcf0e604855dbb870e08 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 10:54:19 +0330 Subject: [PATCH 15/16] chg: ui files --- .../commercial/restapi/v2/hello/__init__.py | 2 +- hiddifypanel/templates/500.html | 19 ++++++++++--------- hiddifypanel/templates/error.html | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py b/hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py index 0d59f1957..175fad1e8 100644 --- a/hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +++ b/hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py @@ -12,5 +12,5 @@ def init_app(app): with app.app_context(): from .hello import Hello - bp.add_url_rule('/hello/', view_func=Hello.as_view('hello')) + bp.add_url_rule('/', view_func=Hello.as_view('hello')) app.register_blueprint(bp) diff --git a/hiddifypanel/templates/500.html b/hiddifypanel/templates/500.html index 5bdab4b39..cf41872d6 100644 --- a/hiddifypanel/templates/500.html +++ b/hiddifypanel/templates/500.html @@ -4,20 +4,21 @@

500

-

{{_('Oops! Something went wrong.')}} Hiddify Panel {{version}}

- %if has_update +

{{_('Oops! Something went wrong.')}} Hiddify + Panel {{version}}

+ %if has_update

{{_('This version of hiddify panel is outdated. Please update it from admin area.')}}

%else

{{_('Please create an issue on Github.')}} -

- - - -
+
+ + + +

%endif - +
%if not has_update @@ -29,6 +30,6 @@

{{_('This version of hiddify panel is outdated. Please update it from admin {{trace}} %endif - + {%endblock%} \ No newline at end of file diff --git a/hiddifypanel/templates/error.html b/hiddifypanel/templates/error.html index 7d6c81f2b..8b9bb8516 100644 --- a/hiddifypanel/templates/error.html +++ b/hiddifypanel/templates/error.html @@ -2,13 +2,13 @@ {%block body%}
-

{{error.code}}

+

{{error.status_code}} {{error.message}}

-

{{_('Oops! Something went wrong.')}}

+

- {{error|safe}} + {{error.detail if error.detail}}

- +
From 086a0bf777bdcac3931d3bb619f21d13fd056c80 Mon Sep 17 00:00:00 2001 From: Sarina Date: Tue, 21 Nov 2023 11:11:28 +0330 Subject: [PATCH 16/16] fix: hiddify_next.ico --- hiddifypanel/static/apps-icon/hiddify_next.ico | Bin 9712 -> 850 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/hiddifypanel/static/apps-icon/hiddify_next.ico b/hiddifypanel/static/apps-icon/hiddify_next.ico index e76434d6d717e8d98ee8b6aae5d606762112b4ce..319ee17f5cae295d7dd22d1626e6d292f479a582 100644 GIT binary patch literal 850 zcmV-Y1FigtP)OD)PwRwyXSPs_|n zKTwmDQN(EtUQ8kkv{8JiiJ12Krt1pr94 z6v;Z&LoxsW0-s4lK~z|U?U-9@6G0Tm&zafW6MLanXo*4*ywr!HMM*(QMez&x0RJ=SWegeU^RI5-G6f9OK8VYKy)-)z*Hf>@y*~{@E+Admmy3KAZxPKP* z%+8#%KW69592SWPHj~)D2KZebkVqboNFI<#9*{`?H;^hXEGM5^kC7UK0zkQE_m*D{ z0zhoH)#=#?pwjzSxzRxY&SL{qNm>tf7f4;oWJWHk3%6PD5FiI20JL^-aSc_z&CXwI z*YmGgs1J~@)5}F5y}ZQ2oxp;&yc`B9OpSh@n_g)WX#li(5fP=WBRIB(l=O$}gEM;J zezMvG zg*RjJlj9iZWsxIwhH?NX_w;UY@)QdXteMPONnV_|g_3`@mkb`=+S+&0Eg;fW^zZ-M z)>6%WDkslju-EKC%A|`xfR?))a+EreZ!rUb)L|XcUfM6eoA`JMmDG$aFI!sg3{`Vq zGQ$_N{6iM$(3O&VK;ju9UCWGLP2GtbrCAp2Mv#Q(Q9TmOP;ur(ZX|)e9u{>btLp{I z4JTNrfBi>e{StgK^zT_Cm2lvqMJDq>r3`T_j zkvBODw2|@yc`Io$849wPvox@go!Y2KZHZ7gsQMO&o9BSEInpY>>@sPy0C7`qiuCHwhM|)-54_zRkJPVq0ks@`q{=B| zCLmo4cI>Lz(Y?pe7eekc_eORmYIa~-SET2LxfjUb(KhQhqW9BNAP-0+4@e{rNF)zP cq)i9(1HM!2d)s3B5&!@I07*qoM6N<$g36w6`2YX_ literal 9712 zcmbta>u%f15q=dyMU57SDBdqd-BVe%WA`|g8(-3qAn{fp85C)i`)u$I+{-0kufJo|uKH$}`Y;Xb1>_qMYLG%|j@#O*oJp)9lHMFl&S z4bg!tXP`@94i+4w?+@gMiV33Pf0VRm$39{IdN(N3(u*f)m6U^H>-#(I1$!OFPxBw$ zg(G*l@AZ-pj#T_`sRDZd1MbWF9TtoaV1bKHir6coH2G1LXNxbgduBBQR@-YYc@dS% zYTg~WL%hhld#L0!En}^m=FLq2Ab><*Y0t zmhYHg2l)nroW;y5*epqLcXFApOd zZ`E_wmzLjPnq|IMl=9adRy)Yo8O*CF4xXv9gL{2@z%rHuEb${ed|94mEXUK_izF`- zyumms$~>A^C40eMs3Kn=c%3nn{DQJrj*=MRSN>9@Nf$BpFTTOBnim=KWw~*nKRXReQSi9rd2*v~Kg%XGIYui~ES0D%Fuy-PUYV;r}$yrg6aX;y%4# z$VceFz%ge*<+I>JN^aV=aY+@&w3elHJ&DH7GIUchGzV+~7J5}&o}z*!E`ya9kyUv! z$BU7=2mvh6L$M9hF$~4D9VOHZ!*lddF%1>PA@f{CM_ISO2k-aI z-OUY-8$X%H>74u|_cqs2LQVcZ2Y*5rE^VTqT%P1Y!C1wO4$T3DAI-Et(rhUFNNs>_<=SOmMa zX-ldmyN;na{lN8xD4aIO8cObWv;hfOBb`Qt6gd(bGyQHAvI4!qN)3(|vGrNQQ!^8tMx+E!8k0o2|vI?VGHr6 z4mT=4DAvh89YaTqL^uF^SsxVsREG<@F$mGs;RFsS__S~%=aI{8!Vc*0T)HaUQSbq* z)_f@J*m#Z_xCuKt4?ECH(A8mv&cpe%aC5$wO)^^4Q3sVoQGcNrb4$@VB0KeCHxJ!3 z>wPVq@WioMVz?|~+DLG%MT}ZulsNu8KQR-U_pU2$RK%;OD0T$jposIil)(s7_Y$WoMx1nrrWwny16A=PlE%QD+SF8I zoyvQszXQGMX z45<*yuL_i8qF&oAGRMIxN!# zWoyi`E!RdRWSbUR2?SeIIhJmP76aC#-=HBl&_c^W9y9QRiU3*tIUHCFyCFvlbw}4M z`q3>5@f{VBZsWHJJfa*R6R?9i3BjH)mI{byX~jT61X}Dhfk*n+b;wd}Yyp46Cdjn{ zONH*x7?2PBY%A0lXrS5=irEDVW>Iod`@vDNNl$oZ0u%N7m!pg-%I0+xmYT6YzKC-8 z2;NhMX})UhFAzuM;zdO7r)G$0ZTHm2mw1tNLJrx`9j(!up$R*o3V+Ewv}z*6P=S3H zeWTZ2$Pm)cr=SQh3M-kQXrTKvjmszl*+0@sT2)0U&6!kSV8{X~N~AKCR+#KaX)aOw zo)%1M=8^?f{tPXJ;78y8KW>ZV87rR3w2tf0NIXSBgY9`C4ugK|I^>b0i7#m)`pc$S zLX^!#j6|(6HxwG9!gB1!Ti5-ih#t6{DESGK#wt%{C9alwzxD}eIzMv$?ykFUb=L}z z42}=#$MEs813-nSnA|w|08$*iq8!v-*m8$>EZ1FjK{-ZPO&81PlOcmYv?@G^q`rlX9yh0p0pTGT7I7=(cq=V(Ye?`&$$xWJy({xVt<)RqmHt9P_shu;p-{hZf z$CFR9F(v6yeUT%)+k``+%H9#18pgN9c{)~<>ORiQ2i2aq6KA!VPK*!#xcPEse%Vg# zB-PIDrpk@sKHc80qswyn>ovP9%7?#y3cjv~_xHnTb&5Z&u%~~Ue2FY;Gx$!X!xKvv bBwV~X`u;2-c_qq0l!gEKx3n)S4!-{v;RQ)=