gitscope
← marzneshin / Diff · d6600c5 → d6600c5
Changes d6600c5..d6600c5
Patch commit
diff --git a/app/routes/subscription.py b/app/routes/subscription.py
index a64d353..bf08877 100644
--- a/app/routes/subscription.py
+++ b/app/routes/subscription.py
@@ -8,7 +8,7 @@ from app.db import crud
from app.dependencies import DBDep, SubUserDep, StartDateDep, EndDateDep
from app.models.user import UserResponse
from app.templates import render_template
-from app.utils.share import encode_title, generate_subscription, generate_v2ray_links
+from app.utils.share import encode_title, generate_subscription
from config import (
SUB_PROFILE_TITLE,
SUB_SUPPORT_URL,
@@ -43,13 +43,7 @@ def user_subscription(
user: UserResponse = UserResponse.model_validate(db_user)
if "text/html" in accept_header:
- links = generate_v2ray_links(
- user.inbounds,
- user.key,
- user.model_dump(
- exclude={"subscription_url", "services", "inbounds", "links"}
- ),
- )
+ links = generate_subscription(user=user, config_format="v2ray").split()
return HTMLResponse(
render_template(SUBSCRIPTION_PAGE_TEMPLATE, {"user": user, "links": links})
)
@@ -70,31 +64,16 @@ def user_subscription(
crud.update_user_sub(db, db_user, user_agent)
if re.match("^([Cc]lash-verge|[Cc]lash-?[Mm]eta)", user_agent):
- conf = generate_subscription(
- user=user, config_format="clash-meta", as_base64=False
- )
+ conf = generate_subscription(user=user, config_format="clash-meta")
return Response(content=conf, media_type="text/yaml", headers=response_headers)
-
elif re.match("^([Cc]lash|[Ss]tash)", user_agent):
- conf = generate_subscription(user=user, config_format="clash", as_base64=False)
+ conf = generate_subscription(user=user, config_format="clash")
return Response(content=conf, media_type="text/yaml", headers=response_headers)
-
elif re.match("^(SFA|SFI|SFM|SFT)", user_agent):
- conf = generate_subscription(
- user=user, config_format="sing-box", as_base64=False
- )
- return Response(
- content=conf, media_type="application/json", headers=response_headers
- )
-
- elif re.match("^(SS|SSR|SSD|SSS|Outline|Shadowsocks|SSconf)", user_agent):
- conf = generate_subscription(
- user=user, config_format="outline", as_base64=False
- )
+ conf = generate_subscription(user=user, config_format="sing-box")
return Response(
content=conf, media_type="application/json", headers=response_headers
)
-
else:
conf = generate_subscription(user=user, config_format="v2ray", as_base64=True)
return Response(content=conf, media_type="text/plain", headers=response_headers)
@@ -118,10 +97,10 @@ def user_get_usage(
def user_subscription_with_client_type(
db_user: SubUserDep,
request: Request,
- client_type: str = Path(..., regex="sing-box|clash-meta|clash|outline|v2ray"),
+ client_type: str = Path(regex="sing-box|clash-meta|clash|xray|v2ray"),
):
"""
- Subscription link, v2ray, clash, sing-box, outline and clash-meta supported
+ Subscription by client type; v2ray, xray, sing-box, clash and clash-meta formats supported
"""
user: UserResponse = UserResponse.model_validate(db_user)
@@ -140,34 +119,25 @@ def user_subscription_with_client_type(
}
if client_type == "clash-meta":
- conf = generate_subscription(
- user=user, config_format="clash-meta", as_base64=False
- )
+ conf = generate_subscription(user=user, config_format="clash-meta")
return Response(content=conf, media_type="text/yaml", headers=response_headers)
elif client_type == "sing-box":
- conf = generate_subscription(
- user=user, config_format="sing-box", as_base64=False
- )
+ conf = generate_subscription(user=user, config_format="sing-box")
return Response(
content=conf, media_type="application/json", headers=response_headers
)
-
elif client_type == "clash":
- conf = generate_subscription(user=user, config_format="clash", as_base64=False)
+ conf = generate_subscription(user=user, config_format="clash")
return Response(content=conf, media_type="text/yaml", headers=response_headers)
elif client_type == "v2ray":
conf = generate_subscription(user=user, config_format="v2ray", as_base64=True)
return Response(content=conf, media_type="text/plain", headers=response_headers)
-
- elif client_type == "outline":
- conf = generate_subscription(
- user=user, config_format="outline", as_base64=False
- )
+ elif client_type == "xray":
return Response(
- content=conf, media_type="application/json", headers=response_headers
+ content=generate_subscription(user=user, config_format="xray"),
+ headers=response_headers,
)
-
else:
raise HTTPException(status_code=400, detail="Invalid subscription type")
diff --git a/app/utils/keygen.py b/app/utils/keygen.py
index 1b2654f..c8ed478 100644
--- a/app/utils/keygen.py
+++ b/app/utils/keygen.py
@@ -1,7 +1,5 @@
-# import hashlib
import xxhash
import uuid
-from app.models.proxy import ProxyTypes
def gen_uuid(key: str) -> str:
@@ -10,14 +8,3 @@ def gen_uuid(key: str) -> str:
def gen_password(key: str) -> str:
return xxhash.xxh128(key.encode()).hexdigest()
-
-
-def generate_settings(key: str, protocol: ProxyTypes) -> dict:
- if protocol in (ProxyTypes.Trojan, ProxyTypes.Shadowsocks):
- return (
- ProxyTypes(protocol)
- .settings_model(password=gen_password(key))
- .dict(no_obj=True)
- )
- elif protocol in (ProxyTypes.VMess, ProxyTypes.VLESS):
- return ProxyTypes(protocol).settings_model(id=gen_uuid(key)).dict(no_obj=True)
diff --git a/app/utils/share.py b/app/utils/share.py
index d93fc18..e150edb 100644
--- a/app/utils/share.py
+++ b/app/utils/share.py
@@ -7,7 +7,8 @@ from typing import TYPE_CHECKING, Literal, Union, List
from uuid import UUID
from jdatetime import date as jd
-from v2share import V2Data, SingBoxConfig, ClashConfig, ClashMetaConfig
+from v2share import V2Data, SingBoxConfig, ClashConfig, ClashMetaConfig, XrayConfig
+from v2share.links import LinksConfig
from app.db import GetDB, crud
from app.models.proxy import (
@@ -15,6 +16,7 @@ from app.models.proxy import (
)
from app.utils.keygen import gen_uuid, gen_password
from app.utils.system import get_public_ip, readable_size
+from app.models.user import UserStatus
if TYPE_CHECKING:
from app.models.user import UserResponse
@@ -26,69 +28,37 @@ STATUS_EMOJIS = {
"expired": "⌛️",
"limited": "🪫",
"disabled": "❌",
- "connect_to_start": "🔌",
+ "on_hold": "🔌",
}
-
-def generate_v2ray_links(inbounds: list, key: str, extra_data: dict) -> list:
- format_variables = setup_format_variables(extra_data)
- return process_inbounds_and_tags(inbounds, key, format_variables, mode="v2ray")
-
-
-def generate_clash_subscription(
- inbounds: list, key: str, extra_data: dict, is_meta: bool = False
-) -> str:
- if is_meta is True:
- conf = ClashMetaConfig()
- else:
- conf = ClashConfig()
-
- format_variables = setup_format_variables(extra_data)
- return process_inbounds_and_tags(
- inbounds, key, format_variables, mode="clash", conf=conf
- )
-
-
-def generate_singbox_subscription(inbounds: list, key: str, extra_data: dict) -> str:
- conf = SingBoxConfig()
-
- format_variables = setup_format_variables(extra_data)
- return process_inbounds_and_tags(
- inbounds, key, format_variables, mode="sing-box", conf=conf
- )
-
-
-def generate_v2ray_subscription(links: list) -> str:
- return base64.b64encode("\n".join(links).encode()).decode()
+subscription_handlers = {
+ "v2ray": LinksConfig,
+ "xray": XrayConfig,
+ "clash-meta": ClashMetaConfig,
+ "clash": ClashConfig,
+ "sing-box": SingBoxConfig,
+}
def generate_subscription(
user: "UserResponse",
- config_format: Literal["v2ray", "clash-meta", "clash", "sing-box"],
- as_base64: bool,
+ config_format: Literal["v2ray", "xray", "clash-meta", "clash", "sing-box"],
+ as_base64: bool = False,
) -> str:
- kwargs = {
- # "proxies": user.proxies,
- "inbounds": user.inbounds,
- "key": user.key,
- "extra_data": user.__dict__,
- }
- if config_format == "v2ray":
- config = "\n".join(generate_v2ray_links(**kwargs))
- elif config_format == "clash-meta":
- config = generate_clash_subscription(**kwargs, is_meta=True)
- elif config_format == "clash":
- config = generate_clash_subscription(**kwargs)
- elif config_format == "sing-box":
- config = generate_singbox_subscription(**kwargs)
- else:
+ inbounds = user.inbounds
+ key = user.key
+ extra_data = user.model_dump(exclude={"subscription_url", "services", "inbounds"})
+ format_variables = setup_format_variables(extra_data)
+
+ if config_format not in subscription_handlers.keys():
raise ValueError(f'Unsupported format "{config_format}"')
- if as_base64:
- config = base64.b64encode(config.encode()).decode()
+ config = process_inbounds_and_tags(
+ inbounds, key, format_variables, conf=subscription_handlers[config_format]()
+ )
- return config
+ return config if not as_base64 else base64.b64encode(config.encode()).decode()
def format_time_left(seconds_left: int) -> str:
@@ -114,9 +84,6 @@ def format_time_left(seconds_left: int) -> str:
return " ".join(result)
-from app.models.user import UserStatus
-
-
def setup_format_variables(extra_data: dict) -> dict:
user_status = extra_data.get("status")
@@ -179,13 +146,11 @@ def setup_format_variables(extra_data: dict) -> dict:
def process_inbounds_and_tags(
inbounds: list,
- ukey: str,
+ key: str,
format_variables: dict,
- mode: str = "v2ray",
conf=None,
) -> Union[List, str]:
salt = secrets.token_hex(8)
- results = []
for inb in inbounds:
inb_id, inbound, protocol, tag = (
@@ -232,25 +197,19 @@ def process_inbounds_and_tags(
sni=sni,
host=req_host,
tls=host_tls or inbound["tls"],
- alpn=host.alpn.value or inbound.get("alpn", ""),
+ alpn=host.alpn.value or None,
path=(
host.path.format_map(format_variables)
if host.path
- else inbound.get("path", "")
+ else inbound.get("path")
),
fingerprint=host.fingerprint.value,
allow_insecure=host.allowinsecure,
- uuid=UUID(gen_uuid(ukey)),
- password=gen_password(ukey),
+ uuid=UUID(gen_uuid(key)),
+ password=gen_password(key),
)
- if mode == "v2ray":
- results.append(data.to_link())
- elif mode in ["clash", "sing-box"]:
- conf.add_proxies([data])
-
- if mode in ["clash", "sing-box"]:
- return conf.render()
- return results
+ conf.add_proxies([data])
+ return conf.render()
def encode_title(text: str) -> str:
diff --git a/requirements.txt b/requirements.txt
index 8724bbb..62917c7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@ anyio==4.2.0
APScheduler==3.10.4
commentjson==0.9.0
cryptography==42.0.4
-fastapi==0.110.0
+fastapi==0.111.0
fastapi-pagination==0.12.21
fastapi-responses==0.2.1
grpclib==0.4.7
@@ -12,7 +12,7 @@ Jinja2==3.1.4
passlib==1.7.4
protobuf==4.25.2
psutil==5.9.7
-pydantic==2.5.3
+pydantic==2.7.1
PyMySQL==1.1.1
pyOpenSSL==24.0.0
pyTelegramBotAPI==4.15.2
@@ -22,12 +22,12 @@ python-dateutil==2.8.2
python-jose==3.3.0
pytz==2023.3.post1
qrcode==7.4.2
-Requests==2.32.2
+requests==2.32.2
rich==13.7.0
SQLAlchemy==2.0.25
-starlette==0.36.3
-typer==0.9.0
-uvicorn==0.25.0
+starlette==0.37.2
+typer==0.12.3
+uvicorn==0.29.0
websockets==12.0
PyYAML==6.0.1
aiohttp==3.9.4
@@ -35,4 +35,4 @@ python-multipart==0.0.7
xxhash==3.4.1
grpcio==1.60.0
psycopg==3.1.18
-v2share==0.1.0a6
\ No newline at end of file
+v2share==0.1.0b3