feat: harden public install guide loading

This commit is contained in:
3252a8
2026-05-22 23:29:58 +03:00
parent 40414264be
commit 17224b4f74
4 changed files with 95 additions and 3 deletions
+14 -1
View File
@@ -38,8 +38,21 @@ async def public_subscription_guides_route(request: web.Request) -> web.Response
if not share_token: if not share_token:
return web.json_response({"ok": False, "error": "invalid_share_token"}, status=404) return web.json_response({"ok": False, "error": "invalid_share_token"}, status=404)
status = await _subscription_guides_status_shared(request.app)
subscription = await _public_subscription_payload(request, share_token) subscription = await _public_subscription_payload(request, share_token)
if not subscription.get("active"):
return web.json_response(
{
"ok": False,
"enabled": False,
"config": None,
"source": None,
"subscription": subscription,
"error": "subscription_unavailable",
},
status=404,
)
status = await _subscription_guides_status_shared(request.app)
payload = { payload = {
"enabled": bool(status.get("enabled")), "enabled": bool(status.get("enabled")),
"config": status.get("config") if status.get("enabled") else None, "config": status.get("config") if status.get("enabled") else None,
+10 -1
View File
@@ -1064,6 +1064,15 @@
openExternalLink(url); openExternalLink(url);
} }
function openPublicConnectLink() {
const url = publicInstallSubscription?.connect_url || publicInstallSubscription?.config_link;
if (!url) {
showToast(t("wa_connect_link_unavailable"));
return;
}
openExternalLink(url);
}
function openInstallOrConnect() { function openInstallOrConnect() {
if (canUseInstallGuides()) { if (canUseInstallGuides()) {
goInstall(); goInstall();
@@ -1363,7 +1372,7 @@
user={{}} user={{}}
subscription={publicInstallSubscription || { install_share_token: publicInstallToken }} subscription={publicInstallSubscription || { install_share_token: publicInstallToken }}
{goHome} {goHome}
{openConnectLink} openConnectLink={openPublicConnectLink}
{openExternalLink} {openExternalLink}
{copyText} {copyText}
{t} {t}
@@ -21,7 +21,12 @@ export function createInstallGuidesStore({ api, t, showToast }) {
}); });
if (!force && snapshot?.loaded) return snapshot; if (!force && snapshot?.loaded) return snapshot;
const promise = (async () => { const promise = (async () => {
state.update((s) => ({ ...s, loading: true, error: "" })); state.update((s) => ({
...s,
loading: true,
loaded: force ? false : s.loaded,
error: "",
}));
try { try {
const response = await api(path); const response = await api(path);
const next = { const next = {
+65
View File
@@ -206,6 +206,71 @@ class SubscriptionGuidesRouteTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(body["subscription"]["install_share_token"], share_token) self.assertEqual(body["subscription"]["install_share_token"], share_token)
panel_service.get_user_by_uuid.assert_awaited_once_with("panel-user") panel_service.get_user_by_uuid.assert_awaited_once_with("panel-user")
async def test_public_route_rejects_unknown_share_token_without_loading_config(self):
share_token = "8f559061460e8fede78ef18dce887236"
panel_service = SimpleNamespace(
get_subscription_page_config_list=AsyncMock(),
get_subscription_page_config_by_uuid=AsyncMock(),
get_user_by_uuid=AsyncMock(),
)
request = self._request(
self._settings(SUBSCRIPTION_MINI_APP_URL="https://app.example.test/app"),
panel_service,
match_info={"share_token": share_token},
)
with patch.object(
guides.subscription_dal,
"get_subscription_by_install_share_token",
AsyncMock(return_value=None),
):
response = await guides.public_subscription_guides_route(request)
body = json.loads(response.text)
self.assertEqual(response.status, 404)
self.assertFalse(body["ok"])
self.assertEqual(body["error"], "subscription_unavailable")
self.assertFalse(body["enabled"])
self.assertIsNone(body["config"])
self.assertEqual(body["subscription"]["install_share_token"], share_token)
self.assertFalse(body["subscription"]["active"])
panel_service.get_subscription_page_config_list.assert_not_called()
panel_service.get_subscription_page_config_by_uuid.assert_not_called()
panel_service.get_user_by_uuid.assert_not_called()
async def test_public_route_rejects_inactive_share_token_without_panel_user_lookup(self):
share_token = "8f559061460e8fede78ef18dce887236"
panel_service = SimpleNamespace(
get_subscription_page_config_list=AsyncMock(),
get_subscription_page_config_by_uuid=AsyncMock(),
get_user_by_uuid=AsyncMock(),
)
request = self._request(
self._settings(SUBSCRIPTION_MINI_APP_URL="https://app.example.test/app"),
panel_service,
match_info={"share_token": share_token},
)
local_sub = SimpleNamespace(
panel_user_uuid="panel-user",
install_share_token=share_token,
is_active=False,
end_date=datetime.now(timezone.utc) + timedelta(days=3),
)
with patch.object(
guides.subscription_dal,
"get_subscription_by_install_share_token",
AsyncMock(return_value=local_sub),
):
response = await guides.public_subscription_guides_route(request)
body = json.loads(response.text)
self.assertEqual(response.status, 404)
self.assertFalse(body["ok"])
self.assertEqual(body["error"], "subscription_unavailable")
self.assertFalse(body["subscription"]["active"])
panel_service.get_user_by_uuid.assert_not_called()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()