@allbluecn/admin-app 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/package.json +19 -17
  2. package/src/components/admin/admin-user-menu.tsx +4 -4
  3. package/src/components/admin/developer-log.tsx +16 -32
  4. package/src/components/ai-memory/memory-facts-panel.tsx +30 -70
  5. package/src/components/ai-prompts/preview-panel.tsx +1 -1
  6. package/src/components/ai-prompts/prompt-editor.tsx +8 -9
  7. package/src/components/ai-prompts/prompt-table.tsx +6 -6
  8. package/src/components/ai-prompts/section-editor.tsx +3 -3
  9. package/src/components/ai-prompts/variable-panel.tsx +2 -2
  10. package/src/components/ai-prompts/version-history.tsx +21 -41
  11. package/src/components/ai-provider/model-list-editor.tsx +2 -3
  12. package/src/components/ai-provider/provider-form.tsx +7 -7
  13. package/src/components/ai-provider/provider-table.tsx +17 -40
  14. package/src/components/auth/admin-login-form.tsx +7 -7
  15. package/src/components/auth/icon-input.tsx +2 -2
  16. package/src/components/auth/password-input.tsx +2 -2
  17. package/src/components/component/component-form-dialog.tsx +10 -18
  18. package/src/components/component/component-import-dialog.tsx +6 -6
  19. package/src/components/component/component-preview.tsx +311 -0
  20. package/src/components/component/component-table.tsx +3 -3
  21. package/src/components/dependency/changelog-dialog.tsx +2 -2
  22. package/src/components/dependency/columns.tsx +3 -3
  23. package/src/components/dependency/dependency-table.tsx +7 -7
  24. package/src/components/dependency/edit-dialog.tsx +8 -8
  25. package/src/components/invitations/invitations-page.tsx +26 -49
  26. package/src/components/knowledge-public/bookmark-form-dialog.tsx +6 -6
  27. package/src/components/knowledge-public/compliance-form-dialog.tsx +6 -6
  28. package/src/components/knowledge-public/legal-form-dialog.tsx +7 -7
  29. package/src/components/layout/sidebar-02/app-sidebar.tsx +1 -2
  30. package/src/components/layout/sidebar-02/index.tsx +1 -1
  31. package/src/components/layout/sidebar-02/nav-main.tsx +2 -3
  32. package/src/components/layout/sidebar-02/nav-notifications.tsx +3 -3
  33. package/src/components/market/columns.tsx +4 -4
  34. package/src/components/market/market-app-form.tsx +6 -7
  35. package/src/components/market/market-app-list.tsx +14 -29
  36. package/src/components/monitoring/error-list.tsx +2 -2
  37. package/src/components/monitoring/metrics-stats.tsx +1 -1
  38. package/src/components/monitoring/request-log-table.tsx +2 -2
  39. package/src/components/oauth-providers/provider-page.tsx +20 -38
  40. package/src/components/permissions/assign-dialog.tsx +135 -0
  41. package/src/components/permissions/create-role-dialog.tsx +135 -0
  42. package/src/components/permissions/types.ts +49 -0
  43. package/src/components/user/columns.tsx +6 -6
  44. package/src/components/user/settings-activity-log.tsx +7 -7
  45. package/src/components/user/settings-profile/avatar-uploader.tsx +118 -0
  46. package/src/components/user/settings-profile/social-links.tsx +63 -0
  47. package/src/components/user/settings-profile.tsx +26 -125
  48. package/src/components/user/user-create-dialog.tsx +6 -6
  49. package/src/components/user/user-detail/error-boundary.tsx +19 -0
  50. package/src/components/user/user-detail/user-detail-dialogs.tsx +203 -0
  51. package/src/components/user/user-filters.tsx +4 -4
  52. package/src/lib/auth/page-gate.ts +28 -0
  53. package/src/lib/auth.config.ts +25 -9
  54. package/src/lib/i18n/detect.ts +1 -1
  55. package/src/lib/loader-serial.ts +14 -0
  56. package/src/providers/index.tsx +1 -1
  57. package/src/routeTree.gen.ts +67 -78
  58. package/src/routes/__root.tsx +4 -4
  59. package/src/routes/_nav/audit/route.lazy.tsx +1 -2
  60. package/src/routes/_nav/compliance/route.lazy.tsx +5 -3
  61. package/src/routes/_nav/components/route.lazy.tsx +14 -34
  62. package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +18 -38
  63. package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +18 -38
  64. package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +6 -6
  65. package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +3 -3
  66. package/src/routes/_nav/licenses/new/route.lazy.tsx +5 -5
  67. package/src/routes/_nav/licenses/route.lazy.tsx +1 -1
  68. package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +2 -2
  69. package/src/routes/_nav/monitoring/errors/route.lazy.tsx +10 -10
  70. package/src/routes/_nav/monitoring/errors/route.tsx +7 -0
  71. package/src/routes/_nav/monitoring/health/route.lazy.tsx +13 -15
  72. package/src/routes/_nav/monitoring/health/route.tsx +14 -0
  73. package/src/routes/_nav/monitoring/logs/route.lazy.tsx +24 -9
  74. package/src/routes/_nav/monitoring/logs/route.tsx +13 -0
  75. package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +12 -14
  76. package/src/routes/_nav/monitoring/metrics/route.tsx +7 -0
  77. package/src/routes/_nav/monitoring/overview/route.lazy.tsx +14 -13
  78. package/src/routes/_nav/monitoring/overview/route.tsx +17 -0
  79. package/src/routes/_nav/monitoring/route.lazy.tsx +6 -6
  80. package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +2 -2
  81. package/src/routes/_nav/permissions/route.lazy.tsx +21 -311
  82. package/src/routes/_nav/permissions/route.tsx +1 -1
  83. package/src/routes/_nav/route.tsx +5 -13
  84. package/src/routes/_nav/sso/route.lazy.tsx +2 -3
  85. package/src/routes/_nav/users/$userId/route.lazy.tsx +91 -156
  86. package/src/routes/_nav/users/-components/users-table.tsx +1 -1
  87. package/src/routes/_nav/users/route.lazy.tsx +1 -1
  88. package/src/routes/login/route.lazy.tsx +1 -1
  89. package/src/routes/setup/route.lazy.tsx +2 -2
  90. package/src/server/__tests__/admin-billing.test.ts +191 -16
  91. package/src/server/__tests__/compliance.test.ts +44 -7
  92. package/src/server/__tests__/permissions.test.ts +10 -5
  93. package/src/server/__tests__/prompt-defaults.test.ts +3 -3
  94. package/src/server/admin-ai-alerts.ts +2 -1
  95. package/src/server/admin-ai-usage.ts +2 -1
  96. package/src/server/ai-memory/store.ts +6 -2
  97. package/src/server/{serverFns/ai-prompts → ai-prompts}/prompt-defaults.ts +5 -3
  98. package/src/server/{serverFns/dependency → dependency}/helpers.ts +4 -3
  99. package/src/server/knowledge-public/github-sync.ts +2 -1
  100. package/src/server/license-issue.ts +2 -1
  101. package/src/server/middlewares/auth-rate-limit.ts +117 -0
  102. package/src/server/observability/health-check.ts +4 -1
  103. package/src/server/observability/metrics.ts +17 -15
  104. package/src/server/observability/request-middleware.ts +2 -1
  105. package/src/server/observability/timing.ts +14 -16
  106. package/src/server/serverFns/admin-billing.ts +107 -12
  107. package/src/server/serverFns/ai-memory/list-users.ts +2 -1
  108. package/src/server/serverFns/ai-prompts/activate.ts +2 -1
  109. package/src/server/serverFns/ai-prompts/get.ts +3 -2
  110. package/src/server/serverFns/ai-prompts/list.ts +3 -2
  111. package/src/server/serverFns/ai-prompts/rollback.ts +3 -1
  112. package/src/server/serverFns/ai-prompts/save.ts +5 -3
  113. package/src/server/serverFns/ai-provider/create.ts +4 -1
  114. package/src/server/serverFns/ai-provider/delete.ts +4 -1
  115. package/src/server/serverFns/ai-provider/get.ts +4 -1
  116. package/src/server/serverFns/ai-provider/list.ts +28 -1
  117. package/src/server/serverFns/ai-provider/seed-from-seed.ts +2 -1
  118. package/src/server/serverFns/ai-provider/update.ts +4 -1
  119. package/src/server/serverFns/anyhub/market.ts +8 -1
  120. package/src/server/serverFns/audit.ts +14 -1
  121. package/src/server/serverFns/auth/setup.ts +3 -1
  122. package/src/server/serverFns/compliance.ts +32 -3
  123. package/src/server/serverFns/components/import.ts +69 -14
  124. package/src/server/serverFns/components/list.ts +4 -1
  125. package/src/server/serverFns/components/mutations.ts +9 -1
  126. package/src/server/serverFns/dependency/list.ts +28 -5
  127. package/src/server/serverFns/dependency/sync.ts +7 -2
  128. package/src/server/serverFns/dependency/update.ts +4 -1
  129. package/src/server/serverFns/developer-log-auto-generate.ts +63 -32
  130. package/src/server/serverFns/developer-logs.ts +5 -1
  131. package/src/server/serverFns/invitations.ts +7 -1
  132. package/src/server/serverFns/knowledge-public/bookmarks/create.ts +2 -1
  133. package/src/server/serverFns/knowledge-public/bookmarks/delete.ts +2 -1
  134. package/src/server/serverFns/knowledge-public/bookmarks/detail.ts +2 -1
  135. package/src/server/serverFns/knowledge-public/bookmarks/list.ts +2 -1
  136. package/src/server/serverFns/knowledge-public/bookmarks/sync-all-github.ts +2 -1
  137. package/src/server/serverFns/knowledge-public/bookmarks/update.ts +2 -1
  138. package/src/server/serverFns/knowledge-public/compliance/create.ts +2 -1
  139. package/src/server/serverFns/knowledge-public/compliance/delete.ts +2 -1
  140. package/src/server/serverFns/knowledge-public/compliance/detail.ts +2 -1
  141. package/src/server/serverFns/knowledge-public/compliance/list.ts +2 -1
  142. package/src/server/serverFns/knowledge-public/compliance/update.ts +2 -1
  143. package/src/server/serverFns/knowledge-public/legal/create.ts +2 -1
  144. package/src/server/serverFns/knowledge-public/legal/delete.ts +2 -1
  145. package/src/server/serverFns/knowledge-public/legal/detail.ts +2 -1
  146. package/src/server/serverFns/knowledge-public/legal/list.ts +2 -1
  147. package/src/server/serverFns/knowledge-public/legal/update.ts +2 -1
  148. package/src/server/serverFns/licenses.ts +8 -2
  149. package/src/server/serverFns/monitoring/get-errors.ts +2 -1
  150. package/src/server/serverFns/monitoring/get-health.ts +2 -1
  151. package/src/server/serverFns/monitoring/get-metrics.ts +2 -1
  152. package/src/server/serverFns/monitoring/get-request-logs.ts +2 -1
  153. package/src/server/serverFns/monitoring/report-error.ts +2 -1
  154. package/src/server/serverFns/monitoring/reset.ts +2 -1
  155. package/src/server/serverFns/oauth-providers.ts +4 -1
  156. package/src/server/serverFns/permissions.ts +18 -11
  157. package/src/server/serverFns/sentry/config.ts +11 -8
  158. package/src/server/serverFns/sso-config.ts +17 -2
  159. package/src/server/serverFns/user/create.ts +6 -1
  160. package/src/server/serverFns/user/delete.ts +6 -1
  161. package/src/server/serverFns/user/detail.ts +5 -2
  162. package/src/server/serverFns/user/list.ts +19 -4
  163. package/src/server/serverFns/user/reset-password.ts +10 -2
  164. package/src/server/serverFns/user/update-role.ts +10 -2
  165. package/src/server/serverFns/user/update-status.ts +10 -2
  166. package/src/styles/globals.css +4 -0
  167. package/src/components/data-table/data-table-pagination.tsx +0 -112
  168. package/src/components/data-table/data-table.tsx +0 -218
  169. package/src/components/data-table/table-detail-layout.tsx +0 -98
  170. package/src/components/ui/alert-dialog.tsx +0 -187
  171. package/src/components/ui/alert.tsx +0 -75
  172. package/src/components/ui/avatar.tsx +0 -106
  173. package/src/components/ui/badge.tsx +0 -51
  174. package/src/components/ui/button.tsx +0 -57
  175. package/src/components/ui/card.tsx +0 -102
  176. package/src/components/ui/checkbox.tsx +0 -28
  177. package/src/components/ui/collapsible.tsx +0 -19
  178. package/src/components/ui/dialog.tsx +0 -155
  179. package/src/components/ui/dropdown-menu.tsx +0 -267
  180. package/src/components/ui/field.tsx +0 -236
  181. package/src/components/ui/input-group.tsx +0 -158
  182. package/src/components/ui/input.tsx +0 -19
  183. package/src/components/ui/label.tsx +0 -17
  184. package/src/components/ui/pagination.tsx +0 -130
  185. package/src/components/ui/select.tsx +0 -234
  186. package/src/components/ui/separator.tsx +0 -22
  187. package/src/components/ui/sheet.tsx +0 -135
  188. package/src/components/ui/sidebar.tsx +0 -721
  189. package/src/components/ui/skeleton.tsx +0 -13
  190. package/src/components/ui/switch.tsx +0 -29
  191. package/src/components/ui/table.tsx +0 -115
  192. package/src/components/ui/textarea.tsx +0 -17
  193. package/src/components/ui/toast.tsx +0 -282
  194. package/src/components/ui/tooltip.tsx +0 -63
  195. package/src/hooks/use-mobile.ts +0 -19
  196. package/src/lib/avatar.ts +0 -41
  197. /package/src/routes/{api.auth.$.ts → api/auth/$.ts} +0 -0
  198. /package/src/routes/{api.sentry-test.ts → api/sentry-test.ts} +0 -0
