From e9e028fc00873953d5c602cc521a572c9f53f785 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Tue, 2 Jun 2026 11:31:03 +0300 Subject: [PATCH] fix: narrow Remnashop settings import --- backend/scripts/import_legacy.py | 66 ++++++++++++++++++++------------ docs/migrations/remnashop.md | 26 ++++++++++--- tests/test_remnashop_import.py | 48 ++++++++++++++++++++++- 3 files changed, 109 insertions(+), 31 deletions(-) diff --git a/backend/scripts/import_legacy.py b/backend/scripts/import_legacy.py index df3b0ed..f3dbd0b 100644 --- a/backend/scripts/import_legacy.py +++ b/backend/scripts/import_legacy.py @@ -61,6 +61,7 @@ except Exception: # pragma: no cover - defensive fallback for minimal tooling. SOURCE = "remnashop" REMNASHOP_ENCRYPTED_PREFIX = "enc_" +PLACEHOLDER_SETTING_VALUES = {"change_me", "changeme"} GIB = 1024**3 UUID_RE = re.compile( r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-" @@ -230,7 +231,13 @@ def read_remnashop_env_file(path: Optional[str]) -> dict[str, str]: return parse_remnashop_env_text(Path(path).read_text(encoding="utf-8")) +def _is_placeholder_setting_value(value: Any) -> bool: + return isinstance(value, str) and value.strip().lower() in PLACEHOLDER_SETTING_VALUES + + def _clean_url(value: Any) -> Optional[str]: + if _is_placeholder_setting_value(value): + return None text_value = str(value or "").strip().rstrip("/") return text_value or None @@ -259,26 +266,23 @@ def _source_public_base_from_env(env: dict[str, str]) -> Optional[str]: def _support_link_from_username(value: Any) -> Optional[str]: + if _is_placeholder_setting_value(value): + return None username = str(value or "").strip().lstrip("@") if not username: return None return f"https://t.me/{username}" -def _mini_app_url_from_env(value: Any) -> Optional[str]: - raw = str(value or "").strip() - if not raw: - return None - if raw.lower() in {"true", "false", "0", "1"}: - return None - return raw if raw.startswith("https://") else None - - def _add_override(overrides: dict[str, Any], key: str, value: Any) -> None: if value is None: return - if isinstance(value, str) and not value.strip(): - return + if isinstance(value, str): + value = value.strip() + if not value: + return + if value.lower() in PLACEHOLDER_SETTING_VALUES: + return overrides[key] = value @@ -293,11 +297,6 @@ def remnashop_env_overrides(env: dict[str, str]) -> dict[str, Any]: _support_link_from_username(env.get("BOT_SUPPORT_USERNAME")), ) _add_override(overrides, "DEFAULT_LANGUAGE", env.get("APP_DEFAULT_LOCALE")) - _add_override( - overrides, - "SUBSCRIPTION_MINI_APP_URL", - _mini_app_url_from_env(env.get("BOT_MINI_APP")), - ) return overrides @@ -458,17 +457,24 @@ def remnashop_payment_gateway_overrides( if gateway_type == "CRYPTOPAY": _add_override(overrides, "CRYPTOPAY_ENABLED", active) _add_override(overrides, "CRYPTOPAY_TOKEN", settings.get("api_key")) - if currency: - _add_override(overrides, "CRYPTOPAY_ASSET", currency) + if currency and currency != "RUB": + warnings.append( + f"CryptoPay source currency was {currency}; Minishop keeps payment currency " + "controlled by tariffs/default currency. Configure CRYPTOPAY_ASSET manually " + "if this instance needs a different default." + ) return _provider_mapping_result(gateway_type, ["cryptopay"], overrides, warnings) if gateway_type == "HELEKET": _add_override(overrides, "HELEKET_ENABLED", active) _add_override(overrides, "HELEKET_MERCHANT_ID", settings.get("merchant_id")) _add_override(overrides, "HELEKET_API_KEY", settings.get("api_key")) - if currency: - _add_override(overrides, "HELEKET_CURRENCY", currency) - _add_override(overrides, "HELEKET_SUPPORTED_CURRENCIES", currency) + if currency and currency != "RUB": + warnings.append( + f"Heleket source currency was {currency}; Minishop keeps payment currency " + "controlled by tariffs/default currency. Configure HELEKET_CURRENCY manually " + "if this instance needs a different default." + ) return _provider_mapping_result(gateway_type, ["heleket"], overrides, warnings) if gateway_type == "FREEKASSA": @@ -492,8 +498,12 @@ def remnashop_payment_gateway_overrides( _add_override(overrides, "PLATEGA_SECRET", settings.get("api_key")) _add_override(overrides, "PLATEGA_PAYMENT_METHOD", settings.get("payment_method")) _add_override(overrides, "PLATEGA_SBP_METHOD", settings.get("payment_method")) - if currency: - _add_override(overrides, "PLATEGA_SUPPORTED_CURRENCIES", currency) + if currency and currency != "RUB": + warnings.append( + f"Platega source currency was {currency}; Minishop keeps payment currency " + "controlled by tariffs/default currency. Configure PLATEGA_SUPPORTED_CURRENCIES " + "manually if this instance needs a different currency." + ) return _provider_mapping_result(gateway_type, ["platega_sbp"], overrides, warnings) return _provider_mapping_result(gateway_type, [], overrides, warnings) @@ -972,12 +982,15 @@ class RemnashopImporter: "REMNAWAVE_WEBHOOK_SECRET", "BOT_SUPPORT_USERNAME", "APP_DEFAULT_LOCALE", - "BOT_MINI_APP", ) if self.source_env.get(key) + and not _is_placeholder_setting_value(self.source_env.get(key)) ), "has_app_crypt_key": bool(self.source_env.get("APP_CRYPT_KEY")), "source_urls": remnashop_source_urls_from_env(self.source_env), + "ignored_keys_present": sorted( + key for key in ("BOT_MINI_APP",) if self.source_env.get(key) + ), }, ) return written @@ -1595,13 +1608,16 @@ class RemnashopImporter: "REMNAWAVE_WEBHOOK_SECRET", "BOT_SUPPORT_USERNAME", "APP_DEFAULT_LOCALE", - "BOT_MINI_APP", "APP_DOMAIN", "APP_CRYPT_KEY", ) if self.source_env.get(key) + and not _is_placeholder_setting_value(self.source_env.get(key)) ), "source_urls": remnashop_source_urls_from_env(self.source_env), + "ignored_keys_present": sorted( + key for key in ("BOT_MINI_APP",) if self.source_env.get(key) + ), }, } env_override_keys = await self.import_env_settings() diff --git a/docs/migrations/remnashop.md b/docs/migrations/remnashop.md index a719c4a..00dd2af 100644 --- a/docs/migrations/remnashop.md +++ b/docs/migrations/remnashop.md @@ -33,14 +33,30 @@ message logs как заметки, чтобы администратор мог - `REMNAWAVE_TOKEN` -> `PANEL_API_KEY`; - `REMNAWAVE_WEBHOOK_SECRET` -> `PANEL_WEBHOOK_SECRET`; - `BOT_SUPPORT_USERNAME` -> `SUPPORT_LINK`; -- `APP_DEFAULT_LOCALE` -> `DEFAULT_LANGUAGE`; -- `BOT_MINI_APP` -> `SUBSCRIPTION_MINI_APP_URL`, если там уже HTTPS URL. +- `APP_DEFAULT_LOCALE` -> `DEFAULT_LANGUAGE`. + +`BOT_MINI_APP` из Remnashop не переносится автоматически. В Remnashop эта +переменная управляет кнопкой подключения к subscription page или внешнему Mini +App, а не веб-кабинетом Remnashop. В Minishop `SUBSCRIPTION_MINI_APP_URL` +должен указывать на текущий frontend/Mini App этого стека; wizard настраивает +его из `WEBHOOK_HOST`/`MINIAPP_HOST` или `MINIAPP_PUBLIC_URL`. + +Значения-заглушки вроде `change_me` importer пропускает, чтобы случайно не +записать шаблонные секреты в рабочую конфигурацию. Платежные провайдеры берутся из таблицы Remnashop `payment_gateways`. Поддерживаются и автоматически маппятся: Telegram Stars, YooKassa, WATA, CryptoPay, Heleket, FreeKassa и Platega. Для них importer переносит флаги -включения, API-ключи/merchant IDs и доступные provider-specific параметры в -раздел настроек админки. +включения, API-ключи/merchant IDs и прямые технические параметры, без которых +провайдер не сможет работать: YooKassa receipt email/VAT, FreeKassa second +secret/payment method/server IP и Platega payment method. + +Provider currency и supported-currency ограничения не переносятся автоматически: +в Minishop валюта платежа управляется тарифами и `DEFAULT_CURRENCY_SYMBOL`. +Если старый gateway Remnashop был настроен на нестандартную валюту, importer +оставит предупреждение в JSON-сводке; проверьте `CRYPTOPAY_ASSET`, +`HELEKET_CURRENCY`, `HELEKET_SUPPORTED_CURRENCIES` или +`PLATEGA_SUPPORTED_CURRENCIES` вручную. Провайдеры YooMoney, Cryptomus, MulenPay, PayMaster, RoboKassa и UrlPay сейчас не имеют прямого аналога в Minishop. Если они были в Remnashop, importer @@ -72,7 +88,7 @@ Remnashop может хранить секреты в формате `enc_...`. `raw.githubusercontent.com`, без клонирования репозитория. 2. Вы указываете source PostgreSQL DSN Remnashop и schema, обычно `public`. 3. Опционально указываете путь к старому Remnashop `.env` для `APP_CRYPT_KEY`, - Remnawave API settings и переносимых payment/provider settings. + Remnawave API settings и переносимых settings. 4. Вы выбираете целевую БД: текущую compose-БД или ручной target DSN. 5. При необходимости указываете JSON map тарифов Remnashop в локальные `tariff_key`, например `{"basic": "standard_month"}`. diff --git a/tests/test_remnashop_import.py b/tests/test_remnashop_import.py index 0010ef1..4316f96 100644 --- a/tests/test_remnashop_import.py +++ b/tests/test_remnashop_import.py @@ -73,10 +73,24 @@ def test_remnashop_env_parser_and_overrides_map_safe_values(): "PANEL_WEBHOOK_SECRET": "panel secret", "SUPPORT_LINK": "https://t.me/support_bot", "DEFAULT_LANGUAGE": "en", - "SUBSCRIPTION_MINI_APP_URL": "https://app.example.com/", } +def test_remnashop_env_overrides_skip_placeholders_and_mini_app(): + overrides = remnashop_env_overrides( + { + "REMNAWAVE_HOST": "change_me", + "REMNAWAVE_TOKEN": "change_me", + "REMNAWAVE_WEBHOOK_SECRET": "change_me", + "BOT_SUPPORT_USERNAME": "change_me", + "APP_DEFAULT_LOCALE": "change_me", + "BOT_MINI_APP": "https://old-mini-app.example.com/", + } + ) + + assert overrides == {} + + def test_remnashop_yookassa_gateway_maps_to_current_provider_settings(): result = remnashop_payment_gateway_overrides( { @@ -138,6 +152,38 @@ def test_remnashop_free_kassa_and_platega_gateways_map_available_settings(): assert platega["provider_ids"] == ["platega_sbp"] assert platega["overrides"]["PLATEGA_SBP_ENABLED"] is True assert platega["overrides"]["PLATEGA_SBP_METHOD"] == 2 + assert "PLATEGA_SUPPORTED_CURRENCIES" not in platega["overrides"] + + +def test_remnashop_crypto_provider_currency_is_not_imported_as_setting_override(): + cryptopay = remnashop_payment_gateway_overrides( + { + "type": "CRYPTOPAY", + "currency": "USD", + "is_active": True, + "settings": {"api_key": "crypto-token"}, + } + ) + assert cryptopay["overrides"] == { + "CRYPTOPAY_ENABLED": True, + "CRYPTOPAY_TOKEN": "crypto-token", + } + assert any("source currency was USD" in warning for warning in cryptopay["warnings"]) + + heleket = remnashop_payment_gateway_overrides( + { + "type": "HELEKET", + "currency": "USD", + "is_active": True, + "settings": {"merchant_id": "merchant", "api_key": "secret"}, + } + ) + assert heleket["overrides"] == { + "HELEKET_ENABLED": True, + "HELEKET_MERCHANT_ID": "merchant", + "HELEKET_API_KEY": "secret", + } + assert "HELEKET_CURRENCY" not in heleket["overrides"] def test_remnashop_unsupported_gateway_is_reported_without_overrides():