添加新功能,完善项目

This commit is contained in:
truewhile
2026-09-22 16:22:29 +08:00
parent 47899284a5
commit b5ce4646cf
85 changed files with 7103 additions and 982 deletions
+26 -2
View File
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"html"
"strings"
"time"
@@ -34,6 +35,10 @@ type DeviceService struct {
// notifyUser sends a Telegram message to the local user (resolved to their
// Telegram binding). Wired by the bot service; nil disables notifications.
notifyUser func(ctx context.Context, userID, text string)
// notifyAdmin sends a Telegram message to all admin accounts.
// Wired by the bot service; nil disables notifications.
notifyAdmin func(ctx context.Context, text string)
}
// NewDeviceService constructs a DeviceService.
@@ -46,6 +51,11 @@ func (s *DeviceService) SetNotifier(fn func(ctx context.Context, userID, text st
s.notifyUser = fn
}
// SetAdminNotifier wires the admin-broadcast Telegram notification callback.
func (s *DeviceService) SetAdminNotifier(fn func(ctx context.Context, text string)) {
s.notifyAdmin = fn
}
func (s *DeviceService) SetSessionTracker(tracker *SessionTrackerService) {
s.sessions = tracker
}
@@ -102,6 +112,8 @@ func (s *DeviceService) RecordLogin(ctx context.Context, userID, deviceID, devic
FirstSeenAt: now,
LastSeenAt: now,
})
// 新终端首次登录才通知:已知设备重复登录不刷屏。
s.notify(ctx, userID, fmt.Sprintf("🔔 新设备登录:<b>%s</b>\n如果这不是你本人,请到「个人资料 → 我的设备」踢下线并修改密码。", deviceLabel(deviceName, client)))
} else {
if existing.Fingerprint != "" && existing.Fingerprint != fp {
mismatch = true
@@ -236,7 +248,10 @@ func (s *DeviceService) registerFingerprintWarning(ctx context.Context, userID,
"last_share_warn_at": &now,
})
left := cfg.WarnThreshold + 1 - warnings
s.notify(ctx, userID, fmt.Sprintf("⚠️ 账号 <b>%s</b> 触发设备指纹警告:%s\n这是第 <b>%d</b> 次警告,再异常 <b>%d</b> 次将禁用账号。请使用 Bot 的「我的设备」踢下线异常设备。", u.Username, reason, warnings, left))
warnText := fmt.Sprintf("⚠️ 账号 <b>%s</b> 触发设备指纹警告:%s\n这是第 <b>%d</b> 次警告,再异常 <b>%d</b> 次将禁用账号。请到「个人资料 → 我的设备」踢下线异常设备。",
html.EscapeString(u.Username), html.EscapeString(reason), warnings, left)
s.notify(ctx, userID, warnText)
s.notifyAdminMsg(ctx, warnText)
s.log.Info("anti-share: warning issued", zap.String("user", u.Username), zap.Int("warnings", warnings), zap.String("reason", reason))
}
@@ -255,7 +270,10 @@ func (s *DeviceService) disableForPolicy(ctx context.Context, userID, reason str
"last_share_warn_at": &now,
})
_ = s.repo.UserDevice.SetKickedByUser(ctx, userID, true)
s.notify(ctx, userID, fmt.Sprintf("⛔️ 账号 <b>%s</b> 因触发设备规则已被禁用:%s\n请联系管理员解除禁用,或通过「我的设备」踢下线多余设备后再申请恢复。", u.Username, reason))
disableText := fmt.Sprintf("⛔️ 账号 <b>%s</b> 因触发设备规则已被禁用:%s\n请联系管理员解除禁用,或通过「个人资料 → 我的设备」踢下线多余设备后再申请恢复。",
html.EscapeString(u.Username), html.EscapeString(reason))
s.notify(ctx, userID, disableText)
s.notifyAdminMsg(ctx, disableText)
s.log.Warn("device policy: disabled account", zap.String("user", u.Username), zap.String("reason", reason))
}
@@ -276,6 +294,12 @@ func (s *DeviceService) notify(ctx context.Context, userID, text string) {
}
}
func (s *DeviceService) notifyAdminMsg(ctx context.Context, text string) {
if s.notifyAdmin != nil {
s.notifyAdmin(ctx, text)
}
}
func deviceLabel(name, client string) string {
name = strings.TrimSpace(name)
client = strings.TrimSpace(client)