mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-17 18:14:13 +00:00
feat(webhooks): paginate list + call-history endpoints (server + web UI) (#1268)
Add total-count pagination to the webhook admin endpoints and the web UI.
Store:
- WebhookStore/WebhookCallStore gain Count; WebhookListFilter gains
IncludeRevoked + Query (PG + SQLite, parameterized, tenant-scoped)
API:
- GET /v1/webhooks and GET /v1/webhooks/{id}/calls return
{items, total, limit, offset} with server-side search + revoked filtering
Web UI:
- server-driven list pager + search/revoked filter; call-history dialog uses
the real total (fixes the full-page "has more" boundary bug)
- i18n pager labels (en/vi/zh)
Tests: store pagination integration test; mock stores implement Count.
No schema migration (read-only COUNT).
This commit is contained in:
1 parent
2945d4db6c
commit
06ef06ca37
22 files changed
+472
-54
No files matched your search
@@ -238,6 +238,31 @@ func (s *SQLiteWebhookCallStore) List(ctx context.Context, f store.WebhookCallLi
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *SQLiteWebhookCallStore) Count(ctx context.Context, f store.WebhookCallListFilter) (int, error) {
|
||||
tid, err := requireTenantID(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
q := `SELECT COUNT(*) FROM webhook_calls WHERE tenant_id = ?`
|
||||
args := []any{tid}
|
||||
|
||||
if f.WebhookID != nil {
|
||||
q += ` AND webhook_id = ?`
|
||||
args = append(args, *f.WebhookID)
|
||||
}
|
||||
if f.Status != "" {
|
||||
q += ` AND status = ?`
|
||||
args = append(args, f.Status)
|
||||
}
|
||||
|
||||
var total int
|
||||
if err := s.db.QueryRowContext(ctx, q, args...).Scan(&total); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteWebhookCallStore) DeleteOlderThan(ctx context.Context, tenantID uuid.UUID, ts time.Time) (int64, error) {
|
||||
var res sql.Result
|
||||
var err error
|
||||
|
||||
@@ -154,6 +154,13 @@ func (s *SQLiteWebhookStore) List(ctx context.Context, f store.WebhookListFilter
|
||||
q += ` AND agent_id = ?`
|
||||
args = append(args, *f.AgentID)
|
||||
}
|
||||
if !f.IncludeRevoked {
|
||||
q += ` AND revoked = 0`
|
||||
}
|
||||
if f.Query != "" {
|
||||
q += ` AND (name LIKE ? COLLATE NOCASE OR secret_prefix LIKE ?)`
|
||||
args = append(args, "%"+f.Query+"%", f.Query+"%")
|
||||
}
|
||||
q += ` ORDER BY created_at DESC`
|
||||
|
||||
limit := f.Limit
|
||||
@@ -180,6 +187,34 @@ func (s *SQLiteWebhookStore) List(ctx context.Context, f store.WebhookListFilter
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *SQLiteWebhookStore) Count(ctx context.Context, f store.WebhookListFilter) (int, error) {
|
||||
tid, err := requireTenantID(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
q := `SELECT COUNT(*) FROM webhooks WHERE tenant_id = ?`
|
||||
args := []any{tid}
|
||||
|
||||
if f.AgentID != nil {
|
||||
q += ` AND agent_id = ?`
|
||||
args = append(args, *f.AgentID)
|
||||
}
|
||||
if !f.IncludeRevoked {
|
||||
q += ` AND revoked = 0`
|
||||
}
|
||||
if f.Query != "" {
|
||||
q += ` AND (name LIKE ? COLLATE NOCASE OR secret_prefix LIKE ?)`
|
||||
args = append(args, "%"+f.Query+"%", f.Query+"%")
|
||||
}
|
||||
|
||||
var total int
|
||||
if err := s.db.QueryRowContext(ctx, q, args...).Scan(&total); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteWebhookStore) Update(ctx context.Context, id uuid.UUID, updates map[string]any) error {
|
||||
tid, err := requireTenantID(ctx)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user