From bcda6f6fe64feca23d01490650f4ed9af41160f4 Mon Sep 17 00:00:00 2001
From: Acbox
Date: Sun, 29 Mar 2026 18:49:30 +0800
Subject: [PATCH] refactor: replace Load More with Pagination across frontend
and backend
- Replace all "Load More" / "Show More" buttons with Pagination components
in model-list, bot-compaction, and bot-heartbeat views
- Convert backend log APIs (compaction, heartbeat, schedule) from
cursor-based (before+limit) to offset+limit pagination with total_count
- Update SQL queries to use OFFSET+LIMIT and add COUNT queries
- Add shared parseOffsetLimit helper in handler_helpers.go
- Regenerate sqlc, Swagger docs, and TypeScript SDK
- Clean up unused i18n keys (loadMore, showMore, history.loadMore)
---
apps/web/src/i18n/locales/en.json | 6 +-
apps/web/src/i18n/locales/zh.json | 6 +-
.../pages/bots/components/bot-compaction.vue | 91 ++++++++++++-------
.../pages/bots/components/bot-heartbeat.vue | 91 ++++++++++++-------
.../pages/providers/components/model-list.vue | 66 ++++++++++----
db/queries/compaction_logs.sql | 6 +-
db/queries/heartbeat_logs.sql | 6 +-
db/queries/schedule_logs.sql | 12 ++-
internal/command/heartbeat_cmd.go | 2 +-
internal/compaction/service.go | 30 +++---
internal/compaction/types.go | 3 +-
internal/db/sqlc/compaction_logs.sql.go | 22 +++--
internal/db/sqlc/heartbeat_logs.sql.go | 22 +++--
internal/db/sqlc/schedule_logs.sql.go | 44 ++++++---
internal/handlers/compaction.go | 24 +----
internal/handlers/handler_helpers.go | 18 ++++
internal/handlers/heartbeat.go | 24 +----
internal/handlers/schedule.go | 34 ++-----
internal/heartbeat/service.go | 25 +++--
internal/heartbeat/types.go | 3 +-
internal/schedule/service.go | 48 ++++++----
internal/schedule/types.go | 3 +-
packages/sdk/src/types.gen.ts | 35 +++----
spec/docs.go | 61 ++++++++-----
spec/swagger.json | 61 ++++++++-----
spec/swagger.yaml | 42 +++++----
26 files changed, 469 insertions(+), 316 deletions(-)
diff --git a/apps/web/src/i18n/locales/en.json b/apps/web/src/i18n/locales/en.json
index f34cea54..6e760b47 100644
--- a/apps/web/src/i18n/locales/en.json
+++ b/apps/web/src/i18n/locales/en.json
@@ -218,8 +218,7 @@
"importSuccess": "Successfully imported {created} models, skipped {skipped}",
"importFailed": "Failed to import models",
"importConfirmHint": "Models will be imported from this provider's API with default compatibilities (vision, tool-call, reasoning).",
- "showingCount": "Showing {count} of {total}",
- "showMore": "Show More"
+ "showingCount": "Showing {count} of {total}"
},
"provider": {
"add": "Add Provider",
@@ -1019,7 +1018,6 @@
"title": "Compaction Logs",
"loadFailed": "Failed to load compaction logs",
"empty": "No compaction logs",
- "loadMore": "Load More",
"clearLogs": "Clear Logs",
"clearConfirm": "Are you sure you want to clear all compaction logs? This cannot be undone.",
"clearSuccess": "Compaction logs cleared",
@@ -1037,7 +1035,6 @@
"title": "Heartbeat Logs",
"loadFailed": "Failed to load heartbeat logs",
"empty": "No heartbeat logs",
- "loadMore": "Load More",
"clearLogs": "Clear Logs",
"clearConfirm": "Are you sure you want to clear all heartbeat logs? This cannot be undone.",
"clearSuccess": "Heartbeat logs cleared",
@@ -1071,7 +1068,6 @@
"title": "History",
"loadFailed": "Failed to load history",
"empty": "No history messages",
- "loadMore": "Load More",
"messageCount": "{count} messages",
"selectAll": "Select all",
"deleteSelectedConfirm": "Are you sure you want to delete the selected {count} message(s)? This will clear all history and cannot be undone.",
diff --git a/apps/web/src/i18n/locales/zh.json b/apps/web/src/i18n/locales/zh.json
index f8b931e1..87e8c3c7 100644
--- a/apps/web/src/i18n/locales/zh.json
+++ b/apps/web/src/i18n/locales/zh.json
@@ -214,8 +214,7 @@
"importSuccess": "成功导入 {created} 个模型,跳过 {skipped} 个",
"importFailed": "导入模型失败",
"importConfirmHint": "将从该服务商的 API 导入模型,默认兼容性为视觉、工具调用和推理。",
- "showingCount": "显示 {count} / {total}",
- "showMore": "加载更多"
+ "showingCount": "显示 {count} / {total}"
},
"provider": {
"add": "添加服务商",
@@ -1015,7 +1014,6 @@
"title": "压缩记录",
"loadFailed": "加载压缩记录失败",
"empty": "暂无压缩记录",
- "loadMore": "加载更多",
"clearLogs": "清空记录",
"clearConfirm": "确定要清空所有压缩记录吗?此操作不可撤销。",
"clearSuccess": "压缩记录已清空",
@@ -1033,7 +1031,6 @@
"title": "心跳日志",
"loadFailed": "加载心跳日志失败",
"empty": "暂无心跳日志",
- "loadMore": "加载更多",
"clearLogs": "清空日志",
"clearConfirm": "确定要清空所有心跳日志吗?此操作不可撤销。",
"clearSuccess": "心跳日志已清空",
@@ -1067,7 +1064,6 @@
"title": "对话历史",
"loadFailed": "加载历史消息失败",
"empty": "暂无历史消息",
- "loadMore": "加载更多",
"messageCount": "{count} 条消息",
"selectAll": "全选",
"deleteSelectedConfirm": "确定要删除选中的 {count} 条消息吗?此操作将清空所有历史记录,且无法撤销。",
diff --git a/apps/web/src/pages/bots/components/bot-compaction.vue b/apps/web/src/pages/bots/components/bot-compaction.vue
index 0e8227d3..21e05ea2 100644
--- a/apps/web/src/pages/bots/components/bot-compaction.vue
+++ b/apps/web/src/pages/bots/components/bot-compaction.vue
@@ -207,23 +207,42 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
@@ -236,6 +255,9 @@ import { useI18n } from 'vue-i18n'
import { toast } from 'vue-sonner'
import {
Button, Badge, Spinner, NativeSelect, Label, Switch, Input, Separator,
+ Pagination, PaginationContent, PaginationEllipsis,
+ PaginationFirst, PaginationItem, PaginationLast,
+ PaginationNext, PaginationPrevious,
} from '@memohai/ui'
import ConfirmPopover from '@/components/confirm-popover/index.vue'
import ModelSelect from './model-select.vue'
@@ -335,17 +357,32 @@ async function handleSaveSettings() {
const isLoading = ref(false)
const isClearing = ref(false)
const logs = ref([])
+const totalCount = ref(0)
const statusFilter = ref('')
const expandedIds = ref(new Set())
-const hasMore = ref(false)
+const currentPage = ref(1)
-const PAGE_SIZE = 50
+const PAGE_SIZE = 20
const filteredLogs = computed(() => {
if (!statusFilter.value) return logs.value
return logs.value.filter(l => l.status === statusFilter.value)
})
+const totalPages = computed(() => Math.ceil(totalCount.value / PAGE_SIZE))
+
+const paginationSummary = computed(() => {
+ const total = totalCount.value
+ if (total === 0) return ''
+ const start = (currentPage.value - 1) * PAGE_SIZE + 1
+ const end = Math.min(currentPage.value * PAGE_SIZE, total)
+ return `${start}-${end} / ${total}`
+})
+
+watch(currentPage, () => {
+ fetchLogs()
+})
+
function statusVariant(status: string | undefined) {
if (status === 'ok') return 'secondary' as const
if (status === 'pending') return 'default' as const
@@ -374,22 +411,18 @@ function toggleExpand(id: string | undefined) {
}
}
-async function fetchLogs(before?: string) {
+async function fetchLogs() {
if (!props.botId) return
isLoading.value = true
try {
+ const offset = (currentPage.value - 1) * PAGE_SIZE
const { data } = await getBotsByBotIdCompactionLogs({
path: { bot_id: props.botId },
- query: { limit: PAGE_SIZE, ...(before ? { before } : {}) },
+ query: { limit: PAGE_SIZE, offset },
throwOnError: true,
})
- const items = data?.items ?? []
- if (!before) {
- logs.value = items
- } else {
- logs.value.push(...items)
- }
- hasMore.value = items.length >= PAGE_SIZE
+ logs.value = data?.items ?? []
+ totalCount.value = data?.total_count ?? 0
} catch (error) {
toast.error(resolveApiErrorMessage(error, t('bots.compaction.loadFailed')))
} finally {
@@ -397,14 +430,9 @@ async function fetchLogs(before?: string) {
}
}
-async function loadMore() {
- if (logs.value.length === 0) return
- const lastLog = logs.value[logs.value.length - 1]
- await fetchLogs(lastLog?.started_at)
-}
-
async function handleRefresh() {
expandedIds.value.clear()
+ currentPage.value = 1
await fetchLogs()
}
@@ -416,6 +444,7 @@ async function handleClear() {
throwOnError: true,
})
logs.value = []
+ totalCount.value = 0
expandedIds.value.clear()
toast.success(t('bots.compaction.clearSuccess'))
} catch (error) {
diff --git a/apps/web/src/pages/bots/components/bot-heartbeat.vue b/apps/web/src/pages/bots/components/bot-heartbeat.vue
index d747e067..fa1d7fee 100644
--- a/apps/web/src/pages/bots/components/bot-heartbeat.vue
+++ b/apps/web/src/pages/bots/components/bot-heartbeat.vue
@@ -211,23 +211,42 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
@@ -240,6 +259,9 @@ import { useI18n } from 'vue-i18n'
import { toast } from 'vue-sonner'
import {
Button, Badge, Spinner, NativeSelect, Label, Switch, Input, Separator,
+ Pagination, PaginationContent, PaginationEllipsis,
+ PaginationFirst, PaginationItem, PaginationLast,
+ PaginationNext, PaginationPrevious,
} from '@memohai/ui'
import ConfirmPopover from '@/components/confirm-popover/index.vue'
import ModelSelect from './model-select.vue'
@@ -338,17 +360,32 @@ async function handleSaveSettings() {
const isLoading = ref(false)
const isClearing = ref(false)
const logs = ref([])
+const totalCount = ref(0)
const statusFilter = ref('')
const expandedIds = ref(new Set())
-const hasMore = ref(false)
+const currentPage = ref(1)
-const PAGE_SIZE = 50
+const PAGE_SIZE = 20
const filteredLogs = computed(() => {
if (!statusFilter.value) return logs.value
return logs.value.filter(l => l.status === statusFilter.value)
})
+const totalPages = computed(() => Math.ceil(totalCount.value / PAGE_SIZE))
+
+const paginationSummary = computed(() => {
+ const total = totalCount.value
+ if (total === 0) return ''
+ const start = (currentPage.value - 1) * PAGE_SIZE + 1
+ const end = Math.min(currentPage.value * PAGE_SIZE, total)
+ return `${start}-${end} / ${total}`
+})
+
+watch(currentPage, () => {
+ fetchLogs()
+})
+
function statusVariant(status: string | undefined) {
if (status === 'ok') return 'secondary' as const
if (status === 'alert') return 'default' as const
@@ -383,22 +420,18 @@ function toggleExpand(id: string | undefined) {
}
}
-async function fetchLogs(before?: string) {
+async function fetchLogs() {
if (!props.botId) return
isLoading.value = true
try {
+ const offset = (currentPage.value - 1) * PAGE_SIZE
const { data } = await getBotsByBotIdHeartbeatLogs({
path: { bot_id: props.botId },
- query: { limit: PAGE_SIZE, ...(before ? { before } : {}) },
+ query: { limit: PAGE_SIZE, offset },
throwOnError: true,
})
- const items = data?.items ?? []
- if (!before) {
- logs.value = items
- } else {
- logs.value.push(...items)
- }
- hasMore.value = items.length >= PAGE_SIZE
+ logs.value = data?.items ?? []
+ totalCount.value = data?.total_count ?? 0
} catch (error) {
toast.error(resolveApiErrorMessage(error, t('bots.heartbeat.loadFailed')))
} finally {
@@ -406,14 +439,9 @@ async function fetchLogs(before?: string) {
}
}
-async function loadMore() {
- if (logs.value.length === 0) return
- const lastLog = logs.value[logs.value.length - 1]
- await fetchLogs(lastLog?.started_at)
-}
-
async function handleRefresh() {
expandedIds.value.clear()
+ currentPage.value = 1
await fetchLogs()
}
@@ -425,6 +453,7 @@ async function handleClear() {
throwOnError: true,
})
logs.value = []
+ totalCount.value = 0
expandedIds.value.clear()
toast.success(t('bots.heartbeat.clearSuccess'))
} catch (error) {
diff --git a/apps/web/src/pages/providers/components/model-list.vue b/apps/web/src/pages/providers/components/model-list.vue
index 0f265dc6..9194b691 100644
--- a/apps/web/src/pages/providers/components/model-list.vue
+++ b/apps/web/src/pages/providers/components/model-list.vue
@@ -41,20 +41,40 @@
- {{ $t('models.showingCount', { count: displayLimit, total: filteredModels.length }) }}
+ {{ $t('models.showingCount', { count: `${pageStart}-${pageEnd}`, total: filteredModels.length }) }}
-
+
+
+
+
+
+
+
+
+
+
+
import { ref, computed, watch } from 'vue'
import {
- Button,
Empty,
EmptyContent,
EmptyDescription,
@@ -99,6 +118,14 @@ import {
InputGroup,
InputGroupAddon,
InputGroupInput,
+ Pagination,
+ PaginationContent,
+ PaginationEllipsis,
+ PaginationFirst,
+ PaginationItem,
+ PaginationLast,
+ PaginationNext,
+ PaginationPrevious,
} from '@memohai/ui'
import { Search, List } from 'lucide-vue-next'
import CreateModel from '@/components/create-model/index.vue'
@@ -120,7 +147,7 @@ defineEmits<{
}>()
const searchQuery = ref('')
-const displayLimit = ref(PAGE_SIZE)
+const currentPage = ref(1)
const filteredModels = computed(() => {
if (!props.models) return []
@@ -133,14 +160,15 @@ const filteredModels = computed(() => {
})
})
-const displayedModels = computed(() => filteredModels.value.slice(0, displayLimit.value))
-const hasMore = computed(() => displayLimit.value < filteredModels.value.length)
-
-function showMore() {
- displayLimit.value += PAGE_SIZE
-}
+const totalPages = computed(() => Math.ceil(filteredModels.value.length / PAGE_SIZE))
+const pageStart = computed(() => (currentPage.value - 1) * PAGE_SIZE + 1)
+const pageEnd = computed(() => Math.min(currentPage.value * PAGE_SIZE, filteredModels.value.length))
+const displayedModels = computed(() => {
+ const start = (currentPage.value - 1) * PAGE_SIZE
+ return filteredModels.value.slice(start, start + PAGE_SIZE)
+})
watch(searchQuery, () => {
- displayLimit.value = PAGE_SIZE
+ currentPage.value = 1
})
diff --git a/db/queries/compaction_logs.sql b/db/queries/compaction_logs.sql
index bae4c8b6..e73661fc 100644
--- a/db/queries/compaction_logs.sql
+++ b/db/queries/compaction_logs.sql
@@ -24,9 +24,11 @@ WHERE id = $1;
SELECT id, bot_id, session_id, status, summary, message_count, error_message, usage, model_id, started_at, completed_at
FROM bot_history_message_compacts
WHERE bot_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2)
ORDER BY started_at DESC
-LIMIT $3;
+LIMIT $2 OFFSET $3;
+
+-- name: CountCompactionLogsByBot :one
+SELECT count(*) FROM bot_history_message_compacts WHERE bot_id = $1;
-- name: ListCompactionLogsBySession :many
SELECT id, bot_id, session_id, status, summary, message_count, error_message, usage, model_id, started_at, completed_at
diff --git a/db/queries/heartbeat_logs.sql b/db/queries/heartbeat_logs.sql
index e57da7c5..abc75243 100644
--- a/db/queries/heartbeat_logs.sql
+++ b/db/queries/heartbeat_logs.sql
@@ -18,9 +18,11 @@ RETURNING id, bot_id, session_id, status, result_text, error_message, usage, mod
SELECT id, bot_id, session_id, status, result_text, error_message, usage, started_at, completed_at
FROM bot_heartbeat_logs
WHERE bot_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2::timestamptz)
ORDER BY started_at DESC
-LIMIT $3;
+LIMIT $2 OFFSET $3;
+
+-- name: CountHeartbeatLogsByBot :one
+SELECT count(*) FROM bot_heartbeat_logs WHERE bot_id = $1;
-- name: DeleteHeartbeatLogsByBot :exec
DELETE FROM bot_heartbeat_logs WHERE bot_id = $1;
diff --git a/db/queries/schedule_logs.sql b/db/queries/schedule_logs.sql
index 1fde9cb3..3ae0c7da 100644
--- a/db/queries/schedule_logs.sql
+++ b/db/queries/schedule_logs.sql
@@ -18,17 +18,21 @@ RETURNING id, schedule_id, bot_id, session_id, status, result_text, error_messag
SELECT id, schedule_id, bot_id, session_id, status, result_text, error_message, usage, started_at, completed_at
FROM schedule_logs
WHERE bot_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2::timestamptz)
ORDER BY started_at DESC
-LIMIT $3;
+LIMIT $2 OFFSET $3;
+
+-- name: CountScheduleLogsByBot :one
+SELECT count(*) FROM schedule_logs WHERE bot_id = $1;
-- name: ListScheduleLogsBySchedule :many
SELECT id, schedule_id, bot_id, session_id, status, result_text, error_message, usage, started_at, completed_at
FROM schedule_logs
WHERE schedule_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2::timestamptz)
ORDER BY started_at DESC
-LIMIT $3;
+LIMIT $2 OFFSET $3;
+
+-- name: CountScheduleLogsBySchedule :one
+SELECT count(*) FROM schedule_logs WHERE schedule_id = $1;
-- name: DeleteScheduleLogsByBot :exec
DELETE FROM schedule_logs WHERE bot_id = $1;
diff --git a/internal/command/heartbeat_cmd.go b/internal/command/heartbeat_cmd.go
index 5628a6a2..e57cf7b7 100644
--- a/internal/command/heartbeat_cmd.go
+++ b/internal/command/heartbeat_cmd.go
@@ -11,7 +11,7 @@ func (h *Handler) buildHeartbeatGroup() *CommandGroup {
Name: "logs",
Usage: "logs - List recent heartbeat logs",
Handler: func(cc CommandContext) (string, error) {
- items, err := h.heartbeatService.ListLogs(cc.Ctx, cc.BotID, nil, 10)
+ items, _, err := h.heartbeatService.ListLogs(cc.Ctx, cc.BotID, 10, 0)
if err != nil {
return "", err
}
diff --git a/internal/compaction/service.go b/internal/compaction/service.go
index 61aabe82..132d8661 100644
--- a/internal/compaction/service.go
+++ b/internal/compaction/service.go
@@ -5,7 +5,6 @@ import (
"encoding/json"
"log/slog"
"strings"
- "time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
@@ -151,35 +150,38 @@ func (s *Service) completeLog(ctx context.Context, logID pgtype.UUID, status, su
}
// ListLogs returns paginated compaction logs for a bot.
-func (s *Service) ListLogs(ctx context.Context, botID string, before *time.Time, limit int) ([]Log, error) {
+func (s *Service) ListLogs(ctx context.Context, botID string, limit, offset int) ([]Log, int64, error) {
botUUID, err := db.ParseUUID(botID)
if err != nil {
- return nil, err
+ return nil, 0, err
}
- var beforeTS pgtype.Timestamptz
- if before != nil {
- beforeTS = pgtype.Timestamptz{Time: *before, Valid: true}
+ if limit <= 0 || limit > 100 {
+ limit = 50
+ }
+ if offset < 0 {
+ offset = 0
}
- clampedLimit := limit
- if clampedLimit > 1000 {
- clampedLimit = 1000
+ total, err := s.queries.CountCompactionLogsByBot(ctx, botUUID)
+ if err != nil {
+ return nil, 0, err
}
+
rows, err := s.queries.ListCompactionLogsByBot(ctx, sqlc.ListCompactionLogsByBotParams{
- BotID: botUUID,
- Column2: beforeTS,
- Limit: int32(clampedLimit), //nolint:gosec // clamped above
+ BotID: botUUID,
+ Limit: int32(limit), //nolint:gosec // clamped above
+ Offset: int32(offset), //nolint:gosec // validated above
})
if err != nil {
- return nil, err
+ return nil, 0, err
}
logs := make([]Log, len(rows))
for i, r := range rows {
logs[i] = toLog(r)
}
- return logs, nil
+ return logs, total, nil
}
// DeleteLogs deletes all compaction logs for a bot.
diff --git a/internal/compaction/types.go b/internal/compaction/types.go
index 8a352b37..b1edb31b 100644
--- a/internal/compaction/types.go
+++ b/internal/compaction/types.go
@@ -22,7 +22,8 @@ type Log struct {
// ListLogsResponse is the API response for listing compaction logs.
type ListLogsResponse struct {
- Items []Log `json:"items"`
+ Items []Log `json:"items"`
+ TotalCount int64 `json:"total_count"`
}
// TriggerConfig holds the parameters needed to trigger a compaction.
diff --git a/internal/db/sqlc/compaction_logs.sql.go b/internal/db/sqlc/compaction_logs.sql.go
index ebe311cb..db262287 100644
--- a/internal/db/sqlc/compaction_logs.sql.go
+++ b/internal/db/sqlc/compaction_logs.sql.go
@@ -61,6 +61,17 @@ func (q *Queries) CompleteCompactionLog(ctx context.Context, arg CompleteCompact
return i, err
}
+const countCompactionLogsByBot = `-- name: CountCompactionLogsByBot :one
+SELECT count(*) FROM bot_history_message_compacts WHERE bot_id = $1
+`
+
+func (q *Queries) CountCompactionLogsByBot(ctx context.Context, botID pgtype.UUID) (int64, error) {
+ row := q.db.QueryRow(ctx, countCompactionLogsByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
const createCompactionLog = `-- name: CreateCompactionLog :one
INSERT INTO bot_history_message_compacts (bot_id, session_id)
VALUES ($1, $2)
@@ -129,19 +140,18 @@ const listCompactionLogsByBot = `-- name: ListCompactionLogsByBot :many
SELECT id, bot_id, session_id, status, summary, message_count, error_message, usage, model_id, started_at, completed_at
FROM bot_history_message_compacts
WHERE bot_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2)
ORDER BY started_at DESC
-LIMIT $3
+LIMIT $2 OFFSET $3
`
type ListCompactionLogsByBotParams struct {
- BotID pgtype.UUID `json:"bot_id"`
- Column2 pgtype.Timestamptz `json:"column_2"`
- Limit int32 `json:"limit"`
+ BotID pgtype.UUID `json:"bot_id"`
+ Limit int32 `json:"limit"`
+ Offset int32 `json:"offset"`
}
func (q *Queries) ListCompactionLogsByBot(ctx context.Context, arg ListCompactionLogsByBotParams) ([]BotHistoryMessageCompact, error) {
- rows, err := q.db.Query(ctx, listCompactionLogsByBot, arg.BotID, arg.Column2, arg.Limit)
+ rows, err := q.db.Query(ctx, listCompactionLogsByBot, arg.BotID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
diff --git a/internal/db/sqlc/heartbeat_logs.sql.go b/internal/db/sqlc/heartbeat_logs.sql.go
index 95c1cc18..6efc09d6 100644
--- a/internal/db/sqlc/heartbeat_logs.sql.go
+++ b/internal/db/sqlc/heartbeat_logs.sql.go
@@ -57,6 +57,17 @@ func (q *Queries) CompleteHeartbeatLog(ctx context.Context, arg CompleteHeartbea
return i, err
}
+const countHeartbeatLogsByBot = `-- name: CountHeartbeatLogsByBot :one
+SELECT count(*) FROM bot_heartbeat_logs WHERE bot_id = $1
+`
+
+func (q *Queries) CountHeartbeatLogsByBot(ctx context.Context, botID pgtype.UUID) (int64, error) {
+ row := q.db.QueryRow(ctx, countHeartbeatLogsByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
const createHeartbeatLog = `-- name: CreateHeartbeatLog :one
INSERT INTO bot_heartbeat_logs (bot_id, session_id, started_at)
VALUES ($1, $2::uuid, now())
@@ -110,15 +121,14 @@ const listHeartbeatLogsByBot = `-- name: ListHeartbeatLogsByBot :many
SELECT id, bot_id, session_id, status, result_text, error_message, usage, started_at, completed_at
FROM bot_heartbeat_logs
WHERE bot_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2::timestamptz)
ORDER BY started_at DESC
-LIMIT $3
+LIMIT $2 OFFSET $3
`
type ListHeartbeatLogsByBotParams struct {
- BotID pgtype.UUID `json:"bot_id"`
- Column2 pgtype.Timestamptz `json:"column_2"`
- Limit int32 `json:"limit"`
+ BotID pgtype.UUID `json:"bot_id"`
+ Limit int32 `json:"limit"`
+ Offset int32 `json:"offset"`
}
type ListHeartbeatLogsByBotRow struct {
@@ -134,7 +144,7 @@ type ListHeartbeatLogsByBotRow struct {
}
func (q *Queries) ListHeartbeatLogsByBot(ctx context.Context, arg ListHeartbeatLogsByBotParams) ([]ListHeartbeatLogsByBotRow, error) {
- rows, err := q.db.Query(ctx, listHeartbeatLogsByBot, arg.BotID, arg.Column2, arg.Limit)
+ rows, err := q.db.Query(ctx, listHeartbeatLogsByBot, arg.BotID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
diff --git a/internal/db/sqlc/schedule_logs.sql.go b/internal/db/sqlc/schedule_logs.sql.go
index 540a3eff..1717fc9d 100644
--- a/internal/db/sqlc/schedule_logs.sql.go
+++ b/internal/db/sqlc/schedule_logs.sql.go
@@ -58,6 +58,28 @@ func (q *Queries) CompleteScheduleLog(ctx context.Context, arg CompleteScheduleL
return i, err
}
+const countScheduleLogsByBot = `-- name: CountScheduleLogsByBot :one
+SELECT count(*) FROM schedule_logs WHERE bot_id = $1
+`
+
+func (q *Queries) CountScheduleLogsByBot(ctx context.Context, botID pgtype.UUID) (int64, error) {
+ row := q.db.QueryRow(ctx, countScheduleLogsByBot, botID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
+const countScheduleLogsBySchedule = `-- name: CountScheduleLogsBySchedule :one
+SELECT count(*) FROM schedule_logs WHERE schedule_id = $1
+`
+
+func (q *Queries) CountScheduleLogsBySchedule(ctx context.Context, scheduleID pgtype.UUID) (int64, error) {
+ row := q.db.QueryRow(ctx, countScheduleLogsBySchedule, scheduleID)
+ var count int64
+ err := row.Scan(&count)
+ return count, err
+}
+
const createScheduleLog = `-- name: CreateScheduleLog :one
INSERT INTO schedule_logs (schedule_id, bot_id, session_id, started_at)
VALUES ($1, $2, $3::uuid, now())
@@ -123,15 +145,14 @@ const listScheduleLogsByBot = `-- name: ListScheduleLogsByBot :many
SELECT id, schedule_id, bot_id, session_id, status, result_text, error_message, usage, started_at, completed_at
FROM schedule_logs
WHERE bot_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2::timestamptz)
ORDER BY started_at DESC
-LIMIT $3
+LIMIT $2 OFFSET $3
`
type ListScheduleLogsByBotParams struct {
- BotID pgtype.UUID `json:"bot_id"`
- Column2 pgtype.Timestamptz `json:"column_2"`
- Limit int32 `json:"limit"`
+ BotID pgtype.UUID `json:"bot_id"`
+ Limit int32 `json:"limit"`
+ Offset int32 `json:"offset"`
}
type ListScheduleLogsByBotRow struct {
@@ -148,7 +169,7 @@ type ListScheduleLogsByBotRow struct {
}
func (q *Queries) ListScheduleLogsByBot(ctx context.Context, arg ListScheduleLogsByBotParams) ([]ListScheduleLogsByBotRow, error) {
- rows, err := q.db.Query(ctx, listScheduleLogsByBot, arg.BotID, arg.Column2, arg.Limit)
+ rows, err := q.db.Query(ctx, listScheduleLogsByBot, arg.BotID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
@@ -182,15 +203,14 @@ const listScheduleLogsBySchedule = `-- name: ListScheduleLogsBySchedule :many
SELECT id, schedule_id, bot_id, session_id, status, result_text, error_message, usage, started_at, completed_at
FROM schedule_logs
WHERE schedule_id = $1
- AND ($2::timestamptz IS NULL OR started_at < $2::timestamptz)
ORDER BY started_at DESC
-LIMIT $3
+LIMIT $2 OFFSET $3
`
type ListScheduleLogsByScheduleParams struct {
- ScheduleID pgtype.UUID `json:"schedule_id"`
- Column2 pgtype.Timestamptz `json:"column_2"`
- Limit int32 `json:"limit"`
+ ScheduleID pgtype.UUID `json:"schedule_id"`
+ Limit int32 `json:"limit"`
+ Offset int32 `json:"offset"`
}
type ListScheduleLogsByScheduleRow struct {
@@ -207,7 +227,7 @@ type ListScheduleLogsByScheduleRow struct {
}
func (q *Queries) ListScheduleLogsBySchedule(ctx context.Context, arg ListScheduleLogsByScheduleParams) ([]ListScheduleLogsByScheduleRow, error) {
- rows, err := q.db.Query(ctx, listScheduleLogsBySchedule, arg.ScheduleID, arg.Column2, arg.Limit)
+ rows, err := q.db.Query(ctx, listScheduleLogsBySchedule, arg.ScheduleID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
diff --git a/internal/handlers/compaction.go b/internal/handlers/compaction.go
index 77a7787a..0cdc892e 100644
--- a/internal/handlers/compaction.go
+++ b/internal/handlers/compaction.go
@@ -4,9 +4,7 @@ import (
"context"
"log/slog"
"net/http"
- "strconv"
"strings"
- "time"
"github.com/labstack/echo/v4"
@@ -42,8 +40,8 @@ func (h *CompactionHandler) Register(e *echo.Echo) {
// @Description List compaction logs for a bot
// @Tags compaction
// @Param bot_id path string true "Bot ID"
-// @Param before query string false "Before timestamp (RFC3339)"
// @Param limit query int false "Limit" default(50)
+// @Param offset query int false "Offset" default(0)
// @Success 200 {object} compaction.ListLogsResponse
// @Failure 400 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
@@ -61,26 +59,12 @@ func (h *CompactionHandler) ListLogs(c echo.Context) error {
return err
}
- var before *time.Time
- if raw := strings.TrimSpace(c.QueryParam("before")); raw != "" {
- t, err := time.Parse(time.RFC3339Nano, raw)
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, "invalid before timestamp")
- }
- before = &t
- }
- limit := 50
- if raw := strings.TrimSpace(c.QueryParam("limit")); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil && v > 0 {
- limit = v
- }
- }
-
- items, err := h.service.ListLogs(c.Request().Context(), botID, before, limit)
+ limit, offset := parseOffsetLimit(c)
+ items, total, err := h.service.ListLogs(c.Request().Context(), botID, limit, offset)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- return c.JSON(http.StatusOK, compaction.ListLogsResponse{Items: items})
+ return c.JSON(http.StatusOK, compaction.ListLogsResponse{Items: items, TotalCount: total})
}
// DeleteLogs godoc
diff --git a/internal/handlers/handler_helpers.go b/internal/handlers/handler_helpers.go
index 06df3c77..040ab26e 100644
--- a/internal/handlers/handler_helpers.go
+++ b/internal/handlers/handler_helpers.go
@@ -4,6 +4,8 @@ import (
"context"
"errors"
"net/http"
+ "strconv"
+ "strings"
"github.com/labstack/echo/v4"
@@ -46,3 +48,19 @@ func AuthorizeBotAccess(ctx context.Context, botService *bots.Service, accountSe
}
return bot, nil
}
+
+// parseOffsetLimit extracts limit and offset query parameters with defaults.
+func parseOffsetLimit(c echo.Context) (limit, offset int) {
+ limit = 50
+ if raw := strings.TrimSpace(c.QueryParam("limit")); raw != "" {
+ if v, err := strconv.Atoi(raw); err == nil && v > 0 {
+ limit = v
+ }
+ }
+ if raw := strings.TrimSpace(c.QueryParam("offset")); raw != "" {
+ if v, err := strconv.Atoi(raw); err == nil && v >= 0 {
+ offset = v
+ }
+ }
+ return limit, offset
+}
diff --git a/internal/handlers/heartbeat.go b/internal/handlers/heartbeat.go
index 6b09ed9d..322ba4f9 100644
--- a/internal/handlers/heartbeat.go
+++ b/internal/handlers/heartbeat.go
@@ -4,9 +4,7 @@ import (
"context"
"log/slog"
"net/http"
- "strconv"
"strings"
- "time"
"github.com/labstack/echo/v4"
@@ -42,8 +40,8 @@ func (h *HeartbeatHandler) Register(e *echo.Echo) {
// @Description List heartbeat execution logs for a bot
// @Tags heartbeat
// @Param bot_id path string true "Bot ID"
-// @Param before query string false "Before timestamp (RFC3339)"
// @Param limit query int false "Limit" default(50)
+// @Param offset query int false "Offset" default(0)
// @Success 200 {object} heartbeat.ListLogsResponse
// @Failure 400 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
@@ -61,26 +59,12 @@ func (h *HeartbeatHandler) ListLogs(c echo.Context) error {
return err
}
- var before *time.Time
- if raw := strings.TrimSpace(c.QueryParam("before")); raw != "" {
- t, err := time.Parse(time.RFC3339Nano, raw)
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, "invalid before timestamp")
- }
- before = &t
- }
- limit := 50
- if raw := strings.TrimSpace(c.QueryParam("limit")); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil && v > 0 {
- limit = v
- }
- }
-
- items, err := h.service.ListLogs(c.Request().Context(), botID, before, limit)
+ limit, offset := parseOffsetLimit(c)
+ items, total, err := h.service.ListLogs(c.Request().Context(), botID, limit, offset)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- return c.JSON(http.StatusOK, heartbeat.ListLogsResponse{Items: items})
+ return c.JSON(http.StatusOK, heartbeat.ListLogsResponse{Items: items, TotalCount: total})
}
// DeleteLogs godoc
diff --git a/internal/handlers/schedule.go b/internal/handlers/schedule.go
index dfe368f6..26ce97b2 100644
--- a/internal/handlers/schedule.go
+++ b/internal/handlers/schedule.go
@@ -4,9 +4,7 @@ import (
"context"
"log/slog"
"net/http"
- "strconv"
"strings"
- "time"
"github.com/labstack/echo/v4"
@@ -225,8 +223,8 @@ func (h *ScheduleHandler) Delete(c echo.Context) error {
// @Description List schedule execution logs for a bot
// @Tags schedule
// @Param bot_id path string true "Bot ID"
-// @Param before query string false "Before timestamp (RFC3339)"
// @Param limit query int false "Limit" default(50)
+// @Param offset query int false "Offset" default(0)
// @Success 200 {object} schedule.ListLogsResponse
// @Failure 400 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
@@ -244,12 +242,12 @@ func (h *ScheduleHandler) ListLogs(c echo.Context) error {
return err
}
- before, limit := parseBeforeLimit(c)
- items, err := h.service.ListLogs(c.Request().Context(), botID, before, limit)
+ limit, offset := parseOffsetLimit(c)
+ items, total, err := h.service.ListLogs(c.Request().Context(), botID, limit, offset)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- return c.JSON(http.StatusOK, schedule.ListLogsResponse{Items: items})
+ return c.JSON(http.StatusOK, schedule.ListLogsResponse{Items: items, TotalCount: total})
}
// ListLogsBySchedule godoc
@@ -258,8 +256,8 @@ func (h *ScheduleHandler) ListLogs(c echo.Context) error {
// @Tags schedule
// @Param bot_id path string true "Bot ID"
// @Param id path string true "Schedule ID"
-// @Param before query string false "Before timestamp (RFC3339)"
// @Param limit query int false "Limit" default(50)
+// @Param offset query int false "Offset" default(0)
// @Success 200 {object} schedule.ListLogsResponse
// @Failure 400 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
@@ -281,12 +279,12 @@ func (h *ScheduleHandler) ListLogsBySchedule(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "schedule id is required")
}
- before, limit := parseBeforeLimit(c)
- items, err := h.service.ListLogsBySchedule(c.Request().Context(), scheduleID, before, limit)
+ limit, offset := parseOffsetLimit(c)
+ items, total, err := h.service.ListLogsBySchedule(c.Request().Context(), scheduleID, limit, offset)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- return c.JSON(http.StatusOK, schedule.ListLogsResponse{Items: items})
+ return c.JSON(http.StatusOK, schedule.ListLogsResponse{Items: items, TotalCount: total})
}
// DeleteLogs godoc
@@ -316,22 +314,6 @@ func (h *ScheduleHandler) DeleteLogs(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
}
-func parseBeforeLimit(c echo.Context) (*time.Time, int) {
- var before *time.Time
- if raw := strings.TrimSpace(c.QueryParam("before")); raw != "" {
- if t, err := time.Parse(time.RFC3339Nano, raw); err == nil {
- before = &t
- }
- }
- limit := 50
- if raw := strings.TrimSpace(c.QueryParam("limit")); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil && v > 0 {
- limit = v
- }
- }
- return before, limit
-}
-
func (*ScheduleHandler) requireUserID(c echo.Context) (string, error) {
return RequireChannelIdentityID(c)
}
diff --git a/internal/heartbeat/service.go b/internal/heartbeat/service.go
index e954d734..d19ae56f 100644
--- a/internal/heartbeat/service.go
+++ b/internal/heartbeat/service.go
@@ -182,31 +182,36 @@ func (s *Service) completeLog(ctx context.Context, logID pgtype.UUID, status, re
}
}
-func (s *Service) ListLogs(ctx context.Context, botID string, before *time.Time, limit int) ([]Log, error) {
+func (s *Service) ListLogs(ctx context.Context, botID string, limit, offset int) ([]Log, int64, error) {
pgBotID, err := db.ParseUUID(botID)
if err != nil {
- return nil, err
+ return nil, 0, err
}
if limit <= 0 || limit > 100 {
limit = 50
}
- beforeTS := pgtype.Timestamptz{}
- if before != nil {
- beforeTS = pgtype.Timestamptz{Time: *before, Valid: true}
+ if offset < 0 {
+ offset = 0
}
+
+ total, err := s.queries.CountHeartbeatLogsByBot(ctx, pgBotID)
+ if err != nil {
+ return nil, 0, err
+ }
+
rows, err := s.queries.ListHeartbeatLogsByBot(ctx, sqlc.ListHeartbeatLogsByBotParams{
- BotID: pgBotID,
- Column2: beforeTS,
- Limit: int32(limit), //nolint:gosec // capped to 100 above
+ BotID: pgBotID,
+ Limit: int32(limit), //nolint:gosec // capped to 100 above
+ Offset: int32(offset), //nolint:gosec // validated above
})
if err != nil {
- return nil, err
+ return nil, 0, err
}
items := make([]Log, 0, len(rows))
for _, row := range rows {
items = append(items, toLog(row))
}
- return items, nil
+ return items, total, nil
}
func (s *Service) DeleteLogs(ctx context.Context, botID string) error {
diff --git a/internal/heartbeat/types.go b/internal/heartbeat/types.go
index d446b0f6..cae5b9a1 100644
--- a/internal/heartbeat/types.go
+++ b/internal/heartbeat/types.go
@@ -21,5 +21,6 @@ type Log struct {
}
type ListLogsResponse struct {
- Items []Log `json:"items"`
+ Items []Log `json:"items"`
+ TotalCount int64 `json:"total_count"`
}
diff --git a/internal/schedule/service.go b/internal/schedule/service.go
index 2804f157..97441f77 100644
--- a/internal/schedule/service.go
+++ b/internal/schedule/service.go
@@ -325,58 +325,68 @@ func (s *Service) completeLog(ctx context.Context, logID pgtype.UUID, status, re
}
}
-func (s *Service) ListLogs(ctx context.Context, botID string, before *time.Time, limit int) ([]Log, error) {
+func (s *Service) ListLogs(ctx context.Context, botID string, limit, offset int) ([]Log, int64, error) {
pgBotID, err := db.ParseUUID(botID)
if err != nil {
- return nil, err
+ return nil, 0, err
}
if limit <= 0 || limit > 100 {
limit = 50
}
- beforeTS := pgtype.Timestamptz{}
- if before != nil {
- beforeTS = pgtype.Timestamptz{Time: *before, Valid: true}
+ if offset < 0 {
+ offset = 0
}
+
+ total, err := s.queries.CountScheduleLogsByBot(ctx, pgBotID)
+ if err != nil {
+ return nil, 0, err
+ }
+
rows, err := s.queries.ListScheduleLogsByBot(ctx, sqlc.ListScheduleLogsByBotParams{
- BotID: pgBotID,
- Column2: beforeTS,
- Limit: int32(limit), //nolint:gosec // capped to 100 above
+ BotID: pgBotID,
+ Limit: int32(limit), //nolint:gosec // capped to 100 above
+ Offset: int32(offset), //nolint:gosec // validated above
})
if err != nil {
- return nil, err
+ return nil, 0, err
}
items := make([]Log, 0, len(rows))
for _, row := range rows {
items = append(items, toScheduleLog(row))
}
- return items, nil
+ return items, total, nil
}
-func (s *Service) ListLogsBySchedule(ctx context.Context, scheduleID string, before *time.Time, limit int) ([]Log, error) {
+func (s *Service) ListLogsBySchedule(ctx context.Context, scheduleID string, limit, offset int) ([]Log, int64, error) {
pgID, err := db.ParseUUID(scheduleID)
if err != nil {
- return nil, err
+ return nil, 0, err
}
if limit <= 0 || limit > 100 {
limit = 50
}
- beforeTS := pgtype.Timestamptz{}
- if before != nil {
- beforeTS = pgtype.Timestamptz{Time: *before, Valid: true}
+ if offset < 0 {
+ offset = 0
}
+
+ total, err := s.queries.CountScheduleLogsBySchedule(ctx, pgID)
+ if err != nil {
+ return nil, 0, err
+ }
+
rows, err := s.queries.ListScheduleLogsBySchedule(ctx, sqlc.ListScheduleLogsByScheduleParams{
ScheduleID: pgID,
- Column2: beforeTS,
- Limit: int32(limit), //nolint:gosec // capped to 100 above
+ Limit: int32(limit), //nolint:gosec // capped to 100 above
+ Offset: int32(offset), //nolint:gosec // validated above
})
if err != nil {
- return nil, err
+ return nil, 0, err
}
items := make([]Log, 0, len(rows))
for _, row := range rows {
items = append(items, toScheduleLogFromSchedule(row))
}
- return items, nil
+ return items, total, nil
}
func (s *Service) DeleteLogs(ctx context.Context, botID string) error {
diff --git a/internal/schedule/types.go b/internal/schedule/types.go
index fcc4785e..e49c65e8 100644
--- a/internal/schedule/types.go
+++ b/internal/schedule/types.go
@@ -85,5 +85,6 @@ type Log struct {
}
type ListLogsResponse struct {
- Items []Log `json:"items"`
+ Items []Log `json:"items"`
+ TotalCount int64 `json:"total_count"`
}
diff --git a/packages/sdk/src/types.gen.ts b/packages/sdk/src/types.gen.ts
index 3b4dec6e..cd188d2d 100644
--- a/packages/sdk/src/types.gen.ts
+++ b/packages/sdk/src/types.gen.ts
@@ -575,6 +575,7 @@ export type ChannelUpsertConfigRequest = {
export type CompactionListLogsResponse = {
items?: Array;
+ total_count?: number;
};
export type CompactionLog = {
@@ -1053,6 +1054,7 @@ export type HandlersUpdateSessionRequest = {
export type HeartbeatListLogsResponse = {
items?: Array;
+ total_count?: number;
};
export type HeartbeatLog = {
@@ -1320,6 +1322,7 @@ export type ScheduleCreateRequest = {
export type ScheduleListLogsResponse = {
items?: Array;
+ total_count?: number;
};
export type ScheduleListResponse = {
@@ -2267,14 +2270,14 @@ export type GetBotsByBotIdCompactionLogsData = {
bot_id: string;
};
query?: {
- /**
- * Before timestamp (RFC3339)
- */
- before?: string;
/**
* Limit
*/
limit?: number;
+ /**
+ * Offset
+ */
+ offset?: number;
};
url: '/bots/{bot_id}/compaction/logs';
};
@@ -3538,14 +3541,14 @@ export type GetBotsByBotIdHeartbeatLogsData = {
bot_id: string;
};
query?: {
- /**
- * Before timestamp (RFC3339)
- */
- before?: string;
/**
* Limit
*/
limit?: number;
+ /**
+ * Offset
+ */
+ offset?: number;
};
url: '/bots/{bot_id}/heartbeat/logs';
};
@@ -4775,14 +4778,14 @@ export type GetBotsByBotIdScheduleLogsData = {
bot_id: string;
};
query?: {
- /**
- * Before timestamp (RFC3339)
- */
- before?: string;
/**
* Limit
*/
limit?: number;
+ /**
+ * Offset
+ */
+ offset?: number;
};
url: '/bots/{bot_id}/schedule/logs';
};
@@ -4929,14 +4932,14 @@ export type GetBotsByBotIdScheduleByIdLogsData = {
id: string;
};
query?: {
- /**
- * Before timestamp (RFC3339)
- */
- before?: string;
/**
* Limit
*/
limit?: number;
+ /**
+ * Offset
+ */
+ offset?: number;
};
url: '/bots/{bot_id}/schedule/{id}/logs';
};
diff --git a/spec/docs.go b/spec/docs.go
index 3469a39b..ffa334e0 100644
--- a/spec/docs.go
+++ b/spec/docs.go
@@ -851,18 +851,19 @@ const docTemplate = `{
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -2374,18 +2375,19 @@ const docTemplate = `{
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -3909,18 +3911,19 @@ const docTemplate = `{
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -4122,18 +4125,19 @@ const docTemplate = `{
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -10213,6 +10217,9 @@ const docTemplate = `{
"items": {
"$ref": "#/definitions/compaction.Log"
}
+ },
+ "total_count": {
+ "type": "integer"
}
}
},
@@ -11386,6 +11393,9 @@ const docTemplate = `{
"items": {
"$ref": "#/definitions/heartbeat.Log"
}
+ },
+ "total_count": {
+ "type": "integer"
}
}
},
@@ -12068,6 +12078,9 @@ const docTemplate = `{
"items": {
"$ref": "#/definitions/schedule.Log"
}
+ },
+ "total_count": {
+ "type": "integer"
}
}
},
diff --git a/spec/swagger.json b/spec/swagger.json
index 7bd8f7b2..eb61d66f 100644
--- a/spec/swagger.json
+++ b/spec/swagger.json
@@ -842,18 +842,19 @@
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -2365,18 +2366,19 @@
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -3900,18 +3902,19 @@
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -4113,18 +4116,19 @@
"in": "path",
"required": true
},
- {
- "type": "string",
- "description": "Before timestamp (RFC3339)",
- "name": "before",
- "in": "query"
- },
{
"type": "integer",
"default": 50,
"description": "Limit",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "integer",
+ "default": 0,
+ "description": "Offset",
+ "name": "offset",
+ "in": "query"
}
],
"responses": {
@@ -10204,6 +10208,9 @@
"items": {
"$ref": "#/definitions/compaction.Log"
}
+ },
+ "total_count": {
+ "type": "integer"
}
}
},
@@ -11377,6 +11384,9 @@
"items": {
"$ref": "#/definitions/heartbeat.Log"
}
+ },
+ "total_count": {
+ "type": "integer"
}
}
},
@@ -12059,6 +12069,9 @@
"items": {
"$ref": "#/definitions/schedule.Log"
}
+ },
+ "total_count": {
+ "type": "integer"
}
}
},
diff --git a/spec/swagger.yaml b/spec/swagger.yaml
index 1b7607fb..aaac48da 100644
--- a/spec/swagger.yaml
+++ b/spec/swagger.yaml
@@ -957,6 +957,8 @@ definitions:
items:
$ref: '#/definitions/compaction.Log'
type: array
+ total_count:
+ type: integer
type: object
compaction.Log:
properties:
@@ -1723,6 +1725,8 @@ definitions:
items:
$ref: '#/definitions/heartbeat.Log'
type: array
+ total_count:
+ type: integer
type: object
heartbeat.Log:
properties:
@@ -2174,6 +2178,8 @@ definitions:
items:
$ref: '#/definitions/schedule.Log'
type: array
+ total_count:
+ type: integer
type: object
schedule.ListResponse:
properties:
@@ -3173,15 +3179,16 @@ paths:
name: bot_id
required: true
type: string
- - description: Before timestamp (RFC3339)
- in: query
- name: before
- type: string
- default: 50
description: Limit
in: query
name: limit
type: integer
+ - default: 0
+ description: Offset
+ in: query
+ name: offset
+ type: integer
responses:
"200":
description: OK
@@ -4179,15 +4186,16 @@ paths:
name: bot_id
required: true
type: string
- - description: Before timestamp (RFC3339)
- in: query
- name: before
- type: string
- default: 50
description: Limit
in: query
name: limit
type: integer
+ - default: 0
+ description: Offset
+ in: query
+ name: offset
+ type: integer
responses:
"200":
description: OK
@@ -5274,15 +5282,16 @@ paths:
name: id
required: true
type: string
- - description: Before timestamp (RFC3339)
- in: query
- name: before
- type: string
- default: 50
description: Limit
in: query
name: limit
type: integer
+ - default: 0
+ description: Offset
+ in: query
+ name: offset
+ type: integer
responses:
"200":
description: OK
@@ -5330,15 +5339,16 @@ paths:
name: bot_id
required: true
type: string
- - description: Before timestamp (RFC3339)
- in: query
- name: before
- type: string
- default: 50
description: Limit
in: query
name: limit
type: integer
+ - default: 0
+ description: Offset
+ in: query
+ name: offset
+ type: integer
responses:
"200":
description: OK