@@ -22,11 +22,11 @@ import { useState } from 'react'
22
22
  import { useForm } from '@tanstack/react-form'
23
23
  import { useServerFn } from '@tanstack/react-start'
24
24
  import { Check, Copy } from 'lucide-react'
25
- import { Button } from '@/components/ui/button'
26
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
27
- import { Input } from '@/components/ui/input'
28
- import { Label } from '@/components/ui/label'
29
- import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
25
+ import { Button } from '@allbluecn/ui/button'
26
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@allbluecn/ui/card'
27
+ import { Input } from '@allbluecn/ui/input'
28
+ import { Label } from '@allbluecn/ui/label'
29
+ import { Alert, AlertDescription, AlertTitle } from '@allbluecn/ui/alert'
30
30
  import { issueLicenseFn } from '@/server/serverFns/licenses'
31
31
  import { FeatureRegistry } from '@allbluecn/kernel'
32
32
 
@@ -18,7 +18,7 @@
18
18
  import { createLazyFileRoute, Link } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import type React from 'react'
21
- import { Button } from '@/components/ui/button'
21
+ import { Button } from '@allbluecn/ui/button'
22
22
 
23
23
  export const Route = createLazyFileRoute('/_nav/licenses')({
24
24
  pendingComponent: () => (
@@ -8,8 +8,8 @@ import { tooltip } from '@tanstack/charts/tooltip'
8
8
  import { Chart } from '@tanstack/charts/react'
9
9
  import { BarChart3, AlertCircle, AlertTriangle, ChevronLeft, ChevronRight, RefreshCw } from 'lucide-react'
10
10
  import { PageHeader } from '@/components/common/page-header'
11
- import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
12
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
11
+ import { Card, CardContent, CardHeader, CardTitle } from '@allbluecn/ui/card'
12
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@allbluecn/ui/table'
13
13
  import { getAiUsageOverviewFn } from '@/server/serverFns/monitoring/get-ai-usage-overview'
14
14
  import { checkAiAlertsFn } from '@/server/serverFns/monitoring/check-ai-alerts'
15
15
  import type { AiUsageOverview } from '@/server/admin-ai-usage'
@@ -17,37 +17,37 @@
17
17
 
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useState, useEffect } from 'react'
20
+ import { useState } from 'react'
21
21
  import { AlertTriangle, RefreshCw, Trash2 } from 'lucide-react'
22
22
  import { PageHeader } from '@/components/common/page-header'
23
23
  import { ErrorList } from '@/components/monitoring/error-list'
24
+ import { ErrorItem } from '@/components/monitoring/error-list'
24
25
  import { getErrors } from '@/server/serverFns/monitoring/get-errors'
25
26
  import { resetMonitoring } from '@/server/serverFns/monitoring/reset'
26
- import { toast } from '@/components/ui/toast'
27
+ import { jsonify } from '@/lib/loader-serial'
28
+ import { toast } from '@allbluecn/ui/toast'
27
29
 
28
30
  export const Route = createLazyFileRoute('/_nav/monitoring/errors')({
29
31
  component: MonitoringErrors,
30
32
  })
31
33
 
32
34
  function MonitoringErrors() {
33
- const [loading, setLoading] = useState(true)
34
- const [data, setData] = useState<any>(null)
35
+ const initial = Route.useLoaderData()
36
+ const [loading, setLoading] = useState(false)
37
+ const [data, setData] = useState(initial)
35
38
 
36
39
  const loadData = async () => {
37
40
  setLoading(true)
38
41
  try {
39
42
  const result = await getErrors()
40
- setData(result)
43
+ // Date → ISO 字符串,与 loader 预取形态对齐
44
+ setData(jsonify(result))
41
45
  } catch {
42
46
  // ignore
43
47
  }
44
48
  setLoading(false)
45
49
  }
46
50
 
47
- useEffect(() => {
48
- loadData()
49
- }, [])
50
-
51
51
  const handleReset = async () => {
52
52
  try {
53
53
  await resetMonitoring({ data: { type: 'errors' } })
@@ -101,7 +101,7 @@ function MonitoringErrors() {
101
101
  <RefreshCw className="size-5 animate-spin text-muted-foreground" />
102
102
  </div>
103
103
  ) : data?.errors?.length > 0 ? (
104
- <ErrorList errors={data.errors} />
104
+ <ErrorList errors={data.errors as ErrorItem[]} />
105
105
  ) : (
106
106
  <div className="flex items-center justify-center h-32 text-sm text-muted-foreground">{m.common_monitoring_errors_empty()}</div>
107
107
  )}
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getErrors } from '@/server/serverFns/monitoring/get-errors'
3
+ import { jsonify } from '@/lib/loader-serial'
4
+
5
+ export const Route = createFileRoute('/_nav/monitoring/errors')({
6
+ loader: async () => jsonify(await getErrors()),
7
+ })
@@ -18,32 +18,29 @@
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import { useState, useEffect, useRef } from 'react'
21
- import { formatTimestamp } from '@allbluecn/web-app/lib/datetime'
21
+ import { formatTimestamp } from '@allbluecn/ui/lib/datetime'
22
22
  import { HeartPulse, Play, Square, RefreshCw } from 'lucide-react'
23
23
  import { PageHeader } from '@/components/common/page-header'
24
24
  import { HealthCards } from '@/components/monitoring/health-cards'
25
- import { DataTable } from '@/components/data-table/data-table'
25
+ import { DataTable } from '@allbluecn/ui/data-table'
26
26
  import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
27
27
  import { getHealthCheckSettings, updateHealthCheckSettings } from '@/server/serverFns/monitoring/get-health-settings'
28
- import { toast } from '@/components/ui/toast'
28
+ import { toast } from '@allbluecn/ui/toast'
29
+ import { jsonify } from '@/lib/loader-serial'
29
30
 
30
31
  export const Route = createLazyFileRoute('/_nav/monitoring/health')({
31
32
  component: MonitoringHealth,
32
33
  })
33
34
 
34
35
  function MonitoringHealth() {
35
- const [loading, setLoading] = useState(true)
36
- const [data, setData] = useState<any>(null)
37
- const [autoEnabled, setAutoEnabled] = useState(true)
38
- const [autoLoading, setAutoLoading] = useState(true)
36
+ const initial = Route.useLoaderData()
37
+ const [loading, setLoading] = useState(false)
38
+ const [data, setData] = useState(initial.data)
39
+ const [autoEnabled, setAutoEnabled] = useState(initial.settings.enabled)
40
+ const [autoLoading, setAutoLoading] = useState(false)
39
41
  const [countdown, setCountdown] = useState(30)
40
42
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
41
43
 
42
- useEffect(() => {
43
- loadHealth()
44
- loadSettings()
45
- }, [])
46
-
47
44
  useEffect(() => {
48
45
  if (timerRef.current) clearInterval(timerRef.current)
49
46
  if (autoEnabled) {
@@ -64,7 +61,8 @@ function MonitoringHealth() {
64
61
  setLoading(true)
65
62
  try {
66
63
  const result = await getHealthStatus()
67
- setData(result)
64
+ // Date → ISO 字符串,与 loader 预取形态对齐
65
+ setData(jsonify(result))
68
66
  } catch {
69
67
  // ignore
70
68
  }
@@ -158,7 +156,7 @@ function MonitoringHealth() {
158
156
  <p className="text-sm font-medium">{m.common_monitoring_health_database_title()}</p>
159
157
  <p className="text-xs text-muted-foreground mt-1">
160
158
  {data.current.checks.database.status === 'connected'
161
- ? m.common_monitoring_health_database_connected({ latency: data.current.checks.database.latency })
159
+ ? m.common_monitoring_health_database_connected({ latency: data.current.checks.database.latency! })
162
160
  : `${m.common_monitoring_health_database_failed()}: ${data.current.checks.database.error || m.common_monitoring_health_database_unknownError()}`}
163
161
  </p>
164
162
  </div>
@@ -178,7 +176,7 @@ function MonitoringHealth() {
178
176
  <h3 className="text-sm font-medium">{m.common_monitoring_health_history()}</h3>
179
177
  </div>
180
178
  <div className="p-4 flex-1 min-h-0">
181
- <HistoryTable data={data.recent} />
179
+ <HistoryTable data={data.recent as HealthHistoryRow[]} />
182
180
  </div>
183
181
  </div>
184
182
  )}
@@ -0,0 +1,14 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
3
+ import { getHealthCheckSettings } from '@/server/serverFns/monitoring/get-health-settings'
4
+ import { jsonify } from '@/lib/loader-serial'
5
+
6
+ export const Route = createFileRoute('/_nav/monitoring/health')({
7
+ loader: async () => {
8
+ const [data, settings] = await Promise.all([
9
+ getHealthStatus(),
10
+ getHealthCheckSettings(),
11
+ ])
12
+ return jsonify({ data, settings })
13
+ },
14
+ })
@@ -17,24 +17,28 @@
17
17
 
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useState, useEffect } from 'react'
20
+ import { useState, useEffect, useRef } from 'react'
21
21
  import { Activity, Filter, RefreshCw, Trash2 } from 'lucide-react'
22
22
  import { PageHeader } from '@/components/common/page-header'
23
23
  import { RequestLogTable } from '@/components/monitoring/request-log-table'
24
- import { Checkbox } from '@/components/ui/checkbox'
24
+ import { Checkbox } from '@allbluecn/ui/checkbox'
25
25
  import { getRequestLogs } from '@/server/serverFns/monitoring/get-request-logs'
26
26
  import { resetMonitoring } from '@/server/serverFns/monitoring/reset'
27
- import { DataTablePagination } from '@/components/data-table/data-table-pagination'
28
- import { toast } from '@/components/ui/toast'
27
+ import { DataTablePagination } from '@allbluecn/ui/data-table/pagination'
28
+ import { toast } from '@allbluecn/ui/toast'
29
+ import { jsonify } from '@/lib/loader-serial'
30
+ import { RequestLogEntry } from '@/components/monitoring/request-log-table'
29
31
 
30
32
  export const Route = createLazyFileRoute('/_nav/monitoring/logs')({
31
33
  component: MonitoringLogs,
32
34
  })
33
35
 
34
36
  function MonitoringLogs() {
35
- const [loading, setLoading] = useState(true)
36
- const [logs, setLogs] = useState<any[]>([])
37
- const [total, setTotal] = useState(0)
37
+ const initial = Route.useLoaderData()
38
+ const skipFirstLoadRef = useRef(true)
39
+ const [loading, setLoading] = useState(false)
40
+ const [logs, setLogs] = useState(initial.logs)
41
+ const [total, setTotal] = useState(initial.total)
38
42
  const [page, setPage] = useState(1)
39
43
  const [method, setMethod] = useState('')
40
44
  const [status, setStatus] = useState('')
@@ -55,7 +59,8 @@ function MonitoringLogs() {
55
59
  excludeMonitoring: filterMonitoring,
56
60
  },
57
61
  })
58
- setLogs(result.logs)
62
+ // Date → ISO 字符串,与 loader 预取形态对齐
63
+ setLogs(jsonify(result.logs))
59
64
  setTotal(result.total)
60
65
  } catch {
61
66
  // ignore
@@ -64,6 +69,11 @@ function MonitoringLogs() {
64
69
  }
65
70
 
66
71
  useEffect(() => {
72
+ // 初始数据已由 route.tsx loader 预取(与 loader 同参),跳过挂载即重复请求
73
+ if (skipFirstLoadRef.current) {
74
+ skipFirstLoadRef.current = false
75
+ return
76
+ }
67
77
  loadLogs()
68
78
  }, [page, method, status, path, filterMonitoring])
69
79
 
@@ -145,13 +155,18 @@ function MonitoringLogs() {
145
155
  ) : logs.length > 0 ? (
146
156
  <div className="flex-1 min-h-0 flex flex-col">
147
157
  <RequestLogTable
148
- logs={logs}
158
+ logs={logs as RequestLogEntry[]}
149
159
  actions={
150
160
  <DataTablePagination
151
161
  page={page}
152
162
  totalPages={Math.ceil(total / limit)}
153
163
  total={total}
154
164
  onPageChange={setPage}
165
+ labels={{
166
+ total: m.common_pagination_total({ total, page, totalPages: Math.ceil(total / limit) }),
167
+ previous: m.common_pagination_prev(),
168
+ next: m.common_pagination_next(),
169
+ }}
155
170
  />
156
171
  }
157
172
  />
@@ -0,0 +1,13 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getRequestLogs } from '@/server/serverFns/monitoring/get-request-logs'
3
+ import { jsonify } from '@/lib/loader-serial'
4
+
5
+ export const Route = createFileRoute('/_nav/monitoring/logs')({
6
+ loader: async () => {
7
+ const result = await getRequestLogs({
8
+ data: { limit: 50, offset: 0, excludeMonitoring: false },
9
+ })
10
+ // Date → ISO 字符串:与客户端 RPC 返回形态对齐(组件按 string 时间戳渲染)
11
+ return jsonify(result)
12
+ },
13
+ })
@@ -15,20 +15,20 @@
15
15
  // You should have received a copy of the GNU Affero General Public License
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
- import { describeMetric } from '@/lib/monitoring-descriptions'
19
18
  import { m } from '@/paraglide/messages.js'
20
19
  import { getBenchmark, getBenchmarkSource, compareDuration } from '@/lib/monitoring-benchmarks'
21
- import { formatTimestamp } from '@allbluecn/web-app/lib/datetime'
20
+ import { formatTimestamp } from '@allbluecn/ui/lib/datetime'
22
21
  import { createLazyFileRoute } from '@tanstack/react-router'
23
- import { useState, useEffect } from 'react'
22
+ import { useState } from 'react'
24
23
  import { BarChart3, RefreshCw, Trash2, MemoryStick } from 'lucide-react'
25
24
  import { ArrowDown, ArrowUp } from 'lucide-react'
26
25
  import { PageHeader } from '@/components/common/page-header'
27
26
  import { MetricsStats } from '@/components/monitoring/metrics-stats'
28
27
  import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
29
28
  import { resetMonitoring } from '@/server/serverFns/monitoring/reset'
30
- import { DataTable } from '@/components/data-table/data-table'
31
- import { toast } from '@/components/ui/toast'
29
+ import { jsonify } from '@/lib/loader-serial'
30
+ import { DataTable } from '@allbluecn/ui/data-table'
31
+ import { toast } from '@allbluecn/ui/toast'
32
32
 
33
33
  export const Route = createLazyFileRoute('/_nav/monitoring/metrics')({
34
34
  component: MonitoringMetrics,
@@ -41,24 +41,22 @@ function formatBytes(bytes: number): string {
41
41
  }
42
42
 
43
43
  function MonitoringMetrics() {
44
- const [loading, setLoading] = useState(true)
45
- const [data, setData] = useState<any>(null)
44
+ const initial = Route.useLoaderData()
45
+ const [loading, setLoading] = useState(false)
46
+ const [data, setData] = useState(initial)
46
47
 
47
48
  const loadData = async () => {
48
49
  setLoading(true)
49
50
  try {
50
51
  const result = await getMetrics()
51
- setData(result)
52
+ // Date → ISO 字符串,与 loader 预取形态对齐
53
+ setData(jsonify(result))
52
54
  } catch {
53
55
  // ignore
54
56
  }
55
57
  setLoading(false)
56
58
  }
57
59
 
58
- useEffect(() => {
59
- loadData()
60
- }, [])
61
-
62
60
  const handleReset = async () => {
63
61
  try {
64
62
  await resetMonitoring({ data: { type: 'metrics' } })
@@ -133,7 +131,7 @@ function MonitoringMetrics() {
133
131
  <h3 className="text-sm font-medium">{m.common_monitoring_metrics_history()}</h3>
134
132
  </div>
135
133
  <div className="p-4 flex-1 min-h-0">
136
- <MetricsHistoryTable data={data.history} />
134
+ <MetricsHistoryTable data={data.history as MetricsHistoryRow[]} />
137
135
  </div>
138
136
  </div>
139
137
  )}
@@ -141,7 +139,7 @@ function MonitoringMetrics() {
141
139
  )
142
140
  }
143
141
 
144
- interface MetricsHistoryRow {
142
+ export interface MetricsHistoryRow {
145
143
  metricName: string
146
144
  count: number
147
145
  avg: number
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
3
+ import { jsonify } from '@/lib/loader-serial'
4
+
5
+ export const Route = createFileRoute('/_nav/monitoring/metrics')({
6
+ loader: async () => jsonify(await getMetrics()),
7
+ })
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useState, useEffect } from 'react'
20
+ import { useState } from 'react'
21
21
  import { Activity, RefreshCw } from 'lucide-react'
22
22
  import { PageHeader } from '@/components/common/page-header'
23
23
  import { HealthCards } from '@/components/monitoring/health-cards'
@@ -26,16 +26,19 @@ import { MetricsStats } from '@/components/monitoring/metrics-stats'
26
26
  import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
27
27
  import { getErrors } from '@/server/serverFns/monitoring/get-errors'
28
28
  import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
29
+ import { jsonify } from '@/lib/loader-serial'
30
+ import { ErrorItem } from '@/components/monitoring/error-list'
29
31
 
30
32
  export const Route = createLazyFileRoute('/_nav/monitoring/overview')({
31
33
  component: MonitoringOverview,
32
34
  })
33
35
 
34
36
  function MonitoringOverview() {
35
- const [loading, setLoading] = useState(true)
36
- const [health, setHealth] = useState<any>(null)
37
- const [errors, setErrors] = useState<any>(null)
38
- const [metrics, setMetrics] = useState<any>(null)
37
+ const initial = Route.useLoaderData()
38
+ const [loading, setLoading] = useState(false)
39
+ const [health, setHealth] = useState(initial.health)
40
+ const [errors, setErrors] = useState(initial.errors)
41
+ const [metrics, setMetrics] = useState(initial.metrics)
39
42
 
40
43
  const loadData = async () => {
41
44
  setLoading(true)
@@ -45,19 +48,17 @@ function MonitoringOverview() {
45
48
  getErrors(),
46
49
  getMetrics(),
47
50
  ])
48
- setHealth(h)
49
- setErrors(e)
50
- setMetrics(m)
51
+ // Date → ISO 字符串,与 loader 预取形态对齐
52
+ const j = jsonify({ h, e, m })
53
+ setHealth(j.h)
54
+ setErrors(j.e)
55
+ setMetrics(j.m)
51
56
  } catch {
52
57
  // ignore
53
58
  }
54
59
  setLoading(false)
55
60
  }
56
61
 
57
- useEffect(() => {
58
- loadData()
59
- }, [])
60
-
61
62
  if (loading) {
62
63
  return (
63
64
  <div className="flex items-center justify-center h-64">
@@ -112,7 +113,7 @@ function MonitoringOverview() {
112
113
  </div>
113
114
  <div className="p-4">
114
115
  {errors?.errors?.slice(0, 5).length > 0 ? (
115
- <ErrorList errors={errors.errors.slice(0, 5)} />
116
+ <ErrorList errors={errors.errors.slice(0, 5) as ErrorItem[]} />
116
117
  ) : (
117
118
  <p className="text-sm text-muted-foreground text-center py-4">{m.common_monitoring_overview_recentErrors_empty()}</p>
118
119
  )}
@@ -0,0 +1,17 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
3
+ import { getErrors } from '@/server/serverFns/monitoring/get-errors'
4
+ import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
5
+ import { jsonify } from '@/lib/loader-serial'
6
+
7
+ export const Route = createFileRoute('/_nav/monitoring/overview')({
8
+ loader: async () => {
9
+ const [health, errors, metrics] = await Promise.all([
10
+ getHealthStatus(),
11
+ getErrors(),
12
+ getMetrics(),
13
+ ])
14
+ // Date → ISO 字符串:与客户端 RPC 返回形态对齐(组件按 string 时间戳渲染)
15
+ return jsonify({ health, errors, metrics })
16
+ },
17
+ })
@@ -15,10 +15,10 @@
15
15
  // You should have received a copy of the GNU Affero General Public License
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
- import { createLazyFileRoute, Outlet, useLocation } from '@tanstack/react-router'
18
+ import { createLazyFileRoute, Link, Outlet, useLocation } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { SidebarTrigger } from '@/components/ui/sidebar'
21
- import { Separator } from '@/components/ui/separator'
20
+ import { SidebarTrigger } from '@allbluecn/ui/sidebar'
21
+ import { Separator } from '@allbluecn/ui/separator'
22
22
 
23
23
  export const Route = createLazyFileRoute('/_nav/monitoring')({
24
24
  component: MonitoringLayout,
@@ -46,16 +46,16 @@ function MonitoringLayout() {
46
46
  <Separator orientation="vertical" className="mr-2 h-4" />
47
47
  <nav className="flex gap-1">
48
48
  {subNavItems.map((item) => (
49
- <a
49
+ <Link
50
50
  key={item.id}
51
- href={item.path}
51
+ to={item.path}
52
52
  className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${currentId === item.id
53
53
  ? 'bg-accent text-accent-foreground'
54
54
  : 'text-muted-foreground hover:text-foreground hover:bg-accent/50'
55
55
  }`}
56
56
  >
57
57
  {item.label}
58
- </a>
58
+ </Link>
59
59
  ))}
60
60
  </nav>
61
61
  </header>
@@ -18,10 +18,10 @@
18
18
  import { createLazyFileRoute, useRouter } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import * as Sentry from '@sentry/tanstackstart-react'
21
- import { BugPlay, ExternalLink, Bug, Activity, AlertTriangle, Eye, EyeOff, Save, RotateCcw, CheckCircle2, XCircle, RefreshCw, ShieldCheck } from 'lucide-react'
21
+ import { BugPlay, ExternalLink, Bug, Activity, Eye, EyeOff, Save, CheckCircle2, XCircle, RefreshCw, ShieldCheck } from 'lucide-react'
22
22
  import { IconBrandSentry } from '@tabler/icons-react';
23
23
  import { useEffect, useState } from 'react'
24
- import { toast } from '@/components/ui/toast'
24
+ import { toast } from '@allbluecn/ui/toast'
25
25
  import { getSentryConfig, saveSentryConfig, reconfigureSentryServer } from '@/server/serverFns/sentry/config'
26
26
  import type { SentryConfig } from '@/server/serverFns/sentry/config'
27
27