160 lines
3.2 KiB
Go
160 lines
3.2 KiB
Go
package helpers
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var lanIPCache struct {
|
|
ip string
|
|
ts time.Time
|
|
}
|
|
|
|
func GetLanIP(preferIface string) string {
|
|
if lanIPCache.ip != "" && time.Since(lanIPCache.ts) < 30*time.Second {
|
|
return lanIPCache.ip
|
|
}
|
|
|
|
ip := "0.0.0.0"
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
lanIPCache.ip = ip
|
|
lanIPCache.ts = time.Now()
|
|
return ip
|
|
}
|
|
|
|
type candidate struct {
|
|
name string
|
|
address string
|
|
}
|
|
var candidates []candidate
|
|
|
|
for _, iface := range ifaces {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, addr := range addrs {
|
|
ipNet, ok := addr.(*net.IPNet)
|
|
if !ok {
|
|
continue
|
|
}
|
|
ip4 := ipNet.IP.To4()
|
|
if ip4 == nil {
|
|
continue
|
|
}
|
|
if ip4.IsLoopback() {
|
|
continue
|
|
}
|
|
ipStr := ip4.String()
|
|
if strings.HasPrefix(ipStr, "169.254.") {
|
|
continue
|
|
}
|
|
candidates = append(candidates, candidate{name: iface.Name, address: ipStr})
|
|
}
|
|
}
|
|
|
|
prefLower := strings.ToLower(preferIface)
|
|
for _, c := range candidates {
|
|
if strings.Contains(strings.ToLower(c.name), prefLower) {
|
|
ip = c.address
|
|
lanIPCache.ip = ip
|
|
lanIPCache.ts = time.Now()
|
|
return ip
|
|
}
|
|
}
|
|
|
|
for _, c := range candidates {
|
|
n := strings.ToLower(c.name)
|
|
if !strings.Contains(n, "vmware") && !strings.Contains(n, "virtual") && !strings.Contains(n, "vethernet") {
|
|
ip = c.address
|
|
lanIPCache.ip = ip
|
|
lanIPCache.ts = time.Now()
|
|
return ip
|
|
}
|
|
}
|
|
|
|
if len(candidates) > 0 {
|
|
ip = candidates[0].address
|
|
}
|
|
|
|
lanIPCache.ip = ip
|
|
lanIPCache.ts = time.Now()
|
|
return ip
|
|
}
|
|
|
|
func CheckPort(port int) bool {
|
|
conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), 1500*time.Millisecond)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
conn.Close()
|
|
return true
|
|
}
|
|
|
|
func WaitForReady(port int, timeoutMs int) bool {
|
|
if timeoutMs <= 0 {
|
|
timeoutMs = 30000
|
|
}
|
|
deadline := time.Now().Add(time.Duration(timeoutMs) * time.Millisecond)
|
|
for time.Now().Before(deadline) {
|
|
if CheckPort(port) {
|
|
return true
|
|
}
|
|
time.Sleep(1500 * time.Millisecond)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func MakeSlug(title string) string {
|
|
dateStr := time.Now().Format("2006-01-02")
|
|
slugPart := title
|
|
// Replace non-word, non-CJK chars with -
|
|
slugPart = strings.Map(func(r rune) rune {
|
|
if (r >= 0x4e00 && r <= 0x9fff) || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' {
|
|
return r
|
|
}
|
|
return '-'
|
|
}, slugPart)
|
|
slugPart = strings.Trim(slugPart, "-")
|
|
slugPart = strings.ToLower(slugPart)
|
|
if slugPart == "" {
|
|
slugPart = "post"
|
|
}
|
|
return dateStr + "-" + slugPart
|
|
}
|
|
|
|
func SessionSummary(title string, categories, tags []string, content string) string {
|
|
lines := 0
|
|
chars := len([]rune(content))
|
|
for _, ch := range content {
|
|
if ch == '\n' {
|
|
lines++
|
|
}
|
|
}
|
|
if content != "" {
|
|
lines++
|
|
}
|
|
|
|
displayTitle := title
|
|
if displayTitle == "" {
|
|
displayTitle = "未命名"
|
|
}
|
|
|
|
parts := []string{
|
|
fmt.Sprintf("📝 <b>%s</b>", displayTitle),
|
|
}
|
|
if len(categories) > 0 {
|
|
parts = append(parts, "📂 "+strings.Join(categories, "、"))
|
|
}
|
|
if len(tags) > 0 {
|
|
parts = append(parts, "🏷 "+strings.Join(tags, "、"))
|
|
}
|
|
if chars > 0 {
|
|
parts = append(parts, fmt.Sprintf("✍ %d 行 %d 字", lines, chars))
|
|
}
|
|
return strings.Join(parts, " · ")
|
|
}
|