feat: 添加公告系统(支持SQLite和PostgreSQL) (#129)

* feat(announcement): add database schema for SQLite and PostgreSQL

Add announcement table with id, title, content, enabled, created_at, updated_at columns to both SQLite and PostgreSQL schemas to support announcement system.

* feat(announcement): implement SQLite repository for announcement management

Add announcement CRUD operations in SQLite repository including create, read, update, delete, and list methods with proper error handling.

* feat(announcement): add HTTP handlers and auth middleware for announcement API

Implement admin-only update endpoint and public read endpoint for announcements. Add auth middleware to enforce admin-only access for update operations.

* feat(announcement): add frontend API client for announcement endpoints

Implement API client methods for fetching announcements and updating announcement settings with proper error handling.

* feat(announcement): add announcement display component to dashboard

Implement announcement display section in dashboard with real-time updates and proper styling using HeroUI components.

* feat(announcement): add announcement management UI to config page

Implement announcement settings panel with enable/disable toggle and content editor for admin users to manage announcements.
This commit is contained in:
sagit
2026-02-15 23:43:41 +08:00
committed by GitHub
parent 1b4500202a
commit 45d7970177
8 changed files with 276 additions and 10 deletions
@@ -177,6 +177,8 @@ func (h *Handler) Register(mux *http.ServeMux) {
mux.HandleFunc("/api/v1/federation/runtime/diagnose", h.authPeer(h.federationRuntimeDiagnose))
mux.HandleFunc("/api/v1/federation/runtime/command", h.authPeer(h.federationRuntimeCommand))
mux.HandleFunc("/api/v1/federation/node/import", h.nodeImport)
mux.HandleFunc("/api/v1/announcement/get", h.getAnnouncement)
mux.HandleFunc("/api/v1/announcement/update", h.updateAnnouncement)
mux.HandleFunc("/flow/test", h.flowTest)
mux.HandleFunc("/flow/config", h.flowConfig)
@@ -1228,3 +1230,53 @@ func (h *Handler) backupImport(w http.ResponseWriter, r *http.Request) {
result.AutoBackup = autoBackup
response.WriteJSON(w, response.OK(result))
}
func (h *Handler) getAnnouncement(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
response.WriteJSON(w, response.ErrDefault("请求失败"))
return
}
ann, err := h.repo.GetAnnouncement()
if err != nil {
response.WriteJSON(w, response.Err(-1, fmt.Sprintf("获取公告失败: %v", err)))
return
}
if ann == nil {
response.WriteJSON(w, response.OK(map[string]interface{}{
"content": "",
"enabled": 0,
}))
return
}
response.WriteJSON(w, response.OK(map[string]interface{}{
"content": ann.Content,
"enabled": ann.Enabled,
}))
}
func (h *Handler) updateAnnouncement(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
response.WriteJSON(w, response.ErrDefault("请求失败"))
return
}
var req struct {
Content string `json:"content"`
Enabled int `json:"enabled"`
}
if err := decodeJSON(r.Body, &req); err != nil {
response.WriteJSON(w, response.Err(500, "请求参数错误"))
return
}
now := time.Now().UnixMilli()
if err := h.repo.UpsertAnnouncement(req.Content, req.Enabled, now); err != nil {
response.WriteJSON(w, response.Err(-1, fmt.Sprintf("更新公告失败: %v", err)))
return
}
response.WriteJSON(w, response.OKEmpty())
}