评论实时更新:SSE 订阅文章频道,发评/编辑/删除/审核通过即时刷新打开中的页面

- internal/hub:按文章 ID 的进程内发布/订阅,信号不携带内容(未审核评论不外泄)
- GET /api/comments/stream?post_id=N:SSE 长连 + 25s 心跳 + X-Accel-Buffering off
- 广播点:发评论、作者编辑/删除、后台审核通过、后台删除(与公开端共享同一 hub)
- 前端 EventSource 静默刷新(不亮加载态、不干扰正在输入/编辑的状态)
- requestLog 的 statusRecorder 补 Flush 透传——包装器没实现 Flusher 时流式端点整体不可用
This commit is contained in:
Sakurasan
2026-09-28 21:42:11 +08:00
parent 661d157b6c
commit a234afe448
8 changed files with 168 additions and 2 deletions
+13
View File
@@ -21,6 +21,7 @@ import (
"oneblog/internal/auth"
"oneblog/internal/config"
"oneblog/internal/db"
"oneblog/internal/hub"
"oneblog/internal/storage"
"oneblog/internal/store"
"oneblog/internal/thumbs"
@@ -66,6 +67,8 @@ func main() {
readerSessions := auth.NewReaderSessions(cfg.SessionSec, 30*24*time.Hour)
// 后台管理员会话(cookie one_session),前台评论也用它识别站主身份
adminSessions := admin.NewSessions(cfg.SessionSec, 7*24*time.Hour)
// 评论变更广播(SSE):公开端订阅、双端发布,共用同一实例
commentHub := hub.New()
// 缩略图懒生成 + 磁盘缓存(首次请求生成一次,之后直接供缓存)
thumbCache := thumbs.NewStore(filepath.Join(cfg.DataDir, ".thumbnail_cache"))
@@ -79,8 +82,10 @@ func main() {
GG: auth.Google{ClientID: cfg.GoogleClientID, ClientSecret: cfg.GoogleClientSecret},
TG: auth.Telegram{Bot: cfg.TelegramBot, Token: cfg.TelegramToken},
AdminSessions: adminSessions,
Hub: commentHub,
}
adminAPI := admin.NewAPI(st, cfg, adminSessions)
adminAPI.Hub = commentHub
adminAPI.Blobs = blobs
adminAPI.Thumbs = thumbCache // 删文件时连带清掉它的缩略图
@@ -187,6 +192,14 @@ type statusRecorder struct {
func (s *statusRecorder) WriteHeader(code int) { s.status = code; s.ResponseWriter.WriteHeader(code) }
// Flush 透传给底层 writer——SSE 这类流式响应靠它判定可刷新,
// 包装器不实现 Flusher 的话流式端点会直接不可用。
func (s *statusRecorder) Flush() {
if f, ok := s.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
}
func placeholderPage() string {
return `<!doctype html><meta charset="utf-8"><title>ONE</title>
<style>body{font-family:-apple-system,"PingFang SC",sans-serif;background:#faf7f1;color:#33302b;