package db import "strings" func (s *Store) ListSystemLogsPage(filters SystemLogFilters) (SystemLogPage, error) { page := filters.Page if page <= 0 { page = 1 } perPage := filters.PerPage if perPage <= 0 { perPage = 35 } if perPage > 100 { perPage = 100 } base := systemLogUnionSQL() where, args := systemLogWhere(filters) var total int if err := s.queryRow(`SELECT COUNT(*) FROM (`+base+`) system_logs`+where, args...).Scan(&total); err != nil { return SystemLogPage{}, err } queryArgs := append(append([]any{}, args...), perPage, (page-1)*perPage) rows, err := s.query(`SELECT id, category, type, target, status, message, detail, created_at FROM (`+base+`) system_logs`+where+` ORDER BY created_at DESC, id DESC LIMIT ? OFFSET ?`, queryArgs...) if err != nil { return SystemLogPage{}, err } defer rows.Close() items := []SystemLogItem{} for rows.Next() { var item SystemLogItem if err := rows.Scan(&item.ID, &item.Category, &item.Type, &item.Target, &item.Status, &item.Message, &item.Detail, &item.CreatedAt); err != nil { return SystemLogPage{}, err } items = append(items, item) } if err := rows.Err(); err != nil { return SystemLogPage{}, err } return SystemLogPage{Items: items, Total: total, Page: page, PerPage: perPage}, nil } func systemLogUnionSQL() string { return `SELECT id, 'operation' AS category, type, target, COALESCE(NULLIF(actor, ''), 'system') AS status, message, user_agent AS detail, created_at FROM audit_logs UNION ALL SELECT h.id, 'health' AS category, 'endpoint.health' AS type, COALESCE(NULLIF(e.source_id, ''), 'unknown') AS target, h.status, COALESCE(NULLIF(e.name, ''), 'unknown endpoint') AS message, h.error AS detail, h.checked_at AS created_at FROM endpoint_health_checks h LEFT JOIN source_endpoints e ON e.id = h.source_db_id UNION ALL SELECT id, 'client' AS category, 'endpoint.call' AS type, source_id AS target, status, error AS message, client AS detail, created_at FROM endpoint_call_logs UNION ALL SELECT id, 'database_sync' AS category, direction AS type, direction AS target, status, message, tables_json AS detail, COALESCE(NULLIF(finished_at, ''), started_at) AS created_at FROM database_sync_jobs UNION ALL SELECT id, 'legacy_sync' AS category, 'legacy.sync' AS type, 'legacy' AS target, status, summary AS message, stats_json AS detail, COALESCE(NULLIF(finished_at, ''), started_at) AS created_at FROM legacy_sync_jobs` } func systemLogWhere(filters SystemLogFilters) (string, []any) { clauses := []string{} args := []any{} if category := strings.TrimSpace(filters.Category); category != "" { clauses = append(clauses, "category = ?") args = append(args, sanitize(category)) } if query := strings.TrimSpace(filters.Query); query != "" { like := "%" + strings.ToLower(sanitize(query)) + "%" clauses = append(clauses, `(LOWER(category) LIKE ? OR LOWER(type) LIKE ? OR LOWER(target) LIKE ? OR LOWER(status) LIKE ? OR LOWER(message) LIKE ? OR LOWER(detail) LIKE ? OR LOWER(created_at) LIKE ?)`) args = append(args, like, like, like, like, like, like, like) } if len(clauses) == 0 { return "", args } return " WHERE " + strings.Join(clauses, " AND "), args }