Release 3.9.0+3: subscription-URL routing, emoji flags, Remnawave provisioning.

Route credential-less http(s) URLs pasted into the add-link field to
subscription import (fixes remaining 'proxy URI missing username').
Extend geoflag with RU country names and city hints; live Remnawave
names already carrying emoji flags are kept as-is. Add admin
provisioning via configs/remnawave-api.json (GET by-username / POST
users, 50 GB MONTH plan) and the «Выдать доступ» UI panel.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-08-01 17:36:54 +03:00
co-authored by Cursor
parent e34312ef9c
commit 77bd7da861
24 changed files with 974 additions and 110 deletions
+44 -1
View File
@@ -84,7 +84,7 @@ func emojiToISO(flag string) string {
cc := string([]byte{byte('A' + a), byte('A' + b)})
if !Known(cc) {
// Still return regional indicator pair as ISO guess for display.
if unicode.IsLetter(rune('A'+a)) {
if unicode.IsLetter(rune('A' + a)) {
return cc
}
return ""
@@ -118,6 +118,49 @@ var countryNames = []nameCode{
{"ireland", "IE"}, {"iceland", "IS"}, {"luxembourg", "LU"},
{"malaysia", "MY"}, {"thailand", "TH"}, {"vietnam", "VN"},
{"indonesia", "ID"}, {"philippines", "PH"}, {"south africa", "ZA"},
// Russian country names (lowercase; longer/more specific first).
{"великобритания", "GB"}, {"нидерланды", "NL"}, {"голландия", "NL"},
{"германия", "DE"}, {"австралия", "AU"}, {"австрия", "AT"},
{"швейцария", "CH"}, {"финляндия", "FI"}, {"швеция", "SE"},
{"норвегия", "NO"}, {"дания", "DK"}, {"польша", "PL"},
{"франция", "FR"}, {"испания", "ES"}, {"италия", "IT"},
{"португалия", "PT"}, {"бельгия", "BE"}, {"чехия", "CZ"},
{"словакия", "SK"}, {"словения", "SI"}, {"венгрия", "HU"},
{"румыния", "RO"}, {"болгария", "BG"}, {"украина", "UA"},
{"россия", "RU"}, {"турция", "TR"}, {"израиль", "IL"},
{"эмираты", "AE"}, {"дубай", "AE"}, {"сингапур", "SG"},
{"гонконг", "HK"}, {"япония", "JP"}, {"корея", "KR"},
{"тайвань", "TW"}, {"индия", "IN"}, {"канада", "CA"},
{"бразилия", "BR"}, {"аргентина", "AR"}, {"латвия", "LV"},
{"литва", "LT"}, {"эстония", "EE"}, {"молдова", "MD"},
{"грузия", "GE"}, {"армения", "AM"}, {"казахстан", "KZ"},
{"узбекистан", "UZ"}, {"сербия", "RS"}, {"хорватия", "HR"},
{"греция", "GR"}, {"кипр", "CY"}, {"ирландия", "IE"},
{"исландия", "IS"}, {"люксембург", "LU"}, {"англия", "GB"},
{"сша", "US"}, {"америка", "US"},
// City → country hints (EN + RU).
{"frankfurt", "DE"}, {"amsterdam", "NL"}, {"london", "GB"},
{"paris", "FR"}, {"helsinki", "FI"}, {"stockholm", "SE"},
{"oslo", "NO"}, {"copenhagen", "DK"}, {"warsaw", "PL"},
{"prague", "CZ"}, {"vienna", "AT"}, {"zurich", "CH"},
{"madrid", "ES"}, {"milan", "IT"}, {"lisbon", "PT"},
{"moscow", "RU"}, {"istanbul", "TR"}, {"tokyo", "JP"},
{"seoul", "KR"}, {"new york", "US"}, {"los angeles", "US"},
{"dallas", "US"}, {"miami", "US"}, {"chicago", "US"},
{"toronto", "CA"}, {"riga", "LV"}, {"vilnius", "LT"},
{"tallinn", "EE"}, {"bucharest", "RO"}, {"sofia", "BG"},
{"kyiv", "UA"}, {"kiev", "UA"}, {"tbilisi", "GE"},
{"almaty", "KZ"}, {"франкфурт", "DE"}, {"амстердам", "NL"},
{"лондон", "GB"}, {"париж", "FR"}, {"хельсинки", "FI"},
{"стокгольм", "SE"}, {"осло", "NO"}, {"копенгаген", "DK"},
{"варшава", "PL"}, {"прага", "CZ"}, {"вена", "AT"},
{"цюрих", "CH"}, {"мадрид", "ES"}, {"милан", "IT"},
{"москва", "RU"}, {"стамбул", "TR"}, {"токио", "JP"},
{"сеул", "KR"}, {"торонто", "CA"}, {"рига", "LV"},
{"вильнюс", "LT"}, {"таллин", "EE"}, {"бухарест", "RO"},
{"софия", "BG"}, {"киев", "UA"}, {"тбилиси", "GE"},
}
var isoSet = map[string]struct{}{