feat: surface remnawave panel webhook settings
This commit is contained in:
@@ -30,6 +30,11 @@ class SettingField:
|
|||||||
i18n_label_key: Optional[str] = None
|
i18n_label_key: Optional[str] = None
|
||||||
i18n_description_key: Optional[str] = None
|
i18n_description_key: Optional[str] = None
|
||||||
i18n_subsection_key: Optional[str] = None
|
i18n_subsection_key: Optional[str] = None
|
||||||
|
webhook_path: Optional[str] = None
|
||||||
|
webhook_requires_base_url: bool = False
|
||||||
|
webhook_provider_id: Optional[str] = None
|
||||||
|
webhook_hint_i18n_key: Optional[str] = None
|
||||||
|
webhook_hint: str = ""
|
||||||
|
|
||||||
|
|
||||||
SETTINGS_MANIFEST: List[SettingField] = [
|
SETTINGS_MANIFEST: List[SettingField] = [
|
||||||
@@ -84,44 +89,44 @@ SETTINGS_MANIFEST: List[SettingField] = [
|
|||||||
SettingField(
|
SettingField(
|
||||||
"PANEL_API_URL",
|
"PANEL_API_URL",
|
||||||
"url",
|
"url",
|
||||||
"general",
|
"remnawave",
|
||||||
"URL API Remnawave",
|
"URL API Remnawave",
|
||||||
"Например, https://panel.example.com/api.",
|
"Например, https://panel.example.com/api.",
|
||||||
subsection="Remnawave",
|
|
||||||
),
|
),
|
||||||
SettingField(
|
SettingField(
|
||||||
"PANEL_API_KEY",
|
"PANEL_API_KEY",
|
||||||
"string",
|
"string",
|
||||||
"general",
|
"remnawave",
|
||||||
"API-ключ Remnawave",
|
"API-ключ Remnawave",
|
||||||
"Секретный ключ API панели.",
|
"Секретный ключ API панели.",
|
||||||
secret=True,
|
secret=True,
|
||||||
subsection="Remnawave",
|
|
||||||
),
|
),
|
||||||
SettingField(
|
SettingField(
|
||||||
"PANEL_WEBHOOK_SECRET",
|
"PANEL_WEBHOOK_SECRET",
|
||||||
"string",
|
"string",
|
||||||
"general",
|
"remnawave",
|
||||||
"Секрет вебхуков Remnawave",
|
"Секрет вебхуков Remnawave",
|
||||||
"Используется для проверки входящих вебхуков панели.",
|
"Используется для проверки входящих вебхуков панели.",
|
||||||
secret=True,
|
secret=True,
|
||||||
subsection="Remnawave",
|
webhook_path="/webhook/panel",
|
||||||
|
webhook_requires_base_url=True,
|
||||||
|
webhook_provider_id="remnawave",
|
||||||
|
webhook_hint_i18n_key="admin_settings_panel_webhook_url_hint",
|
||||||
|
webhook_hint="Use this URL as WEBHOOK_URL in Remnawave Panel.",
|
||||||
),
|
),
|
||||||
SettingField(
|
SettingField(
|
||||||
"USER_SQUAD_UUIDS",
|
"USER_SQUAD_UUIDS",
|
||||||
"string",
|
"string",
|
||||||
"general",
|
"remnawave",
|
||||||
"Internal Squads по умолчанию",
|
"Internal Squads по умолчанию",
|
||||||
"UUID через запятую для legacy-режима без JSON-каталога тарифов.",
|
"UUID через запятую для legacy-режима без JSON-каталога тарифов.",
|
||||||
subsection="Remnawave",
|
|
||||||
),
|
),
|
||||||
SettingField(
|
SettingField(
|
||||||
"USER_EXTERNAL_SQUAD_UUID",
|
"USER_EXTERNAL_SQUAD_UUID",
|
||||||
"string",
|
"string",
|
||||||
"general",
|
"remnawave",
|
||||||
"External Squad по умолчанию",
|
"External Squad по умолчанию",
|
||||||
"Необязательный UUID External Squad для новых пользователей.",
|
"Необязательный UUID External Squad для новых пользователей.",
|
||||||
subsection="Remnawave",
|
|
||||||
),
|
),
|
||||||
# ─── Web app appearance ────────────────────────────────────────
|
# ─── Web app appearance ────────────────────────────────────────
|
||||||
SettingField(
|
SettingField(
|
||||||
@@ -707,6 +712,7 @@ def manifest_payload() -> List[dict]:
|
|||||||
sections_order = {
|
sections_order = {
|
||||||
"general": 1,
|
"general": 1,
|
||||||
"appearance": 2,
|
"appearance": 2,
|
||||||
|
"remnawave": 3,
|
||||||
"pricing": 11,
|
"pricing": 11,
|
||||||
"payments": 4,
|
"payments": 4,
|
||||||
"trial": 5,
|
"trial": 5,
|
||||||
@@ -773,6 +779,15 @@ def manifest_payload() -> List[dict]:
|
|||||||
item["default"] = default_value
|
item["default"] = default_value
|
||||||
if webhook_metadata:
|
if webhook_metadata:
|
||||||
item.update(webhook_metadata)
|
item.update(webhook_metadata)
|
||||||
|
if field.webhook_path:
|
||||||
|
item["webhook_path"] = field.webhook_path
|
||||||
|
item["webhook_requires_base_url"] = field.webhook_requires_base_url
|
||||||
|
if field.webhook_provider_id:
|
||||||
|
item["provider_id"] = field.webhook_provider_id
|
||||||
|
if field.webhook_hint_i18n_key:
|
||||||
|
item["webhook_hint_i18n_key"] = field.webhook_hint_i18n_key
|
||||||
|
if field.webhook_hint:
|
||||||
|
item["webhook_hint"] = field.webhook_hint
|
||||||
if field.choices:
|
if field.choices:
|
||||||
item["choices"] = [
|
item["choices"] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -198,6 +198,8 @@
|
|||||||
url,
|
url,
|
||||||
requiresBaseUrl: Boolean(field.webhook_requires_base_url),
|
requiresBaseUrl: Boolean(field.webhook_requires_base_url),
|
||||||
baseConfigured: field.webhook_base_url_configured !== false,
|
baseConfigured: field.webhook_base_url_configured !== false,
|
||||||
|
hintI18nKey: field.webhook_hint_i18n_key || "",
|
||||||
|
hintFallback: field.webhook_hint || "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,6 +335,7 @@
|
|||||||
function sectionTitle(id) {
|
function sectionTitle(id) {
|
||||||
const map = {
|
const map = {
|
||||||
general: "Общие",
|
general: "Общие",
|
||||||
|
remnawave: "Remnawave Panel",
|
||||||
appearance: "Внешний вид",
|
appearance: "Внешний вид",
|
||||||
pricing: "Тарифы и цены",
|
pricing: "Тарифы и цены",
|
||||||
payments: "Платёжные системы",
|
payments: "Платёжные системы",
|
||||||
@@ -415,9 +418,9 @@
|
|||||||
<small>
|
<small>
|
||||||
{webhook.url
|
{webhook.url
|
||||||
? at(
|
? at(
|
||||||
"settings_provider_webhook_url_hint",
|
adminLocaleKey(webhook.hintI18nKey || "settings_provider_webhook_url_hint"),
|
||||||
{},
|
{},
|
||||||
"Use this URL in the provider webhook settings."
|
webhook.hintFallback || "Use this URL in the provider webhook settings."
|
||||||
)
|
)
|
||||||
: at(
|
: at(
|
||||||
"settings_provider_webhook_base_missing",
|
"settings_provider_webhook_base_missing",
|
||||||
|
|||||||
+3
-1
@@ -1017,6 +1017,7 @@
|
|||||||
"admin_content": "Content",
|
"admin_content": "Content",
|
||||||
"admin_logs_empty": "No entries",
|
"admin_logs_empty": "No entries",
|
||||||
"admin_settings_section_general": "General",
|
"admin_settings_section_general": "General",
|
||||||
|
"admin_settings_section_remnawave": "Remnawave Panel",
|
||||||
"admin_settings_section_appearance": "Appearance",
|
"admin_settings_section_appearance": "Appearance",
|
||||||
"admin_settings_section_pricing": "Legacy tariff compatibility",
|
"admin_settings_section_pricing": "Legacy tariff compatibility",
|
||||||
"admin_settings_section_payments": "Payment systems",
|
"admin_settings_section_payments": "Payment systems",
|
||||||
@@ -1039,6 +1040,7 @@
|
|||||||
"admin_settings_subsection_heleket": "Heleket",
|
"admin_settings_subsection_heleket": "Heleket",
|
||||||
"admin_settings_provider_webhook_url": "Webhook URL",
|
"admin_settings_provider_webhook_url": "Webhook URL",
|
||||||
"admin_settings_provider_webhook_url_hint": "Use this URL in the provider webhook settings.",
|
"admin_settings_provider_webhook_url_hint": "Use this URL in the provider webhook settings.",
|
||||||
|
"admin_settings_panel_webhook_url_hint": "Use this URL as WEBHOOK_URL in Remnawave Panel.",
|
||||||
"admin_settings_provider_webhook_base_missing": "Set WEBHOOK_BASE_URL in .env to show the full URL for {path}.",
|
"admin_settings_provider_webhook_base_missing": "Set WEBHOOK_BASE_URL in .env to show the full URL for {path}.",
|
||||||
"admin_settings_provider_admin_only_label": "Only for admins",
|
"admin_settings_provider_admin_only_label": "Only for admins",
|
||||||
"admin_settings_provider_admin_only_description": "Shows this provider only to admins. Webhooks and payment status handling remain active for test payments.",
|
"admin_settings_provider_admin_only_description": "Shows this provider only to admins. Webhooks and payment status handling remain active for test payments.",
|
||||||
@@ -1437,7 +1439,7 @@
|
|||||||
"admin_settings_field_panel_api_key_label": "Remnawave API key",
|
"admin_settings_field_panel_api_key_label": "Remnawave API key",
|
||||||
"admin_settings_field_panel_api_key_description": "Secret API key for the panel.",
|
"admin_settings_field_panel_api_key_description": "Secret API key for the panel.",
|
||||||
"admin_settings_field_panel_webhook_secret_label": "Remnawave webhook secret",
|
"admin_settings_field_panel_webhook_secret_label": "Remnawave webhook secret",
|
||||||
"admin_settings_field_panel_webhook_secret_description": "Used to verify incoming panel webhooks.",
|
"admin_settings_field_panel_webhook_secret_description": "Set the secret in Remnawave Panel and paste the same value here to verify incoming panel webhooks.",
|
||||||
"admin_settings_field_user_squad_uuids_label": "Default Internal Squads",
|
"admin_settings_field_user_squad_uuids_label": "Default Internal Squads",
|
||||||
"admin_settings_field_user_squad_uuids_description": "Comma-separated UUIDs for the legacy mode without a JSON tariff catalog.",
|
"admin_settings_field_user_squad_uuids_description": "Comma-separated UUIDs for the legacy mode without a JSON tariff catalog.",
|
||||||
"admin_settings_field_user_external_squad_uuid_label": "Default External Squad",
|
"admin_settings_field_user_external_squad_uuid_label": "Default External Squad",
|
||||||
|
|||||||
+3
-1
@@ -1017,6 +1017,7 @@
|
|||||||
"admin_content": "Контент",
|
"admin_content": "Контент",
|
||||||
"admin_logs_empty": "Записей нет",
|
"admin_logs_empty": "Записей нет",
|
||||||
"admin_settings_section_general": "Общие",
|
"admin_settings_section_general": "Общие",
|
||||||
|
"admin_settings_section_remnawave": "Remnawave Panel",
|
||||||
"admin_settings_section_appearance": "Внешний вид",
|
"admin_settings_section_appearance": "Внешний вид",
|
||||||
"admin_settings_section_pricing": "Совместимость с legacy-тарифами",
|
"admin_settings_section_pricing": "Совместимость с legacy-тарифами",
|
||||||
"admin_settings_section_payments": "Платёжные системы",
|
"admin_settings_section_payments": "Платёжные системы",
|
||||||
@@ -1039,6 +1040,7 @@
|
|||||||
"admin_settings_subsection_heleket": "Heleket",
|
"admin_settings_subsection_heleket": "Heleket",
|
||||||
"admin_settings_provider_webhook_url": "Webhook URL",
|
"admin_settings_provider_webhook_url": "Webhook URL",
|
||||||
"admin_settings_provider_webhook_url_hint": "Укажите этот адрес в настройках вебхуков провайдера.",
|
"admin_settings_provider_webhook_url_hint": "Укажите этот адрес в настройках вебхуков провайдера.",
|
||||||
|
"admin_settings_panel_webhook_url_hint": "Укажите этот адрес как WEBHOOK_URL в Remnawave Panel.",
|
||||||
"admin_settings_provider_webhook_base_missing": "Укажите WEBHOOK_BASE_URL в .env, чтобы увидеть полный адрес для {path}.",
|
"admin_settings_provider_webhook_base_missing": "Укажите WEBHOOK_BASE_URL в .env, чтобы увидеть полный адрес для {path}.",
|
||||||
"admin_settings_provider_admin_only_label": "Только для админов",
|
"admin_settings_provider_admin_only_label": "Только для админов",
|
||||||
"admin_settings_provider_admin_only_description": "Показывает провайдер только администраторам. Вебхуки и обработка статусов остаются активными для тестовых платежей.",
|
"admin_settings_provider_admin_only_description": "Показывает провайдер только администраторам. Вебхуки и обработка статусов остаются активными для тестовых платежей.",
|
||||||
@@ -1437,7 +1439,7 @@
|
|||||||
"admin_settings_field_panel_api_key_label": "API-ключ Remnawave",
|
"admin_settings_field_panel_api_key_label": "API-ключ Remnawave",
|
||||||
"admin_settings_field_panel_api_key_description": "Секретный ключ API панели.",
|
"admin_settings_field_panel_api_key_description": "Секретный ключ API панели.",
|
||||||
"admin_settings_field_panel_webhook_secret_label": "Секрет вебхуков Remnawave",
|
"admin_settings_field_panel_webhook_secret_label": "Секрет вебхуков Remnawave",
|
||||||
"admin_settings_field_panel_webhook_secret_description": "Используется для проверки входящих вебхуков панели.",
|
"admin_settings_field_panel_webhook_secret_description": "Задайте секрет в Remnawave Panel и вставьте то же значение здесь для проверки входящих вебхуков панели.",
|
||||||
"admin_settings_field_user_squad_uuids_label": "Internal Squads по умолчанию",
|
"admin_settings_field_user_squad_uuids_label": "Internal Squads по умолчанию",
|
||||||
"admin_settings_field_user_squad_uuids_description": "UUID через запятую для legacy-режима без JSON-каталога тарифов.",
|
"admin_settings_field_user_squad_uuids_description": "UUID через запятую для legacy-режима без JSON-каталога тарифов.",
|
||||||
"admin_settings_field_user_external_squad_uuid_label": "External Squad по умолчанию",
|
"admin_settings_field_user_external_squad_uuid_label": "External Squad по умолчанию",
|
||||||
|
|||||||
@@ -230,6 +230,32 @@ def test_payment_provider_settings_include_webhook_metadata():
|
|||||||
assert "webhook_path" not in manifest["PAYMENT_STARS_WEBAPP_LABEL_RU"]
|
assert "webhook_path" not in manifest["PAYMENT_STARS_WEBAPP_LABEL_RU"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_remnawave_settings_include_panel_webhook_metadata():
|
||||||
|
manifest = _manifest_by_key()
|
||||||
|
field = manifest["PANEL_WEBHOOK_SECRET"]
|
||||||
|
remnawave_keys = (
|
||||||
|
"PANEL_API_URL",
|
||||||
|
"PANEL_API_KEY",
|
||||||
|
"PANEL_WEBHOOK_SECRET",
|
||||||
|
"USER_SQUAD_UUIDS",
|
||||||
|
"USER_EXTERNAL_SQUAD_UUID",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert field["webhook_path"] == "/webhook/panel"
|
||||||
|
assert field["webhook_requires_base_url"] is True
|
||||||
|
assert field["provider_id"] == "remnawave"
|
||||||
|
assert field["webhook_hint_i18n_key"] == "admin_settings_panel_webhook_url_hint"
|
||||||
|
for setting_key in remnawave_keys:
|
||||||
|
assert manifest[setting_key]["section"] == "remnawave"
|
||||||
|
assert manifest[setting_key]["section_order"] == 3
|
||||||
|
assert manifest[setting_key]["subsection"] is None
|
||||||
|
|
||||||
|
for language in ("ru", "en"):
|
||||||
|
messages = _locale(language)
|
||||||
|
assert "admin_settings_section_remnawave" in messages
|
||||||
|
assert field["webhook_hint_i18n_key"] in messages
|
||||||
|
|
||||||
|
|
||||||
def test_payment_provider_admin_only_toggles_are_mutually_exclusive():
|
def test_payment_provider_admin_only_toggles_are_mutually_exclusive():
|
||||||
manifest = _manifest_by_key()
|
manifest = _manifest_by_key()
|
||||||
|
|
||||||
|
|||||||
@@ -760,10 +760,13 @@ class AdminSettingsSecurityTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
response = await admin_api.admin_settings_get_route(request)
|
response = await admin_api.admin_settings_get_route(request)
|
||||||
|
|
||||||
payload = json.loads(response.text)
|
payload = json.loads(response.text)
|
||||||
|
section_ids = [section["id"] for section in payload["sections"]]
|
||||||
fields = {
|
fields = {
|
||||||
field["key"]: field for section in payload["sections"] for field in section["fields"]
|
field["key"]: field for section in payload["sections"] for field in section["fields"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.assertLess(section_ids.index("general"), section_ids.index("remnawave"))
|
||||||
|
self.assertLess(section_ids.index("remnawave"), section_ids.index("payments"))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
fields["FREEKASSA_ENABLED"]["webhook_url"],
|
fields["FREEKASSA_ENABLED"]["webhook_url"],
|
||||||
"https://web.tnnl.cc/webhook/freekassa",
|
"https://web.tnnl.cc/webhook/freekassa",
|
||||||
@@ -773,6 +776,11 @@ class AdminSettingsSecurityTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
fields["PAYMENT_PLATEGA_CRYPTO_WEBAPP_LABEL_RU"]["webhook_url"],
|
fields["PAYMENT_PLATEGA_CRYPTO_WEBAPP_LABEL_RU"]["webhook_url"],
|
||||||
"https://web.tnnl.cc/webhook/platega",
|
"https://web.tnnl.cc/webhook/platega",
|
||||||
)
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
fields["PANEL_WEBHOOK_SECRET"]["webhook_url"],
|
||||||
|
"https://web.tnnl.cc/webhook/panel",
|
||||||
|
)
|
||||||
|
self.assertTrue(fields["PANEL_WEBHOOK_SECRET"]["webhook_requires_base_url"])
|
||||||
self.assertNotIn("webhook_url", fields["PAYMENT_STARS_WEBAPP_LABEL_RU"])
|
self.assertNotIn("webhook_url", fields["PAYMENT_STARS_WEBAPP_LABEL_RU"])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user