mirror of
https://github.com/koho/frpmgr.git
synced 2025-10-20 16:03:47 +08:00
Add an open port action in quick add menu (#37)
* Add an open port action in quick add menu * Add tooltip to show error * Update passwd.go
This commit is contained in:
1697
i18n/catalog.go
1697
i18n/catalog.go
File diff suppressed because it is too large
Load Diff
@ -87,6 +87,58 @@
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Number out of allowed range",
|
||||
"message": "Number out of allowed range",
|
||||
"translation": "Number out of allowed range",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number greater than {Intt}.",
|
||||
"message": "Please enter a number greater than {Intt}.",
|
||||
"translation": "Please enter a number greater than {Intt}.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Intt",
|
||||
"string": "%[1]d",
|
||||
"type": "int",
|
||||
"underlyingType": "int",
|
||||
"argNum": 1,
|
||||
"expr": "int(t)"
|
||||
}
|
||||
],
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Password mismatch",
|
||||
"message": "Password mismatch",
|
||||
"translation": "Password mismatch",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Please check and try again.",
|
||||
"message": "Please check and try again.",
|
||||
"translation": "Please check and try again.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Not a number",
|
||||
"message": "Not a number",
|
||||
"translation": "Not a number",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Please enter a valid number.",
|
||||
"message": "Please enter a valid number.",
|
||||
"translation": "Please enter a valid number.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "New Version!",
|
||||
"message": "New Version!",
|
||||
@ -1336,6 +1388,20 @@
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "Port",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Open Port",
|
||||
"message": "Open Port",
|
||||
"translation": "Open Port",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Preferences",
|
||||
"message": "Preferences",
|
||||
@ -1483,13 +1549,6 @@
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "SOCKS5 Proxy",
|
||||
"message": "SOCKS5 Proxy",
|
||||
"translation": "SOCKS5 Proxy",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Enable",
|
||||
"message": "Enable",
|
||||
@ -1600,13 +1659,6 @@
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "Port",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Passive Port Range",
|
||||
"message": "Passive Port Range",
|
||||
@ -1696,6 +1748,87 @@
|
||||
"translation": "The password is incorrect. Re-enter password.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Invalid Input",
|
||||
"message": "Invalid Input",
|
||||
"translation": "Invalid Input",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Number} to {Number_1}.",
|
||||
"message": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translation": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Number",
|
||||
"string": "%.[1]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Number_1",
|
||||
"string": "%.[2]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 2
|
||||
}
|
||||
],
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"message": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translation": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Arg_1",
|
||||
"string": "%[1]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Arg_2",
|
||||
"string": "%[2]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 2
|
||||
}
|
||||
],
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "The text does not match the required pattern.",
|
||||
"message": "The text does not match the required pattern.",
|
||||
"translation": "The text does not match the required pattern.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Selection Required",
|
||||
"message": "Selection Required",
|
||||
"translation": "Selection Required",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "Please select one of the provided options.",
|
||||
"message": "Please select one of the provided options.",
|
||||
"translation": "Please select one of the provided options.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "A selection is required.",
|
||||
"message": "A selection is required.",
|
||||
"translation": "A selection is required.",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
}
|
||||
]
|
||||
}
|
@ -71,6 +71,46 @@
|
||||
"message": "Log Files",
|
||||
"translation": "Archivos de registro"
|
||||
},
|
||||
{
|
||||
"id": "Number out of allowed range",
|
||||
"message": "Number out of allowed range",
|
||||
"translation": "Número fuera del rango permitido"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number greater than {Intt}.",
|
||||
"message": "Please enter a number greater than {Intt}.",
|
||||
"translation": "Ingrese un número mayor que {Intt}.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Intt",
|
||||
"string": "%[1]d",
|
||||
"type": "int",
|
||||
"underlyingType": "int",
|
||||
"argNum": 1,
|
||||
"expr": "int(t)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Password mismatch",
|
||||
"message": "Password mismatch",
|
||||
"translation": "Contraseña no coincide"
|
||||
},
|
||||
{
|
||||
"id": "Please check and try again.",
|
||||
"message": "Please check and try again.",
|
||||
"translation": "Por favor revisa e intenta de nuevo."
|
||||
},
|
||||
{
|
||||
"id": "Not a number",
|
||||
"message": "Not a number",
|
||||
"translation": "No un número"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a valid number.",
|
||||
"message": "Please enter a valid number.",
|
||||
"translation": "Por favor ingrese un número valido."
|
||||
},
|
||||
{
|
||||
"id": "New Version!",
|
||||
"message": "New Version!",
|
||||
@ -994,6 +1034,16 @@
|
||||
"message": "Add",
|
||||
"translation": "Agregar"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "Puerto"
|
||||
},
|
||||
{
|
||||
"id": "Open Port",
|
||||
"message": "Open Port",
|
||||
"translation": "Puerto abierto"
|
||||
},
|
||||
{
|
||||
"id": "Preferences",
|
||||
"message": "Preferences",
|
||||
@ -1099,11 +1149,6 @@
|
||||
"message": "HTTP File Server",
|
||||
"translation": "Servidor de archivos HTTP"
|
||||
},
|
||||
{
|
||||
"id": "SOCKS5 Proxy",
|
||||
"message": "SOCKS5 Proxy",
|
||||
"translation": "Proxy SOCKS5"
|
||||
},
|
||||
{
|
||||
"id": "Enable",
|
||||
"message": "Enable",
|
||||
@ -1194,11 +1239,6 @@
|
||||
"message": "Disable",
|
||||
"translation": "Deshabilitar"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "Puerto"
|
||||
},
|
||||
{
|
||||
"id": "Passive Port Range",
|
||||
"message": "Passive Port Range",
|
||||
@ -1266,6 +1306,73 @@
|
||||
"id": "The password is incorrect. Re-enter password.",
|
||||
"message": "The password is incorrect. Re-enter password.",
|
||||
"translation": "La contraseña es incorrecta. Escriba la contraseña otra vez."
|
||||
},
|
||||
{
|
||||
"id": "Invalid Input",
|
||||
"message": "Invalid Input",
|
||||
"translation": "Entrada invalida"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Number} to {Number_1}.",
|
||||
"message": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translation": "Ingrese un número de {Number} a {Number_1}.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Number",
|
||||
"string": "%.[1]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Number_1",
|
||||
"string": "%.[2]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"message": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translation": "Ingrese un número de {Arg_1} a {Arg_2}.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Arg_1",
|
||||
"string": "%[1]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Arg_2",
|
||||
"string": "%[2]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "The text does not match the required pattern.",
|
||||
"message": "The text does not match the required pattern.",
|
||||
"translation": "El texto no coincide con el patrón requerido."
|
||||
},
|
||||
{
|
||||
"id": "Selection Required",
|
||||
"message": "Selection Required",
|
||||
"translation": "Selección requerida"
|
||||
},
|
||||
{
|
||||
"id": "Please select one of the provided options.",
|
||||
"message": "Please select one of the provided options.",
|
||||
"translation": "Seleccione una de las opciones proporcionadas."
|
||||
},
|
||||
{
|
||||
"id": "A selection is required.",
|
||||
"message": "A selection is required.",
|
||||
"translation": "Se requiere una selección."
|
||||
}
|
||||
]
|
||||
}
|
@ -71,6 +71,46 @@
|
||||
"message": "Log Files",
|
||||
"translation": "ログファイル"
|
||||
},
|
||||
{
|
||||
"id": "Number out of allowed range",
|
||||
"message": "Number out of allowed range",
|
||||
"translation": "許容範囲外の数値"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number greater than {Intt}.",
|
||||
"message": "Please enter a number greater than {Intt}.",
|
||||
"translation": "{Intt} より大きい数値を入力してください。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Intt",
|
||||
"string": "%[1]d",
|
||||
"type": "int",
|
||||
"underlyingType": "int",
|
||||
"argNum": 1,
|
||||
"expr": "int(t)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Password mismatch",
|
||||
"message": "Password mismatch",
|
||||
"translation": "パスワードの不一致"
|
||||
},
|
||||
{
|
||||
"id": "Please check and try again.",
|
||||
"message": "Please check and try again.",
|
||||
"translation": "もう一度確認してください。"
|
||||
},
|
||||
{
|
||||
"id": "Not a number",
|
||||
"message": "Not a number",
|
||||
"translation": "数字ではありません"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a valid number.",
|
||||
"message": "Please enter a valid number.",
|
||||
"translation": "有効な数値を入力してください。"
|
||||
},
|
||||
{
|
||||
"id": "New Version!",
|
||||
"message": "New Version!",
|
||||
@ -1004,6 +1044,16 @@
|
||||
"message": "Add",
|
||||
"translation": "追加"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "ポート"
|
||||
},
|
||||
{
|
||||
"id": "Open Port",
|
||||
"message": "Open Port",
|
||||
"translation": "ポート開放"
|
||||
},
|
||||
{
|
||||
"id": "Preferences",
|
||||
"message": "Preferences",
|
||||
@ -1109,11 +1159,6 @@
|
||||
"message": "HTTP File Server",
|
||||
"translation": "HTTP ファイルサーバー"
|
||||
},
|
||||
{
|
||||
"id": "SOCKS5 Proxy",
|
||||
"message": "SOCKS5 Proxy",
|
||||
"translation": "SOCKS5 プロキシ"
|
||||
},
|
||||
{
|
||||
"id": "Enable",
|
||||
"message": "Enable",
|
||||
@ -1204,11 +1249,6 @@
|
||||
"message": "Disable",
|
||||
"translation": "無効"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "ポート"
|
||||
},
|
||||
{
|
||||
"id": "Passive Port Range",
|
||||
"message": "Passive Port Range",
|
||||
@ -1276,6 +1316,73 @@
|
||||
"id": "The password is incorrect. Re-enter password.",
|
||||
"message": "The password is incorrect. Re-enter password.",
|
||||
"translation": "パスワードが正しくありません。 パスワード再入力。"
|
||||
},
|
||||
{
|
||||
"id": "Invalid Input",
|
||||
"message": "Invalid Input",
|
||||
"translation": "無効入力"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Number} to {Number_1}.",
|
||||
"message": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translation": "{Number} から {Number_1} までの数字を入力してください。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Number",
|
||||
"string": "%.[1]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Number_1",
|
||||
"string": "%.[2]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"message": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translation": "{Arg_1} から {Arg_2} までの数値を入力してください。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Arg_1",
|
||||
"string": "%[1]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Arg_2",
|
||||
"string": "%[2]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "The text does not match the required pattern.",
|
||||
"message": "The text does not match the required pattern.",
|
||||
"translation": "テキストが必要なパターンと一致しません。"
|
||||
},
|
||||
{
|
||||
"id": "Selection Required",
|
||||
"message": "Selection Required",
|
||||
"translation": "選択必須"
|
||||
},
|
||||
{
|
||||
"id": "Please select one of the provided options.",
|
||||
"message": "Please select one of the provided options.",
|
||||
"translation": "提供されたオプションのいずれかを選択してください。"
|
||||
},
|
||||
{
|
||||
"id": "A selection is required.",
|
||||
"message": "A selection is required.",
|
||||
"translation": "選択が必要です。"
|
||||
}
|
||||
]
|
||||
}
|
@ -71,6 +71,46 @@
|
||||
"message": "Log Files",
|
||||
"translation": "로그 파일"
|
||||
},
|
||||
{
|
||||
"id": "Number out of allowed range",
|
||||
"message": "Number out of allowed range",
|
||||
"translation": "허용 범위를 벗어난 숫자"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number greater than {Intt}.",
|
||||
"message": "Please enter a number greater than {Intt}.",
|
||||
"translation": "{Intt}보다 큰 숫자를 입력하세요.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Intt",
|
||||
"string": "%[1]d",
|
||||
"type": "int",
|
||||
"underlyingType": "int",
|
||||
"argNum": 1,
|
||||
"expr": "int(t)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Password mismatch",
|
||||
"message": "Password mismatch",
|
||||
"translation": "암호 불일치"
|
||||
},
|
||||
{
|
||||
"id": "Please check and try again.",
|
||||
"message": "Please check and try again.",
|
||||
"translation": "확인하고 다시 시도해 주세요."
|
||||
},
|
||||
{
|
||||
"id": "Not a number",
|
||||
"message": "Not a number",
|
||||
"translation": "숫자가 아님"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a valid number.",
|
||||
"message": "Please enter a valid number.",
|
||||
"translation": "유효한 숫자를 입력하세요."
|
||||
},
|
||||
{
|
||||
"id": "New Version!",
|
||||
"message": "New Version!",
|
||||
@ -994,6 +1034,16 @@
|
||||
"message": "Add",
|
||||
"translation": "추가하다"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "포트"
|
||||
},
|
||||
{
|
||||
"id": "Open Port",
|
||||
"message": "Open Port",
|
||||
"translation": "오픈 포트"
|
||||
},
|
||||
{
|
||||
"id": "Preferences",
|
||||
"message": "Preferences",
|
||||
@ -1099,11 +1149,6 @@
|
||||
"message": "HTTP File Server",
|
||||
"translation": "HTTP 파일 서버"
|
||||
},
|
||||
{
|
||||
"id": "SOCKS5 Proxy",
|
||||
"message": "SOCKS5 Proxy",
|
||||
"translation": "SOCKS5 프록시"
|
||||
},
|
||||
{
|
||||
"id": "Enable",
|
||||
"message": "Enable",
|
||||
@ -1194,11 +1239,6 @@
|
||||
"message": "Disable",
|
||||
"translation": "폐쇄"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "포트"
|
||||
},
|
||||
{
|
||||
"id": "Passive Port Range",
|
||||
"message": "Passive Port Range",
|
||||
@ -1266,6 +1306,73 @@
|
||||
"id": "The password is incorrect. Re-enter password.",
|
||||
"message": "The password is incorrect. Re-enter password.",
|
||||
"translation": "비밀번호가 올바르지 않습니다. 비밀번호를 다시 입력하세요."
|
||||
},
|
||||
{
|
||||
"id": "Invalid Input",
|
||||
"message": "Invalid Input",
|
||||
"translation": "잘못된 입력"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Number} to {Number_1}.",
|
||||
"message": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translation": "{Number}에서 {Number_1}까지의 숫자를 입력하세요.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Number",
|
||||
"string": "%.[1]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Number_1",
|
||||
"string": "%.[2]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"message": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translation": "{Arg_1}에서 {Arg_2} 사이의 숫자를 입력하십시오.",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Arg_1",
|
||||
"string": "%[1]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Arg_2",
|
||||
"string": "%[2]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "The text does not match the required pattern.",
|
||||
"message": "The text does not match the required pattern.",
|
||||
"translation": "텍스트가 필수 패턴과 일치하지 않습니다."
|
||||
},
|
||||
{
|
||||
"id": "Selection Required",
|
||||
"message": "Selection Required",
|
||||
"translation": "선택 필수"
|
||||
},
|
||||
{
|
||||
"id": "Please select one of the provided options.",
|
||||
"message": "Please select one of the provided options.",
|
||||
"translation": "제공된 옵션 중 하나를 선택하십시오."
|
||||
},
|
||||
{
|
||||
"id": "A selection is required.",
|
||||
"message": "A selection is required.",
|
||||
"translation": "선택이 필요합니다."
|
||||
}
|
||||
]
|
||||
}
|
@ -71,6 +71,46 @@
|
||||
"message": "Log Files",
|
||||
"translation": "日志文件"
|
||||
},
|
||||
{
|
||||
"id": "Number out of allowed range",
|
||||
"message": "Number out of allowed range",
|
||||
"translation": "数值超出允许范围"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number greater than {Intt}.",
|
||||
"message": "Please enter a number greater than {Intt}.",
|
||||
"translation": "请输入一个大于 {Intt} 的数字。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Intt",
|
||||
"string": "%[1]d",
|
||||
"type": "int",
|
||||
"underlyingType": "int",
|
||||
"argNum": 1,
|
||||
"expr": "int(t)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Password mismatch",
|
||||
"message": "Password mismatch",
|
||||
"translation": "密码不匹配"
|
||||
},
|
||||
{
|
||||
"id": "Please check and try again.",
|
||||
"message": "Please check and try again.",
|
||||
"translation": "请检查并重试。"
|
||||
},
|
||||
{
|
||||
"id": "Not a number",
|
||||
"message": "Not a number",
|
||||
"translation": "不是数字"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a valid number.",
|
||||
"message": "Please enter a valid number.",
|
||||
"translation": "请输入一个有效的数字。"
|
||||
},
|
||||
{
|
||||
"id": "New Version!",
|
||||
"message": "New Version!",
|
||||
@ -994,6 +1034,16 @@
|
||||
"message": "Add",
|
||||
"translation": "添加"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "端口"
|
||||
},
|
||||
{
|
||||
"id": "Open Port",
|
||||
"message": "Open Port",
|
||||
"translation": "打开端口"
|
||||
},
|
||||
{
|
||||
"id": "Preferences",
|
||||
"message": "Preferences",
|
||||
@ -1099,11 +1149,6 @@
|
||||
"message": "HTTP File Server",
|
||||
"translation": "HTTP 文件服务"
|
||||
},
|
||||
{
|
||||
"id": "SOCKS5 Proxy",
|
||||
"message": "SOCKS5 Proxy",
|
||||
"translation": "SOCKS5 代理"
|
||||
},
|
||||
{
|
||||
"id": "Enable",
|
||||
"message": "Enable",
|
||||
@ -1194,11 +1239,6 @@
|
||||
"message": "Disable",
|
||||
"translation": "禁用"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "端口"
|
||||
},
|
||||
{
|
||||
"id": "Passive Port Range",
|
||||
"message": "Passive Port Range",
|
||||
@ -1266,6 +1306,73 @@
|
||||
"id": "The password is incorrect. Re-enter password.",
|
||||
"message": "The password is incorrect. Re-enter password.",
|
||||
"translation": "密码错误。请重新输入。"
|
||||
},
|
||||
{
|
||||
"id": "Invalid Input",
|
||||
"message": "Invalid Input",
|
||||
"translation": "输入无效"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Number} to {Number_1}.",
|
||||
"message": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translation": "请输入一个从 {Number} 到 {Number_1} 的数字。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Number",
|
||||
"string": "%.[1]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Number_1",
|
||||
"string": "%.[2]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"message": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translation": "请输入一个从 {Arg_1} 到 {Arg_2} 的数字。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Arg_1",
|
||||
"string": "%[1]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Arg_2",
|
||||
"string": "%[2]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "The text does not match the required pattern.",
|
||||
"message": "The text does not match the required pattern.",
|
||||
"translation": "文本与要求的模式不匹配。"
|
||||
},
|
||||
{
|
||||
"id": "Selection Required",
|
||||
"message": "Selection Required",
|
||||
"translation": "必填项"
|
||||
},
|
||||
{
|
||||
"id": "Please select one of the provided options.",
|
||||
"message": "Please select one of the provided options.",
|
||||
"translation": "请选择其中一个选项。"
|
||||
},
|
||||
{
|
||||
"id": "A selection is required.",
|
||||
"message": "A selection is required.",
|
||||
"translation": "需要选择。"
|
||||
}
|
||||
]
|
||||
}
|
@ -71,6 +71,46 @@
|
||||
"message": "Log Files",
|
||||
"translation": "日誌文件"
|
||||
},
|
||||
{
|
||||
"id": "Number out of allowed range",
|
||||
"message": "Number out of allowed range",
|
||||
"translation": "數值超出允許範圍"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number greater than {Intt}.",
|
||||
"message": "Please enter a number greater than {Intt}.",
|
||||
"translation": "請輸入一個大於 {Intt} 的數字。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Intt",
|
||||
"string": "%[1]d",
|
||||
"type": "int",
|
||||
"underlyingType": "int",
|
||||
"argNum": 1,
|
||||
"expr": "int(t)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Password mismatch",
|
||||
"message": "Password mismatch",
|
||||
"translation": "密碼不匹配"
|
||||
},
|
||||
{
|
||||
"id": "Please check and try again.",
|
||||
"message": "Please check and try again.",
|
||||
"translation": "請檢查並重試。"
|
||||
},
|
||||
{
|
||||
"id": "Not a number",
|
||||
"message": "Not a number",
|
||||
"translation": "不是數字"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a valid number.",
|
||||
"message": "Please enter a valid number.",
|
||||
"translation": "請輸入一個有效的數字。"
|
||||
},
|
||||
{
|
||||
"id": "New Version!",
|
||||
"message": "New Version!",
|
||||
@ -994,6 +1034,16 @@
|
||||
"message": "Add",
|
||||
"translation": "添加"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "端口"
|
||||
},
|
||||
{
|
||||
"id": "Open Port",
|
||||
"message": "Open Port",
|
||||
"translation": "打開端口"
|
||||
},
|
||||
{
|
||||
"id": "Preferences",
|
||||
"message": "Preferences",
|
||||
@ -1099,11 +1149,6 @@
|
||||
"message": "HTTP File Server",
|
||||
"translation": "HTTP 文件服務"
|
||||
},
|
||||
{
|
||||
"id": "SOCKS5 Proxy",
|
||||
"message": "SOCKS5 Proxy",
|
||||
"translation": "SOCKS5 代理"
|
||||
},
|
||||
{
|
||||
"id": "Enable",
|
||||
"message": "Enable",
|
||||
@ -1194,11 +1239,6 @@
|
||||
"message": "Disable",
|
||||
"translation": "禁用"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"message": "Port",
|
||||
"translation": "端口"
|
||||
},
|
||||
{
|
||||
"id": "Passive Port Range",
|
||||
"message": "Passive Port Range",
|
||||
@ -1266,6 +1306,73 @@
|
||||
"id": "The password is incorrect. Re-enter password.",
|
||||
"message": "The password is incorrect. Re-enter password.",
|
||||
"translation": "密碼錯誤。請重新輸入。"
|
||||
},
|
||||
{
|
||||
"id": "Invalid Input",
|
||||
"message": "Invalid Input",
|
||||
"translation": "輸入無效"
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Number} to {Number_1}.",
|
||||
"message": "Please enter a number from {Number} to {Number_1}.",
|
||||
"translation": "請輸入一個從 {Number} 到 {Number_1} 的數字。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Number",
|
||||
"string": "%.[1]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Number_1",
|
||||
"string": "%.[2]f",
|
||||
"type": "",
|
||||
"underlyingType": "float64",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"message": "Please enter a number from {Arg_1} to {Arg_2}.",
|
||||
"translation": "請輸入一個從 {Arg_1} 到 {Arg_2} 的數字。",
|
||||
"placeholders": [
|
||||
{
|
||||
"id": "Arg_1",
|
||||
"string": "%[1]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 1
|
||||
},
|
||||
{
|
||||
"id": "Arg_2",
|
||||
"string": "%[2]s",
|
||||
"type": "",
|
||||
"underlyingType": "string",
|
||||
"argNum": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "The text does not match the required pattern.",
|
||||
"message": "The text does not match the required pattern.",
|
||||
"translation": "文本與要求的模式不匹配。"
|
||||
},
|
||||
{
|
||||
"id": "Selection Required",
|
||||
"message": "Selection Required",
|
||||
"translation": "必填項"
|
||||
},
|
||||
{
|
||||
"id": "Please select one of the provided options.",
|
||||
"message": "Please select one of the provided options.",
|
||||
"translation": "請選擇其中一個選項。"
|
||||
},
|
||||
{
|
||||
"id": "A selection is required.",
|
||||
"message": "A selection is required.",
|
||||
"translation": "需要選擇。"
|
||||
}
|
||||
]
|
||||
}
|
@ -2,6 +2,7 @@ package consts
|
||||
|
||||
import (
|
||||
"github.com/koho/frpmgr/i18n"
|
||||
"github.com/koho/frpmgr/pkg/validators"
|
||||
"github.com/lxn/walk"
|
||||
. "github.com/lxn/walk/declarative"
|
||||
"github.com/lxn/win"
|
||||
@ -44,7 +45,7 @@ const (
|
||||
IconFtp = 137
|
||||
IconHttpFile = 69
|
||||
IconHttpProxy = 114
|
||||
IconSocks5 = 146
|
||||
IconOpenPort = 135
|
||||
IconVpn = 47
|
||||
IconNewVersion1 = -1028
|
||||
IconNewVersion2 = 1
|
||||
@ -96,9 +97,10 @@ var (
|
||||
|
||||
// Validators
|
||||
var (
|
||||
ValidateNonEmpty = Regexp{Pattern: "[^\\s]+"}
|
||||
ValidateRequireInteger = Regexp{Pattern: "^\\d+$"}
|
||||
ValidateInteger = Regexp{Pattern: "^\\d*$"}
|
||||
ValidateNonEmpty = validators.Regexp{Pattern: "[^\\s]+"}
|
||||
ValidateRequireInteger = validators.Regexp{Pattern: "^\\d+$"}
|
||||
ValidateInteger = validators.Regexp{Pattern: "^\\d*$"}
|
||||
ValidatePortRange = []Validator{ValidateRequireInteger, validators.Range{Min: 0, Max: 65535}}
|
||||
)
|
||||
|
||||
// Dialogs
|
||||
|
@ -1,20 +0,0 @@
|
||||
package validators
|
||||
|
||||
import "github.com/lxn/walk"
|
||||
|
||||
// TextEqual compares to the text of a LineEdit.
|
||||
type TextEqual struct {
|
||||
Target **walk.LineEdit
|
||||
}
|
||||
|
||||
func (t TextEqual) Create() (walk.Validator, error) {
|
||||
return &TextEqual{t.Target}, nil
|
||||
}
|
||||
|
||||
func (t *TextEqual) Validate(v interface{}) error {
|
||||
text := v.(string)
|
||||
if (*t.Target).Text() == text {
|
||||
return nil
|
||||
}
|
||||
return walk.NewValidationError("Text mismatch", "")
|
||||
}
|
40
pkg/validators/gte.go
Normal file
40
pkg/validators/gte.go
Normal file
@ -0,0 +1,40 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"github.com/koho/frpmgr/i18n"
|
||||
"github.com/lxn/walk"
|
||||
)
|
||||
|
||||
type GTEValidator struct {
|
||||
Value **walk.LineEdit
|
||||
}
|
||||
|
||||
func (g *GTEValidator) Validate(v interface{}) error {
|
||||
text := v.(string)
|
||||
f, err := walk.ParseFloat(text)
|
||||
if err != nil {
|
||||
return nanErr
|
||||
}
|
||||
val := (*g.Value).Text()
|
||||
if val == "" {
|
||||
return silentErr
|
||||
}
|
||||
t, err := walk.ParseFloat(val)
|
||||
if err != nil {
|
||||
return nanErr
|
||||
}
|
||||
if f >= t {
|
||||
return nil
|
||||
}
|
||||
return walk.NewValidationError(i18n.Sprintf("Number out of allowed range"),
|
||||
i18n.Sprintf("Please enter a number greater than %d.", int(t)))
|
||||
}
|
||||
|
||||
// GTE checks whether the input value is greater than or equal to the target value.
|
||||
type GTE struct {
|
||||
Value **walk.LineEdit
|
||||
}
|
||||
|
||||
func (g GTE) Create() (walk.Validator, error) {
|
||||
return >EValidator{g.Value}, nil
|
||||
}
|
30
pkg/validators/passwd.go
Normal file
30
pkg/validators/passwd.go
Normal file
@ -0,0 +1,30 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"github.com/koho/frpmgr/i18n"
|
||||
"github.com/lxn/walk"
|
||||
)
|
||||
|
||||
type PasswordValidator struct {
|
||||
Password **walk.LineEdit
|
||||
}
|
||||
|
||||
func (p *PasswordValidator) Validate(v interface{}) error {
|
||||
text := v.(string)
|
||||
if text == "" {
|
||||
return silentErr
|
||||
}
|
||||
if (*p.Password).Text() == text {
|
||||
return nil
|
||||
}
|
||||
return walk.NewValidationError(i18n.Sprintf("Password mismatch"), i18n.Sprintf("Please check and try again."))
|
||||
}
|
||||
|
||||
// ConfirmPassword checks whether the input text is equal to the password field.
|
||||
type ConfirmPassword struct {
|
||||
Password **walk.LineEdit
|
||||
}
|
||||
|
||||
func (c ConfirmPassword) Create() (walk.Validator, error) {
|
||||
return &PasswordValidator{c.Password}, nil
|
||||
}
|
40
pkg/validators/presenter.go
Normal file
40
pkg/validators/presenter.go
Normal file
@ -0,0 +1,40 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/koho/frpmgr/i18n"
|
||||
"github.com/lxn/walk"
|
||||
)
|
||||
|
||||
var (
|
||||
nanErr = walk.NewValidationError(i18n.Sprintf("Not a number"), i18n.Sprintf("Please enter a valid number."))
|
||||
silentErr = errors.New("")
|
||||
)
|
||||
|
||||
type ToolTipErrorPresenter struct {
|
||||
*walk.ToolTipErrorPresenter
|
||||
}
|
||||
|
||||
func NewToolTipErrorPresenter() (*ToolTipErrorPresenter, error) {
|
||||
p, err := walk.NewToolTipErrorPresenter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ToolTipErrorPresenter{p}, nil
|
||||
}
|
||||
|
||||
func (ttep *ToolTipErrorPresenter) PresentError(err error, widget walk.Widget) {
|
||||
if errors.Is(err, silentErr) {
|
||||
ttep.ToolTipErrorPresenter.PresentError(nil, widget)
|
||||
} else {
|
||||
ttep.ToolTipErrorPresenter.PresentError(err, widget)
|
||||
}
|
||||
}
|
||||
|
||||
// SilentToolTipErrorPresenter hides the tooltip when the input value is empty.
|
||||
type SilentToolTipErrorPresenter struct {
|
||||
}
|
||||
|
||||
func (SilentToolTipErrorPresenter) Create() (walk.ErrorPresenter, error) {
|
||||
return NewToolTipErrorPresenter()
|
||||
}
|
46
pkg/validators/range.go
Normal file
46
pkg/validators/range.go
Normal file
@ -0,0 +1,46 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"github.com/lxn/walk"
|
||||
)
|
||||
|
||||
type RangeValidator struct {
|
||||
*walk.RangeValidator
|
||||
}
|
||||
|
||||
func NewRangeValidator(min, max float64) (*RangeValidator, error) {
|
||||
validator, err := walk.NewRangeValidator(min, max)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RangeValidator{validator}, nil
|
||||
}
|
||||
|
||||
func (rv *RangeValidator) Validate(v interface{}) error {
|
||||
var value float64
|
||||
switch v.(type) {
|
||||
case string:
|
||||
f, err := walk.ParseFloat(v.(string))
|
||||
if err != nil {
|
||||
return nanErr
|
||||
}
|
||||
value = f
|
||||
case float64:
|
||||
value = v.(float64)
|
||||
default:
|
||||
panic("Unsupported type")
|
||||
}
|
||||
return rv.RangeValidator.Validate(value)
|
||||
}
|
||||
|
||||
// Range checks whether the input value is between Min and Max value.
|
||||
// Supported widgets: NumberEdit, LineEdit.
|
||||
type Range struct {
|
||||
Min float64
|
||||
Max float64
|
||||
}
|
||||
|
||||
func (r Range) Create() (walk.Validator, error) {
|
||||
return NewRangeValidator(r.Min, r.Max)
|
||||
}
|
34
pkg/validators/regexp.go
Normal file
34
pkg/validators/regexp.go
Normal file
@ -0,0 +1,34 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"github.com/lxn/walk"
|
||||
)
|
||||
|
||||
type RegexpValidator struct {
|
||||
*walk.RegexpValidator
|
||||
}
|
||||
|
||||
func NewRegexpValidator(pattern string) (*RegexpValidator, error) {
|
||||
re, err := walk.NewRegexpValidator(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RegexpValidator{re}, nil
|
||||
}
|
||||
|
||||
func (rv *RegexpValidator) Validate(v interface{}) error {
|
||||
err := rv.RegexpValidator.Validate(v)
|
||||
if str, ok := v.(string); ok && str == "" && err != nil {
|
||||
return silentErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type Regexp struct {
|
||||
Pattern string
|
||||
}
|
||||
|
||||
func (re Regexp) Create() (walk.Validator, error) {
|
||||
return NewRegexpValidator(re.Pattern)
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/koho/frpmgr/i18n"
|
||||
"github.com/koho/frpmgr/pkg/config"
|
||||
"github.com/koho/frpmgr/pkg/consts"
|
||||
"github.com/koho/frpmgr/pkg/validators"
|
||||
"github.com/lxn/walk"
|
||||
. "github.com/lxn/walk/declarative"
|
||||
)
|
||||
@ -35,9 +36,18 @@ func NewPluginProxyDialog(title string, icon *walk.Icon, plugin string) *PluginP
|
||||
func (pp *PluginProxyDialog) Run(owner walk.Form) (int, error) {
|
||||
widgets := []Widget{
|
||||
Label{Text: i18n.SprintfColon("Remote Port")},
|
||||
LineEdit{Text: Bind("RemotePort", consts.ValidateRequireInteger)},
|
||||
LineEdit{Text: Bind("RemotePort", consts.ValidatePortRange...)},
|
||||
}
|
||||
switch pp.plugin {
|
||||
case consts.PluginHttpProxy, consts.PluginSocks5:
|
||||
pp.binder.Plugin = consts.PluginHttpProxy
|
||||
widgets = append([]Widget{
|
||||
Label{Text: i18n.SprintfColon("Type")},
|
||||
NewRadioButtonGroup("Plugin", nil, []RadioButton{
|
||||
{Text: "HTTP", Value: consts.PluginHttpProxy},
|
||||
{Text: "SOCKS5", Value: consts.PluginSocks5},
|
||||
}),
|
||||
}, widgets...)
|
||||
case consts.PluginStaticFile:
|
||||
// Make the dialog wider
|
||||
remoteView := widgets[1].(LineEdit)
|
||||
@ -50,12 +60,10 @@ func (pp *PluginProxyDialog) Run(owner walk.Form) (int, error) {
|
||||
)
|
||||
}
|
||||
return NewBasicDialog(&pp.Dialog, fmt.Sprintf("%s %s", i18n.Sprintf("Add"), pp.title), pp.icon, DataBinder{
|
||||
AssignTo: &pp.db,
|
||||
DataSource: pp.binder,
|
||||
}, pp.onSave, Composite{
|
||||
Layout: Grid{Columns: 2, MarginsZero: true},
|
||||
Children: widgets,
|
||||
}, VSpacer{}).Run(owner)
|
||||
AssignTo: &pp.db,
|
||||
DataSource: pp.binder,
|
||||
ErrorPresenter: validators.SilentToolTipErrorPresenter{},
|
||||
}, pp.onSave, append(widgets, VSpacer{})...).Run(owner)
|
||||
}
|
||||
|
||||
func (pp *PluginProxyDialog) GetProxies() []*config.Proxy {
|
||||
@ -66,6 +74,9 @@ func (pp *PluginProxyDialog) onSave() {
|
||||
if err := pp.db.Submit(); err != nil {
|
||||
return
|
||||
}
|
||||
if pp.binder.Plugin != "" {
|
||||
pp.plugin = pp.binder.Plugin
|
||||
}
|
||||
pp.Proxies = append(pp.Proxies, &config.Proxy{
|
||||
BaseProxyConf: config.BaseProxyConf{
|
||||
Name: fmt.Sprintf("%s_%s", pp.plugin, pp.binder.RemotePort),
|
||||
|
104
ui/portproxy.go
Normal file
104
ui/portproxy.go
Normal file
@ -0,0 +1,104 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/koho/frpmgr/i18n"
|
||||
"github.com/koho/frpmgr/pkg/config"
|
||||
"github.com/koho/frpmgr/pkg/consts"
|
||||
"github.com/koho/frpmgr/pkg/validators"
|
||||
"github.com/lxn/walk"
|
||||
. "github.com/lxn/walk/declarative"
|
||||
)
|
||||
|
||||
type portProxyBinder struct {
|
||||
quickAddBinder
|
||||
Name string
|
||||
TCP bool
|
||||
UDP bool
|
||||
}
|
||||
|
||||
type PortProxyDialog struct {
|
||||
*walk.Dialog
|
||||
|
||||
Proxies []*config.Proxy
|
||||
binder *portProxyBinder
|
||||
db *walk.DataBinder
|
||||
}
|
||||
|
||||
func NewPortProxyDialog() *PortProxyDialog {
|
||||
dlg := new(PortProxyDialog)
|
||||
dlg.binder = &portProxyBinder{
|
||||
quickAddBinder: quickAddBinder{
|
||||
LocalAddr: "127.0.0.1",
|
||||
},
|
||||
TCP: true,
|
||||
UDP: true,
|
||||
}
|
||||
return dlg
|
||||
}
|
||||
|
||||
func (pp *PortProxyDialog) Run(owner walk.Form) (int, error) {
|
||||
widgets := []Widget{
|
||||
Label{Text: i18n.SprintfColon("Name"), ColumnSpan: 2},
|
||||
LineEdit{Text: Bind("Name"), CueBanner: "open_xxx", ColumnSpan: 2},
|
||||
Label{Text: i18n.SprintfColon("Remote Port"), ColumnSpan: 2},
|
||||
LineEdit{Text: Bind("RemotePort", consts.ValidatePortRange...), ColumnSpan: 2},
|
||||
Label{Text: i18n.SprintfColon("Protocol"), ColumnSpan: 2},
|
||||
Composite{
|
||||
Layout: HBox{MarginsZero: true},
|
||||
ColumnSpan: 2,
|
||||
Children: []Widget{
|
||||
CheckBox{Text: "TCP", Checked: Bind("TCP")},
|
||||
CheckBox{Text: "UDP", Checked: Bind("UDP")},
|
||||
},
|
||||
},
|
||||
Label{Text: i18n.SprintfColon("Local Address")},
|
||||
Label{Text: i18n.SprintfColon("Port")},
|
||||
LineEdit{Text: Bind("LocalAddr", consts.ValidateNonEmpty), StretchFactor: 2},
|
||||
LineEdit{Text: Bind("LocalPort", consts.ValidatePortRange...), StretchFactor: 1},
|
||||
}
|
||||
return NewBasicDialog(&pp.Dialog, i18n.Sprintf("Open Port"), loadSysIcon("shell32", consts.IconOpenPort, 32), DataBinder{
|
||||
AssignTo: &pp.db,
|
||||
DataSource: pp.binder,
|
||||
ErrorPresenter: validators.SilentToolTipErrorPresenter{},
|
||||
}, pp.onSave, Composite{
|
||||
Layout: Grid{Columns: 2, MarginsZero: true},
|
||||
MinSize: Size{Width: 280},
|
||||
Children: widgets,
|
||||
}, VSpacer{}).Run(owner)
|
||||
}
|
||||
|
||||
func (pp *PortProxyDialog) GetProxies() []*config.Proxy {
|
||||
return pp.Proxies
|
||||
}
|
||||
|
||||
func (pp *PortProxyDialog) onSave() {
|
||||
if err := pp.db.Submit(); err != nil {
|
||||
return
|
||||
}
|
||||
name := pp.binder.Name
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("open_%s", pp.binder.RemotePort)
|
||||
}
|
||||
proxy := config.Proxy{
|
||||
BaseProxyConf: config.BaseProxyConf{
|
||||
Name: name,
|
||||
LocalIP: pp.binder.LocalAddr,
|
||||
LocalPort: pp.binder.LocalPort,
|
||||
},
|
||||
RemotePort: pp.binder.RemotePort,
|
||||
}
|
||||
if pp.binder.TCP {
|
||||
tcpProxy := proxy
|
||||
tcpProxy.Name += "_tcp"
|
||||
tcpProxy.Type = consts.ProxyTypeTCP
|
||||
pp.Proxies = append(pp.Proxies, &tcpProxy)
|
||||
}
|
||||
if pp.binder.UDP {
|
||||
udpProxy := proxy
|
||||
udpProxy.Name += "_udp"
|
||||
udpProxy.Type = consts.ProxyTypeUDP
|
||||
pp.Proxies = append(pp.Proxies, &udpProxy)
|
||||
}
|
||||
pp.Accept()
|
||||
}
|
@ -178,14 +178,18 @@ func (pp *PrefPage) changePassword() string {
|
||||
Password string
|
||||
}
|
||||
NewBasicDialog(nil, i18n.Sprintf("Master password"), loadResourceIcon(consts.IconKey, 32),
|
||||
DataBinder{AssignTo: &db, DataSource: &vm}, nil, Composite{
|
||||
DataBinder{
|
||||
AssignTo: &db,
|
||||
DataSource: &vm,
|
||||
ErrorPresenter: validators.SilentToolTipErrorPresenter{},
|
||||
}, nil, Composite{
|
||||
Layout: VBox{MarginsZero: true},
|
||||
MinSize: Size{Width: 280},
|
||||
Children: []Widget{
|
||||
Label{Text: i18n.SprintfColon("New master password")},
|
||||
LineEdit{AssignTo: &pwdEdit, Text: Bind("Password", consts.ValidateNonEmpty), PasswordMode: true},
|
||||
Label{Text: i18n.SprintfColon("Re-enter password")},
|
||||
LineEdit{Text: Bind("", validators.TextEqual{Target: &pwdEdit}), PasswordMode: true},
|
||||
LineEdit{Text: Bind("", validators.ConfirmPassword{Password: &pwdEdit}), PasswordMode: true},
|
||||
},
|
||||
}, VSpacer{}).Run(pp.Form())
|
||||
if vm.Password != "" {
|
||||
|
@ -22,6 +22,7 @@ type ProxyView struct {
|
||||
|
||||
// Actions
|
||||
newAction *walk.Action
|
||||
portAction *walk.Action
|
||||
rdAction *walk.Action
|
||||
sshAction *walk.Action
|
||||
webAction *walk.Action
|
||||
@ -30,7 +31,6 @@ type ProxyView struct {
|
||||
ftpAction *walk.Action
|
||||
httpFileAction *walk.Action
|
||||
httpProxyAction *walk.Action
|
||||
socks5Action *walk.Action
|
||||
vpnAction *walk.Action
|
||||
editAction *walk.Action
|
||||
deleteAction *walk.Action
|
||||
@ -106,6 +106,14 @@ func (pv *ProxyView) createToolbar() ToolBar {
|
||||
Text: i18n.Sprintf("Quick Add"),
|
||||
Image: loadSysIcon("imageres", consts.IconQuickAdd, 16),
|
||||
Items: []MenuItem{
|
||||
Action{
|
||||
AssignTo: &pv.portAction,
|
||||
Text: i18n.Sprintf("Open Port"),
|
||||
Image: loadSysIcon("shell32", consts.IconOpenPort, 16),
|
||||
OnTriggered: func() {
|
||||
pv.onQuickAdd(NewPortProxyDialog())
|
||||
},
|
||||
},
|
||||
Action{
|
||||
AssignTo: &pv.rdAction,
|
||||
Text: i18n.Sprintf("Remote Desktop"),
|
||||
@ -155,6 +163,15 @@ func (pv *ProxyView) createToolbar() ToolBar {
|
||||
"dns", []string{consts.ProxyTypeUDP}, systemDns+":53"))
|
||||
},
|
||||
},
|
||||
Action{
|
||||
AssignTo: &pv.vpnAction,
|
||||
Text: "OpenVPN",
|
||||
Image: loadSysIcon("shell32", consts.IconVpn, 16),
|
||||
OnTriggered: func() {
|
||||
pv.onQuickAdd(NewSimpleProxyDialog("OpenVPN", loadSysIcon("shell32", consts.IconVpn, 32),
|
||||
"openvpn", []string{consts.ProxyTypeTCP, consts.ProxyTypeUDP}, ":1194"))
|
||||
},
|
||||
},
|
||||
Action{
|
||||
AssignTo: &pv.ftpAction,
|
||||
Text: "FTP",
|
||||
@ -182,24 +199,6 @@ func (pv *ProxyView) createToolbar() ToolBar {
|
||||
consts.PluginHttpProxy))
|
||||
},
|
||||
},
|
||||
Action{
|
||||
AssignTo: &pv.socks5Action,
|
||||
Text: i18n.Sprintf("SOCKS5 Proxy"),
|
||||
Image: loadSysIcon("imageres", consts.IconSocks5, 16),
|
||||
OnTriggered: func() {
|
||||
pv.onQuickAdd(NewPluginProxyDialog(i18n.Sprintf("SOCKS5 Proxy"), loadSysIcon("imageres", consts.IconSocks5, 32),
|
||||
consts.PluginSocks5))
|
||||
},
|
||||
},
|
||||
Action{
|
||||
AssignTo: &pv.vpnAction,
|
||||
Text: "OpenVPN",
|
||||
Image: loadSysIcon("shell32", consts.IconVpn, 16),
|
||||
OnTriggered: func() {
|
||||
pv.onQuickAdd(NewSimpleProxyDialog("OpenVPN", loadSysIcon("shell32", consts.IconVpn, 32),
|
||||
"openvpn", []string{consts.ProxyTypeTCP, consts.ProxyTypeUDP}, ":1194"))
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Action{
|
||||
@ -271,16 +270,16 @@ func (pv *ProxyView) createProxyTable() TableView {
|
||||
Text: i18n.Sprintf("Quick Add"),
|
||||
Image: loadSysIcon("imageres", consts.IconQuickAdd, 16),
|
||||
Items: []MenuItem{
|
||||
ActionRef{&pv.portAction},
|
||||
ActionRef{&pv.rdAction},
|
||||
ActionRef{&pv.vncAction},
|
||||
ActionRef{&pv.sshAction},
|
||||
ActionRef{&pv.webAction},
|
||||
ActionRef{&pv.dnsAction},
|
||||
ActionRef{&pv.vpnAction},
|
||||
ActionRef{&pv.ftpAction},
|
||||
ActionRef{&pv.httpFileAction},
|
||||
ActionRef{&pv.httpProxyAction},
|
||||
ActionRef{&pv.socks5Action},
|
||||
ActionRef{&pv.vpnAction},
|
||||
},
|
||||
},
|
||||
Action{
|
||||
|
@ -13,6 +13,7 @@ type quickAddBinder struct {
|
||||
LocalPortMin string
|
||||
LocalPortMax string
|
||||
Dir string
|
||||
Plugin string
|
||||
}
|
||||
|
||||
// QuickAdd is the interface that must be implemented to build a quick-add dialog
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/koho/frpmgr/pkg/config"
|
||||
"github.com/koho/frpmgr/pkg/consts"
|
||||
"github.com/koho/frpmgr/pkg/util"
|
||||
"github.com/koho/frpmgr/pkg/validators"
|
||||
"github.com/lxn/walk"
|
||||
. "github.com/lxn/walk/declarative"
|
||||
)
|
||||
@ -45,26 +46,28 @@ func NewSimpleProxyDialog(title string, icon *walk.Icon, service string, types [
|
||||
func (sp *SimpleProxyDialog) Run(owner walk.Form) (int, error) {
|
||||
widgets := []Widget{
|
||||
Label{Text: i18n.SprintfColon("Remote Port"), ColumnSpan: 2},
|
||||
LineEdit{Text: Bind("RemotePort", consts.ValidateRequireInteger), ColumnSpan: 2},
|
||||
LineEdit{Text: Bind("RemotePort", consts.ValidatePortRange...), ColumnSpan: 2},
|
||||
Label{Text: i18n.SprintfColon("Local Address")},
|
||||
Label{Text: i18n.SprintfColon("Port")},
|
||||
LineEdit{Text: Bind("LocalAddr", consts.ValidateNonEmpty), StretchFactor: 2},
|
||||
LineEdit{Text: Bind("LocalPort", consts.ValidateRequireInteger), StretchFactor: 1},
|
||||
LineEdit{Text: Bind("LocalPort", consts.ValidatePortRange...), StretchFactor: 1},
|
||||
}
|
||||
switch sp.service {
|
||||
case "ftp":
|
||||
var lPortMinEdit *walk.LineEdit
|
||||
widgets = append(widgets, Label{Text: i18n.SprintfColon("Passive Port Range"), ColumnSpan: 2}, Composite{
|
||||
Layout: HBox{MarginsZero: true},
|
||||
Children: []Widget{
|
||||
LineEdit{Text: Bind("LocalPortMin", consts.ValidateRequireInteger)},
|
||||
LineEdit{AssignTo: &lPortMinEdit, Text: Bind("LocalPortMin", consts.ValidatePortRange...)},
|
||||
Label{Text: "-"},
|
||||
LineEdit{Text: Bind("LocalPortMax", consts.ValidateRequireInteger)},
|
||||
LineEdit{Text: Bind("LocalPortMax", append(consts.ValidatePortRange, validators.GTE{Value: &lPortMinEdit})...)},
|
||||
},
|
||||
})
|
||||
}
|
||||
return NewBasicDialog(&sp.Dialog, fmt.Sprintf("%s %s", i18n.Sprintf("Add"), sp.title), sp.icon, DataBinder{
|
||||
AssignTo: &sp.db,
|
||||
DataSource: sp.binder,
|
||||
AssignTo: &sp.db,
|
||||
DataSource: sp.binder,
|
||||
ErrorPresenter: validators.SilentToolTipErrorPresenter{},
|
||||
}, sp.onSave, Composite{
|
||||
Layout: Grid{Columns: 2, MarginsZero: true},
|
||||
MinSize: Size{Width: 280},
|
||||
|
5
ui/ui.go
5
ui/ui.go
@ -36,6 +36,11 @@ type View interface {
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
walk.SetTranslationFunc(func(source string, context ...string) string {
|
||||
translation := i18n.Sprintf(source)
|
||||
s1 := strings.ReplaceAll(translation, "%!f(MISSING)", "%.f")
|
||||
return strings.ReplaceAll(s1, "%!f(BADINDEX)", "%.f")
|
||||
})
|
||||
}
|
||||
|
||||
type FRPManager struct {
|
||||
|
Reference in New Issue
Block a user