feat: rename project from Fonu to Havline
CI / verify (push) Canceled after 0s

This commit is contained in:
2026-10-01 16:54:35 +08:00
parent c377405696
commit 8fa5d922ac
250 changed files with 6724 additions and 5070 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ import (
)
func formatFooter(at time.Time) string {
return "Fonu · " + at.Format("2006-01-02 15:04:05")
return "Havline · " + at.Format("2006-01-02 15:04:05")
}
type eventMeta struct {
@@ -35,7 +35,7 @@ func FormatAlertAt(event, title, message string, at time.Time) AlertContent {
title = strings.TrimSpace(title)
message = strings.TrimSpace(message)
pushTitle := strings.TrimSpace(meta.icon + " " + title)
subject := "[Fonu] " + pushTitle
subject := "[Havline] " + pushTitle
footer := formatFooter(at)
detail := message
+2 -2
View File
@@ -10,13 +10,13 @@ func TestFormatAlert(t *testing.T) {
at := time.Date(2026, 9, 18, 15, 49, 0, 0, time.FixedZone("CST", 8*3600))
content := FormatAlertAt(EventDDNSIPChange, "DDNS IP 已变更", "example.com\nIPv4: 1.2.3.4 → 5.6.7.8", at)
if content.Subject != "[Fonu] 🌐 DDNS IP 已变更" {
if content.Subject != "[Havline] 🌐 DDNS IP 已变更" {
t.Fatalf("unexpected subject: %s", content.Subject)
}
if !strings.Contains(content.PlainBody, "example.com") {
t.Fatalf("missing detail: %s", content.PlainBody)
}
if !strings.Contains(content.PlainBody, "Fonu · 2026-09-18 15:49:00") {
if !strings.Contains(content.PlainBody, "Havline · 2026-09-18 15:49:00") {
t.Fatalf("missing footer: %s", content.PlainBody)
}
if !strings.Contains(content.HTMLBody, "<b>") || !strings.Contains(content.HTMLBody, "<pre>") {
+2 -2
View File
@@ -114,7 +114,7 @@ func sendWecom(ctx context.Context, client *http.Client, cfg RuntimeConfig, titl
}
// 钉钉群机器人:POST /robot/send?access_token=<key>。
// 正文固定带 “Fonu · ” 前缀,机器人用「自定义关键词 = Fonu」的安全设置即可命中。
// 正文固定带 “Havline · ” 前缀,机器人用「自定义关键词 = Havline」的安全设置即可命中。
func sendDingtalk(ctx context.Context, client *http.Client, cfg RuntimeConfig, title, message string) error {
key := webhookSecret(cfg)
if key == "" {
@@ -124,7 +124,7 @@ func sendDingtalk(ctx context.Context, client *http.Client, cfg RuntimeConfig, t
if server == "" {
server = DefaultDingtalkServer
}
titled := "Fonu · " + title
titled := "Havline · " + title
endpoint := joinURL(server, "robot", "send") + "?access_token=" + url.QueryEscape(key)
return postJSON(ctx, client, endpoint, map[string]any{
"msgtype": "markdown",
+2 -2
View File
@@ -120,8 +120,8 @@ func TestSendDingtalkUsesAccessTokenAndKeyword(t *testing.T) {
if path != "/robot/send" || query != "access_token=tok" {
t.Fatalf("unexpected endpoint: %s?%s", path, query)
}
// 「自定义关键词」安全设置靠正文里的 Fonu 命中(PushTitle 前面还有事件图标)
if !strings.Contains(body, "Fonu · ") || !strings.Contains(body, "标题") {
// 「自定义关键词」安全设置靠正文里的 Havline 命中(PushTitle 前面还有事件图标)
if !strings.Contains(body, "Havline · ") || !strings.Contains(body, "标题") {
t.Fatalf("missing keyword prefix: %s", body)
}
}
+2 -2
View File
@@ -9,8 +9,8 @@ import (
"sync"
"time"
"github.com/fonu/fonu/internal/secret"
"github.com/fonu/fonu/internal/settings"
"github.com/havline/havline/internal/secret"
"github.com/havline/havline/internal/settings"
)
type Service struct {
+1 -1
View File
@@ -8,7 +8,7 @@ import (
func TestBuildMailBody(t *testing.T) {
content := FormatAlert("test", "标题", "正文")
body := string(buildMailBody("from@example.com", []string{"to@example.com"}, content.Subject, content.PlainBody))
if !strings.Contains(body, "Subject: [Fonu]") {
if !strings.Contains(body, "Subject: [Havline]") {
t.Fatalf("missing subject: %s", body)
}
if !strings.Contains(body, "正文") {
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"strconv"
"strings"
"github.com/fonu/fonu/internal/secret"
"github.com/fonu/fonu/internal/settings"
"github.com/havline/havline/internal/secret"
"github.com/havline/havline/internal/settings"
)
type Store struct {
+5 -5
View File
@@ -5,14 +5,14 @@ import (
"path/filepath"
"testing"
"github.com/fonu/fonu/internal/db"
"github.com/fonu/fonu/internal/secret"
"github.com/fonu/fonu/internal/settings"
"github.com/havline/havline/internal/db"
"github.com/havline/havline/internal/secret"
"github.com/havline/havline/internal/settings"
)
func TestStoreSaveTelegramPersistsType(t *testing.T) {
dir := t.TempDir()
conn, err := db.Open(filepath.Join(dir, "fonu.db"))
conn, err := db.Open(filepath.Join(dir, "havline.db"))
if err != nil {
t.Fatal(err)
}
@@ -57,7 +57,7 @@ func TestStoreSaveTelegramPersistsType(t *testing.T) {
func TestStoreMigratesLegacyWebhookURL(t *testing.T) {
dir := t.TempDir()
conn, err := db.Open(filepath.Join(dir, "fonu.db"))
conn, err := db.Open(filepath.Join(dir, "havline.db"))
if err != nil {
t.Fatal(err)
}