feat: 实现管理后台 A3 注册群消息概览与 metrics 门禁
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.asio.asia/nixevol/NixMsg/internal/httpx"
|
||||
)
|
||||
|
||||
func (h *Handler) handleOverview(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var total, selfCount, disabled, online int
|
||||
err := h.db.Read.QueryRowContext(ctx, `
|
||||
SELECT
|
||||
COUNT(*),
|
||||
COALESCE(SUM(CASE WHEN source = 'self' THEN 1 ELSE 0 END), 0),
|
||||
COALESCE(SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END), 0),
|
||||
COALESCE(SUM(CASE
|
||||
WHEN online_since IS NOT NULL
|
||||
AND (offline_since IS NULL OR online_since > offline_since) THEN 1
|
||||
ELSE 0 END), 0)
|
||||
FROM endpoints`).Scan(&total, &selfCount, &disabled, &online)
|
||||
if err != nil {
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
var groupsTotal int
|
||||
if err := h.db.Read.QueryRowContext(ctx, `SELECT COUNT(*) FROM groups`).Scan(&groupsTotal); err != nil {
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
var pending int
|
||||
if err := h.db.Read.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM deliveries WHERE state = 'pending'`).Scan(&pending); err != nil {
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
var scheduled int
|
||||
if err := h.db.Read.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM messages WHERE state = 'scheduled'`).Scan(&scheduled); err != nil {
|
||||
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
|
||||
return
|
||||
}
|
||||
|
||||
httpx.WriteOK(w, map[string]any{
|
||||
"version": h.version,
|
||||
"endpoints_total": total,
|
||||
"endpoints_self": selfCount,
|
||||
"endpoints_online": online,
|
||||
"endpoints_disabled": disabled,
|
||||
"groups_total": groupsTotal,
|
||||
"messages_pending": pending,
|
||||
"messages_scheduled": scheduled,
|
||||
"uptime_ms": time.Since(h.startedAt).Milliseconds(),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) handleSettingsGet(w http.ResponseWriter, _ *http.Request) {
|
||||
cfg := h.cfg
|
||||
httpx.WriteOK(w, map[string]any{
|
||||
"listen": cfg.Listen,
|
||||
"admin_listen": cfg.AdminListen,
|
||||
"session_idle_days": cfg.SessionIdleDays,
|
||||
"record_retention_days": cfg.RecordRetentionDays,
|
||||
"idempotency_hours": cfg.IdempotencyHours,
|
||||
"receipt_retention_days": cfg.ReceiptRetentionDays,
|
||||
"sqlite_synchronous": cfg.SQLiteSynchronous,
|
||||
"limits": map[string]any{
|
||||
"max_body_bytes": cfg.Limits.MaxBodyBytes,
|
||||
"max_meta_bytes": cfg.Limits.MaxMetaBytes,
|
||||
"max_frame_bytes": cfg.Limits.MaxFrameBytes,
|
||||
"max_ttl_seconds": cfg.Limits.MaxTTLSeconds,
|
||||
"max_schedule_seconds": cfg.Limits.MaxScheduleSeconds,
|
||||
"max_group_members": cfg.Limits.MaxGroupMembers,
|
||||
"grace_seconds": cfg.Limits.GraceSeconds,
|
||||
"ack_timeout_seconds": cfg.Limits.AckTimeoutSeconds,
|
||||
"delivery_window": cfg.Limits.DeliveryWindow,
|
||||
"receipt_window": cfg.Limits.ReceiptWindow,
|
||||
"requests_per_second": cfg.Limits.RequestsPerSecond,
|
||||
"max_pending_per_sender": cfg.Limits.MaxPendingPerSender,
|
||||
"max_pending_per_receiver": cfg.Limits.MaxPendingPerReceiver,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func nullInt64API(v sql.NullInt64) any {
|
||||
if !v.Valid {
|
||||
return nil
|
||||
}
|
||||
return v.Int64
|
||||
}
|
||||
|
||||
func parseInt64Query(s string) (int64, bool) {
|
||||
if s == "" {
|
||||
return 0, false
|
||||
}
|
||||
n, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
func isNoRows(err error) bool {
|
||||
return errors.Is(err, sql.ErrNoRows)
|
||||
}
|
||||
Reference in New Issue
Block a user