feat: 实现端停用与删除的消息作废与群主转让级联

This commit is contained in:
Nixevol
2026-09-30 08:10:52 +08:00
parent b8569b0708
commit 19a8ec95fa
8 changed files with 1143 additions and 16 deletions
+29 -8
View File
@@ -320,18 +320,39 @@ func (h *Handler) handleEndpointPatch(w http.ResponseWriter, r *http.Request) {
return
}
wasEnabled, err := h.patchEndpoint(r.Context(), id, req.Name, req.Remark, req.DefaultDelaySeconds, req.Enabled)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
hasMeta := req.Name != nil || req.Remark != nil || req.DefaultDelaySeconds != nil
var wasEnabled bool
var err error
if hasMeta || (req.Enabled != nil && h.identity == nil) {
wasEnabled, err = h.patchEndpoint(r.Context(), id, req.Name, req.Remark, req.DefaultDelaySeconds, req.Enabled)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
h.audit(actorString(p), "endpoint_patch", id, "not_found", ip)
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
return
}
h.audit(actorString(p), "endpoint_patch", id, "error", ip)
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
return
}
}
if req.Enabled != nil && h.identity != nil {
var found bool
found, err = h.setEndpointEnabled(r.Context(), id, *req.Enabled)
if err != nil {
h.audit(actorString(p), "endpoint_patch", id, "error", ip)
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
return
}
if !found {
h.audit(actorString(p), "endpoint_patch", id, "not_found", ip)
httpx.WriteError(w, http.StatusNotFound, "not_found", "端不存在")
return
}
h.audit(actorString(p), "endpoint_patch", id, "error", ip)
httpx.WriteError(w, http.StatusInternalServerError, "internal", "内部错误")
return
}
if req.Enabled != nil && !*req.Enabled && wasEnabled {
if !*req.Enabled {
_, _ = h.kickEndpoint(r.Context(), id)
}
} else if req.Enabled != nil && !*req.Enabled && wasEnabled {
_, _ = h.kickEndpoint(r.Context(), id)
}
row, err := h.getEndpoint(r.Context(), id)
+31 -2
View File
@@ -308,6 +308,21 @@ func (h *Handler) patchEndpoint(ctx context.Context, id string, name, remark *st
}
func (h *Handler) setEndpointEnabled(ctx context.Context, id string, enabled bool) (found bool, err error) {
if h.identity != nil {
var opErr error
if enabled {
opErr = h.identity.Enable(ctx, id)
} else {
opErr = h.identity.Disable(ctx, id)
}
if opErr != nil {
if isEndpointNotFound(opErr) {
return false, nil
}
return false, opErr
}
return true, nil
}
err = h.db.Queue.Do(ctx, func(tx *sql.Tx) error {
v := 0
if enabled {
@@ -331,9 +346,18 @@ func (h *Handler) setEndpointEnabled(ctx context.Context, id string, enabled boo
return found, err
}
// deleteEndpointBasic 删除端行并清掉与之相关的 talk_grants。
// 作废消息、群主转让等完整级联留给 I5。
// deleteEndpointBasic 删除端;注入 Identity 时走 I5 完整级联。
func (h *Handler) deleteEndpointBasic(ctx context.Context, id string) (found bool, err error) {
if h.identity != nil {
opErr := h.identity.Delete(ctx, id)
if opErr != nil {
if isEndpointNotFound(opErr) {
return false, nil
}
return false, opErr
}
return true, nil
}
err = h.db.Queue.Do(ctx, func(tx *sql.Tx) error {
if _, e := tx.ExecContext(ctx, `DELETE FROM talk_grants WHERE sender_id = ? OR target_id = ?`, id, id); e != nil {
return e
@@ -349,6 +373,11 @@ func (h *Handler) deleteEndpointBasic(ctx context.Context, id string) (found boo
return found, err
}
func isEndpointNotFound(err error) bool {
var pe *protocol.Error
return errors.As(err, &pe) && pe.Code == protocol.CodeNotFound
}
func (h *Handler) resetLoginPassword(ctx context.Context, id, loginHash string) (bool, error) {
var found bool
err := h.db.Queue.Do(ctx, func(tx *sql.Tx) error {
+5
View File
@@ -7,6 +7,7 @@ import (
"sync"
"time"
"git.asio.asia/nixevol/NixMsg/internal/app/identity"
"git.asio.asia/nixevol/NixMsg/internal/auth"
"git.asio.asia/nixevol/NixMsg/internal/store"
)
@@ -38,6 +39,8 @@ type Deps struct {
SecureCookies bool
// KickEndpoint 踢下线钩子(只断开连接);nil 时踢线为 no-op。
KickEndpoint EndpointKickFunc
// Identity 端停用/启用/删除级联(I5);nil 时回退为仅改 enabled/删行。
Identity identity.Service
}
// Handler 是可挂载的管理接口(路由前缀 /api/admin/)。
@@ -51,6 +54,7 @@ type Handler struct {
ttl time.Duration
forceSec bool
kick EndpointKickFunc
identity identity.Service
mux *http.ServeMux
@@ -80,6 +84,7 @@ func New(d Deps) *Handler {
ttl: ttl,
forceSec: d.SecureCookies,
kick: d.KickEndpoint,
identity: d.Identity,
mux: http.NewServeMux(),
lastUsed: make(map[string]time.Time),
}