chore: 初始提交 Fonu 1.0.2
- 公网反代:Nginx 状态卡片(版本 / 运行状态 / 配置校验 / 端口监听) - 部署设置第一、二梯队(HTTPS 开关、安全头、限流、连接数、仅中国大陆 IP) - 隧道检测兼容 frps v0.68 / v0.71(V2 与旧版端点) - 恢复被清空的 internal/frp/store.go、internal/api/ddns_handler.go、web/src/views/DdnsView.vue
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var domainRe = regexp.MustCompile(`^(?i:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?i:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$`)
|
||||
|
||||
func CertDomain(domain string) error {
|
||||
domain = strings.TrimSuffix(strings.ToLower(strings.TrimSpace(domain)), ".")
|
||||
if domain == "" {
|
||||
return fmt.Errorf("域名不能为空")
|
||||
}
|
||||
if strings.HasPrefix(domain, "*.") {
|
||||
return Domain(strings.TrimPrefix(domain, "*."))
|
||||
}
|
||||
return Domain(domain)
|
||||
}
|
||||
|
||||
func Domain(domain string) error {
|
||||
domain = strings.TrimSpace(domain)
|
||||
if domain == "" {
|
||||
return fmt.Errorf("域名不能为空")
|
||||
}
|
||||
if len(domain) > 253 {
|
||||
return fmt.Errorf("域名过长")
|
||||
}
|
||||
if strings.Contains(domain, "..") || strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") {
|
||||
return fmt.Errorf("域名格式无效")
|
||||
}
|
||||
if !domainRe.MatchString(domain) {
|
||||
return fmt.Errorf("域名格式无效")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListenPort(port int) error {
|
||||
if port < 1 || port > 65535 {
|
||||
return fmt.Errorf("监听端口无效")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FrontendAddress parses a frontend binding like "example.com" or "example.com:8011".
|
||||
// A zero port means the rule-level default should be used.
|
||||
func FrontendAddress(raw string) (hostname string, port int, err error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return "", 0, fmt.Errorf("前端地址不能为空")
|
||||
}
|
||||
|
||||
host := raw
|
||||
port = 0
|
||||
if strings.Count(raw, ":") == 1 && !strings.Contains(raw, "]") {
|
||||
parts := strings.SplitN(raw, ":", 2)
|
||||
host = parts[0]
|
||||
p, parseErr := strconv.Atoi(parts[1])
|
||||
if parseErr != nil || p < 1 || p > 65535 {
|
||||
return "", 0, fmt.Errorf("前端地址端口无效")
|
||||
}
|
||||
port = p
|
||||
}
|
||||
|
||||
host = strings.ToLower(strings.TrimSpace(host))
|
||||
if err := Domain(host); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
func Upstream(raw string) (string, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return "", fmt.Errorf("目标地址不能为空")
|
||||
}
|
||||
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("目标地址格式无效")
|
||||
}
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return "", fmt.Errorf("目标地址仅支持 http:// 或 https://")
|
||||
}
|
||||
if u.User != nil {
|
||||
return "", fmt.Errorf("目标地址不能包含用户名或密码")
|
||||
}
|
||||
if u.Host == "" {
|
||||
return "", fmt.Errorf("目标地址缺少主机名")
|
||||
}
|
||||
if u.Path != "" && u.Path != "/" {
|
||||
return "", fmt.Errorf("目标地址不能包含路径")
|
||||
}
|
||||
if u.RawQuery != "" || u.Fragment != "" {
|
||||
return "", fmt.Errorf("目标地址不能包含查询参数或片段")
|
||||
}
|
||||
|
||||
host := u.Hostname()
|
||||
port := u.Port()
|
||||
if host == "" {
|
||||
return "", fmt.Errorf("目标地址主机名无效")
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if port == "" {
|
||||
return "", fmt.Errorf("IP 地址目标必须指定端口")
|
||||
}
|
||||
} else if err := Domain(host); err != nil {
|
||||
return "", fmt.Errorf("目标地址主机名无效")
|
||||
}
|
||||
|
||||
normalized := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
|
||||
if !strings.HasPrefix(normalized, "http://") && !strings.HasPrefix(normalized, "https://") {
|
||||
return "", fmt.Errorf("目标地址格式无效")
|
||||
}
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
func Password(password string) error {
|
||||
if len(password) < 8 {
|
||||
return fmt.Errorf("密码长度至少 8 位")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Username(username string) error {
|
||||
username = strings.TrimSpace(username)
|
||||
if username == "" {
|
||||
return fmt.Errorf("用户名不能为空")
|
||||
}
|
||||
if len(username) > 64 {
|
||||
return fmt.Errorf("用户名过长")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user