feat: добавлены новые компоненты для управления правилами и серверами
All checks were successful
Build and Deploy Gateway / build-and-deploy (push) Successful in 25s

- Создан компонент RuleEditorDrawer для редактирования правил с поддержкой JSON.
- Добавлен компонент ServersPage для отображения и управления серверами.
- Реализован компонент SettingsPage для управления подписками и конфигурациями.
- Создан компонент Sidebar для навигации по приложению.
- Добавлен компонент StatusPane для отображения статуса сервера.
- Реализован компонент Toasts для отображения уведомлений.
- Создан компонент Topbar для отображения информации о текущем состоянии.
- Добавлен модуль country.js для определения страны по тегу сервера.

Refs: None
This commit is contained in:
2026-05-08 19:31:49 +03:00
parent a8f2c6f3f9
commit 8476ab16e5
27 changed files with 3014 additions and 1139 deletions

33
src/web/utils/country.js Normal file
View File

@@ -0,0 +1,33 @@
// Грубое определение страны по тегу сервера и/или хосту.
// Это эвристика — мы не делаем GeoIP-lookup.
const COUNTRIES = [
{ re: /\b(ru|россия|russia|moscow|spb)\b/i, code: "RU", flag: "🇷🇺" },
{ re: /\b(de|germany|frankfurt|berlin|deu)\b/i, code: "DE", flag: "🇩🇪" },
{ re: /\b(nl|netherlands|amsterdam|holland)\b/i, code: "NL", flag: "🇳🇱" },
{ re: /\b(us|usa|america|new[-_ ]?york|chicago|miami)\b/i, code: "US", flag: "🇺🇸" },
{ re: /\b(uk|britain|london|england)\b/i, code: "GB", flag: "🇬🇧" },
{ re: /\b(fr|france|paris)\b/i, code: "FR", flag: "🇫🇷" },
{ re: /\b(jp|japan|tokyo)\b/i, code: "JP", flag: "🇯🇵" },
{ re: /\b(sg|singapore)\b/i, code: "SG", flag: "🇸🇬" },
{ re: /\b(hk|hongkong|hong[-_ ]?kong)\b/i, code: "HK", flag: "🇭🇰" },
{ re: /\b(fi|finland|helsinki)\b/i, code: "FI", flag: "🇫🇮" },
{ re: /\b(se|sweden|stockholm)\b/i, code: "SE", flag: "🇸🇪" },
{ re: /\b(pl|poland|warsaw)\b/i, code: "PL", flag: "🇵🇱" },
{ re: /\b(tr|turkey|istanbul)\b/i, code: "TR", flag: "🇹🇷" },
{ re: /\b(ua|ukraine|kiev|kyiv)\b/i, code: "UA", flag: "🇺🇦" },
];
export function detectCountry(...inputs) {
const text = inputs.filter(Boolean).join(" ").toLowerCase();
for (const c of COUNTRIES) {
if (c.re.test(text)) return c;
}
return null;
}
export function flagFor(server) {
if (!server) return "";
const detected = detectCountry(server.tag, server.server);
return detected?.flag || "🌐";
}