Skip to content

Commit 66eb6e2

Browse files
committed
feat: /raw path for 3rd-party apps
1 parent 12358c3 commit 66eb6e2

5 files changed

Lines changed: 45 additions & 18 deletions

File tree

app/operation/subscription.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,9 @@ async def user_subscription(
286286
return HTMLResponse(
287287
render_template(
288288
template,
289-
{
290-
"user": user,
291-
"links": links,
292-
"announce": formatted_announce,
293-
"announce_url": sub_settings.announce_url,
294-
"apps": self._make_apps_import_urls(sub_settings.applications, format_variables),
295-
},
289+
self._build_raw_subscription_payload(
290+
user, links, formatted_announce, sub_settings, format_variables
291+
),
296292
)
297293
)
298294
else:
@@ -374,6 +370,34 @@ async def user_subscription_with_client_type(
374370
# Create response headers
375371
return Response(content=conf, media_type=media_type, headers=response_headers)
376372

373+
def _build_raw_subscription_payload(
374+
self,
375+
user: UsersResponseWithInbounds,
376+
links: list[str],
377+
formatted_announce: str,
378+
sub_settings: SubSettings,
379+
format_variables: dict,
380+
) -> dict[str, Any]:
381+
return {
382+
"user": user,
383+
"links": links,
384+
"announce": formatted_announce,
385+
"announce_url": sub_settings.announce_url,
386+
"apps": self._make_apps_import_urls(sub_settings.applications, format_variables),
387+
}
388+
389+
async def user_subscription_raw(self, db: AsyncSession, token: str):
390+
sub_settings: SubSettings = await subscription_settings()
391+
db_user = await self.get_validated_sub(db, token)
392+
user = await self.validated_user(db_user)
393+
links = []
394+
if sub_settings.allow_browser_config:
395+
conf, _ = await self.fetch_config(user, ConfigFormat.links)
396+
links = conf.splitlines()
397+
format_variables = await self.get_format_variables(user)
398+
formatted_announce = self._format_announce(sub_settings, format_variables)
399+
return self._build_raw_subscription_payload(user, links, formatted_announce, sub_settings, format_variables)
400+
377401
async def user_subscription_by_user(
378402
self,
379403
db_user: User,

app/routers/subscription.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ async def user_subscription_info(request: Request, token: str, db: AsyncSession
4242
return JSONResponse(content=user_data.model_dump(mode="json"), headers=response_headers)
4343

4444

45+
@router.get("/{token}/raw")
46+
async def user_subscription_raw(token: str, db: AsyncSession = Depends(get_db)):
47+
return await subscription_operator.user_subscription_raw(db, token=token)
48+
49+
4550
@router.get("/{token}/apps", response_model=list[Application])
4651
async def user_subscription_apps(token: str, db: AsyncSession = Depends(get_db)):
4752
"""

app/subscription/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from urllib.parse import quote, urlencode
88

99
from app.models.subscription import SubscriptionInboundData
10-
from app.templates import render_template_string
1110

1211

1312
class BaseSubscription:
@@ -17,13 +16,13 @@ def __init__(
1716
grpc_user_agent_template_content: str | None = None,
1817
):
1918
self.proxy_remarks = []
20-
user_agent_data = json.loads(render_template_string(user_agent_template_content))
19+
user_agent_data = json.loads(user_agent_template_content) if user_agent_template_content else {}
2120
if "list" in user_agent_data and isinstance(user_agent_data["list"], list):
2221
self.user_agent_list = user_agent_data["list"]
2322
else:
2423
self.user_agent_list = []
2524

26-
grpc_user_agent_data = json.loads(render_template_string(grpc_user_agent_template_content))
25+
grpc_user_agent_data = json.loads(grpc_user_agent_template_content) if grpc_user_agent_template_content else {}
2726

2827
if "list" in grpc_user_agent_data and isinstance(grpc_user_agent_data["list"], list):
2928
self.grpc_user_agent_data = grpc_user_agent_data["list"]

app/subscription/singbox.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
TLSConfig,
1010
WebSocketTransportConfig,
1111
)
12-
from app.templates import render_template_string
1312
from app.utils.helpers import UUIDEncoder
1413

1514
from . import BaseSubscription
@@ -26,7 +25,7 @@ def __init__(
2625
user_agent_template_content=user_agent_template_content,
2726
grpc_user_agent_template_content=grpc_user_agent_template_content,
2827
)
29-
self.config = json.loads(render_template_string(singbox_template_content))
28+
self.config = json.loads(singbox_template_content) if singbox_template_content else {}
3029
self.config.setdefault("endpoints", [])
3130
self.config.setdefault("outbounds", [])
3231

@@ -59,6 +58,10 @@ def add_endpoint(self, endpoint_data):
5958
self.config["endpoints"].append(endpoint_data)
6059

6160
def render(self):
61+
self._finalize_config()
62+
return json.dumps(self.config, indent=4, cls=UUIDEncoder)
63+
64+
def _finalize_config(self):
6265
urltest_types = ["vmess", "vless", "trojan", "shadowsocks", "hysteria2", "tuic", "http", "ssh"]
6366
urltest_tags = [outbound["tag"] for outbound in self.config["outbounds"] if outbound["type"] in urltest_types]
6467
selector_types = [
@@ -85,8 +88,6 @@ def render(self):
8588
if outbound.get("type") == "selector":
8689
outbound["outbounds"] = selector_tags
8790

88-
return json.dumps(self.config, indent=4, cls=UUIDEncoder)
89-
9091
def add(self, remark: str, address: str, inbound: SubscriptionInboundData, settings: dict):
9192
"""Add outbound using registry pattern"""
9293
# Not supported by sing-box

app/subscription/xray.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
WebSocketTransportConfig,
1212
XHTTPTransportConfig,
1313
)
14-
from app.templates import render_template_string
1514
from app.utils.helpers import UUIDEncoder
1615

1716
from . import BaseSubscription
@@ -29,7 +28,7 @@ def __init__(
2928
grpc_user_agent_template_content=grpc_user_agent_template_content,
3029
)
3130
self.config = []
32-
self.template = render_template_string(xray_template_content)
31+
self.template = json.loads(xray_template_content) if xray_template_content else {}
3332

3433
# Registry for transport handlers
3534
self.transport_handlers = {
@@ -57,8 +56,7 @@ def __init__(
5756
}
5857

5958
def add_config(self, remarks, outbounds, template_content: str | None = None):
60-
rendered_template = render_template_string(template_content) if template_content is not None else self.template
61-
json_template = json.loads(rendered_template)
59+
json_template = json.loads(template_content) if template_content is not None else self.template.copy()
6260
json_template["remarks"] = remarks
6361
json_template["outbounds"] = outbounds + json_template["outbounds"]
6462
self.config.append(json_template)

0 commit comments

Comments
 (0)