From e4f1d55e0c1d8b4dd8ad20c9fc57598f360ee75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A0nh=20Nguy=E1=BB=85n?= Date: Thu, 11 Jun 2026 18:04:18 +0700 Subject: [PATCH] Add more support to cloud and self-hosted Bitrix --- internal/gateway/methods/bitrix_portals.go | 4 ++-- internal/gateway/methods/bitrix_portals_test.go | 4 ++++ .../bitrix24/bitrix-portal-form-step.tsx | 17 ++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/internal/gateway/methods/bitrix_portals.go b/internal/gateway/methods/bitrix_portals.go index 99eb35a7..28db90dc 100644 --- a/internal/gateway/methods/bitrix_portals.go +++ b/internal/gateway/methods/bitrix_portals.go @@ -69,9 +69,9 @@ type bitrixPortalView struct { // Validation regexes. Kept package-level so they compile once and tests can // reference them directly. var ( - // Bitrix24 cloud portal hosts. Matches *.bitrix24.{com,eu,ru,de,fr,jp,in,kz,ua,by} + // Bitrix24 cloud portal hosts. Matches *.bitrix24.{com,eu,ru,de,fr,jp,in,kz,ua,by,vn,tr,es,com.br,com.ar} // plus self-hosted *.bitrix.info. Subdomain regex matches DNS label rules. - bitrixCloudDomainRegex = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.(bitrix24\.(com|eu|ru|de|fr|jp|in|kz|ua|by)|bitrix\.info)$`) + bitrixCloudDomainRegex = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.(bitrix24\.(com|eu|ru|de|fr|jp|in|kz|ua|by|vn|tr|es|com\.br|com\.ar)|bitrix\.info)$`) // Valid hostname regex for self-hosted Bitrix24 instances (custom domains). // Accepts any valid FQDN or hostname with optional port (e.g. bx.example.com, portal.internal:8443). diff --git a/internal/gateway/methods/bitrix_portals_test.go b/internal/gateway/methods/bitrix_portals_test.go index f2d9995b..2ed4568f 100644 --- a/internal/gateway/methods/bitrix_portals_test.go +++ b/internal/gateway/methods/bitrix_portals_test.go @@ -563,6 +563,10 @@ func TestBitrixDomainRegex(t *testing.T) { "my-corp.bitrix24.eu", "a.bitrix24.com", "company.bitrix.info", + "mycorp.bitrix24.vn", + "portal.bitrix24.tr", + "miempresa.bitrix24.es", + "empresa.bitrix24.com.br", } bad := []string{ "tamgiac.bitrix24", diff --git a/ui/web/src/pages/channels/bitrix24/bitrix-portal-form-step.tsx b/ui/web/src/pages/channels/bitrix24/bitrix-portal-form-step.tsx index 13772dda..aa0a15ea 100644 --- a/ui/web/src/pages/channels/bitrix24/bitrix-portal-form-step.tsx +++ b/ui/web/src/pages/channels/bitrix24/bitrix-portal-form-step.tsx @@ -10,11 +10,14 @@ import { useBitrixPortalCreate } from "./use-bitrix-portals"; // Validation mirrors the server-side regex in // internal/gateway/methods/bitrix_portals.go. Server is authoritative; // client validation is purely UX so the operator gets feedback before a -// round-trip. Pattern intentionally accepts a wide TLD set (Bitrix24 has -// regional clouds: .com, .eu, .ru, .de, .fr, .jp, .in, .kz, .ua, .by) plus -// .bitrix.info for self-hosted. +// round-trip. Pattern accepts Bitrix24 regional clouds (.com, .eu, .ru, +// .de, .fr, .jp, .in, .kz, .ua, .by, .vn, .tr, .es, .com.br, .com.ar), +// .bitrix.info self-hosted, plus any valid hostname/port for fully +// self-hosted custom domains. const BITRIX_DOMAIN_RE = - /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.(bitrix24\.(com|eu|ru|de|fr|jp|in|kz|ua|by)|bitrix\.info)$/; + /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.(bitrix24\.(com|eu|ru|de|fr|jp|in|kz|ua|by|vn|tr|es|com\.br|com\.ar)|bitrix\.info)$/; +const SELF_HOSTED_DOMAIN_RE = + /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*(:\d+)?$/; const PORTAL_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,62}[a-z0-9]$/; interface BitrixPortalFormStepProps { @@ -53,9 +56,9 @@ export function BitrixPortalFormStep({ onSuccess, onCancel }: BitrixPortalFormSt defaultValue: "Use lowercase letters, digits, hyphens, underscores (2-64 chars).", }); } - if (!BITRIX_DOMAIN_RE.test(domain.toLowerCase())) { + if (!BITRIX_DOMAIN_RE.test(domain.toLowerCase()) && !SELF_HOSTED_DOMAIN_RE.test(domain.toLowerCase())) { e.domain = t("bitrix24.create.errors.invalidDomain", { - defaultValue: "Must be a valid Bitrix24 portal domain (e.g. mycorp.bitrix24.com).", + defaultValue: "Must be a valid hostname (e.g. *.bitrix24.com, *.bitrix.info, or your self-hosted domain).", }); } if (!clientId.trim()) e.client_id = t("common.required", { defaultValue: "Required" }); @@ -121,7 +124,7 @@ export function BitrixPortalFormStep({ onSuccess, onCancel }: BitrixPortalFormSt value={domain} onChange={(e) => setDomain(e.target.value)} onBlur={handleDomainBlur} - placeholder="tamgiac.bitrix24.com" + placeholder="mycorp.bitrix24.vn or bitrix.example.com" autoComplete="off" autoFocus />