@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.
- package/package.json +19 -17
- package/src/components/admin/admin-user-menu.tsx +4 -4
- package/src/components/admin/developer-log.tsx +16 -32
- package/src/components/ai-memory/memory-facts-panel.tsx +30 -70
- package/src/components/ai-prompts/preview-panel.tsx +1 -1
- package/src/components/ai-prompts/prompt-editor.tsx +8 -9
- package/src/components/ai-prompts/prompt-table.tsx +6 -6
- package/src/components/ai-prompts/section-editor.tsx +3 -3
- package/src/components/ai-prompts/variable-panel.tsx +2 -2
- package/src/components/ai-prompts/version-history.tsx +21 -41
- package/src/components/ai-provider/model-list-editor.tsx +2 -3
- package/src/components/ai-provider/provider-form.tsx +7 -7
- package/src/components/ai-provider/provider-table.tsx +17 -40
- package/src/components/auth/admin-login-form.tsx +7 -7
- package/src/components/auth/icon-input.tsx +2 -2
- package/src/components/auth/password-input.tsx +2 -2
- package/src/components/component/component-form-dialog.tsx +10 -18
- package/src/components/component/component-import-dialog.tsx +6 -6
- package/src/components/component/component-preview.tsx +311 -0
- package/src/components/component/component-table.tsx +3 -3
- package/src/components/dependency/changelog-dialog.tsx +2 -2
- package/src/components/dependency/columns.tsx +3 -3
- package/src/components/dependency/dependency-table.tsx +7 -7
- package/src/components/dependency/edit-dialog.tsx +8 -8
- package/src/components/invitations/invitations-page.tsx +26 -49
- package/src/components/knowledge-public/bookmark-form-dialog.tsx +6 -6
- package/src/components/knowledge-public/compliance-form-dialog.tsx +6 -6
- package/src/components/knowledge-public/legal-form-dialog.tsx +7 -7
- package/src/components/layout/sidebar-02/app-sidebar.tsx +1 -2
- package/src/components/layout/sidebar-02/index.tsx +1 -1
- package/src/components/layout/sidebar-02/nav-main.tsx +2 -3
- package/src/components/layout/sidebar-02/nav-notifications.tsx +3 -3
- package/src/components/market/columns.tsx +4 -4
- package/src/components/market/market-app-form.tsx +6 -7
- package/src/components/market/market-app-list.tsx +14 -29
- package/src/components/monitoring/error-list.tsx +2 -2
- package/src/components/monitoring/metrics-stats.tsx +1 -1
- package/src/components/monitoring/request-log-table.tsx +2 -2
- package/src/components/oauth-providers/provider-page.tsx +20 -38
- package/src/components/permissions/assign-dialog.tsx +135 -0
- package/src/components/permissions/create-role-dialog.tsx +135 -0
- package/src/components/permissions/types.ts +49 -0
- package/src/components/user/columns.tsx +6 -6
- package/src/components/user/settings-activity-log.tsx +7 -7
- package/src/components/user/settings-profile/avatar-uploader.tsx +118 -0
- package/src/components/user/settings-profile/social-links.tsx +63 -0
- package/src/components/user/settings-profile.tsx +26 -125
- package/src/components/user/user-create-dialog.tsx +6 -6
- package/src/components/user/user-detail/error-boundary.tsx +19 -0
- package/src/components/user/user-detail/user-detail-dialogs.tsx +203 -0
- package/src/components/user/user-filters.tsx +4 -4
- package/src/lib/auth/page-gate.ts +28 -0
- package/src/lib/auth.config.ts +25 -9
- package/src/lib/i18n/detect.ts +1 -1
- package/src/lib/loader-serial.ts +14 -0
- package/src/providers/index.tsx +1 -1
- package/src/routeTree.gen.ts +67 -78
- package/src/routes/__root.tsx +4 -4
- package/src/routes/_nav/audit/route.lazy.tsx +1 -2
- package/src/routes/_nav/compliance/route.lazy.tsx +5 -3
- package/src/routes/_nav/components/route.lazy.tsx +14 -34
- package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +18 -38
- package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +18 -38
- package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +6 -6
- package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +3 -3
- package/src/routes/_nav/licenses/new/route.lazy.tsx +5 -5
- package/src/routes/_nav/licenses/route.lazy.tsx +1 -1
- package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +2 -2
- package/src/routes/_nav/monitoring/errors/route.lazy.tsx +10 -10
- package/src/routes/_nav/monitoring/errors/route.tsx +7 -0
- package/src/routes/_nav/monitoring/health/route.lazy.tsx +13 -15
- package/src/routes/_nav/monitoring/health/route.tsx +14 -0
- package/src/routes/_nav/monitoring/logs/route.lazy.tsx +24 -9
- package/src/routes/_nav/monitoring/logs/route.tsx +13 -0
- package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +12 -14
- package/src/routes/_nav/monitoring/metrics/route.tsx +7 -0
- package/src/routes/_nav/monitoring/overview/route.lazy.tsx +14 -13
- package/src/routes/_nav/monitoring/overview/route.tsx +17 -0
- package/src/routes/_nav/monitoring/route.lazy.tsx +6 -6
- package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +2 -2
- package/src/routes/_nav/permissions/route.lazy.tsx +21 -311
- package/src/routes/_nav/permissions/route.tsx +1 -1
- package/src/routes/_nav/route.tsx +5 -13
- package/src/routes/_nav/sso/route.lazy.tsx +2 -3
- package/src/routes/_nav/users/$userId/route.lazy.tsx +91 -156
- package/src/routes/_nav/users/-components/users-table.tsx +1 -1
- package/src/routes/_nav/users/route.lazy.tsx +1 -1
- package/src/routes/login/route.lazy.tsx +1 -1
- package/src/routes/setup/route.lazy.tsx +2 -2
- package/src/server/__tests__/admin-billing.test.ts +191 -16
- package/src/server/__tests__/compliance.test.ts +44 -7
- package/src/server/__tests__/permissions.test.ts +10 -5
- package/src/server/__tests__/prompt-defaults.test.ts +3 -3
- package/src/server/admin-ai-alerts.ts +2 -1
- package/src/server/admin-ai-usage.ts +2 -1
- package/src/server/ai-memory/store.ts +6 -2
- package/src/server/{serverFns/ai-prompts → ai-prompts}/prompt-defaults.ts +5 -3
- package/src/server/{serverFns/dependency → dependency}/helpers.ts +4 -3
- package/src/server/knowledge-public/github-sync.ts +2 -1
- package/src/server/license-issue.ts +2 -1
- package/src/server/middlewares/auth-rate-limit.ts +117 -0
- package/src/server/observability/health-check.ts +4 -1
- package/src/server/observability/metrics.ts +17 -15
- package/src/server/observability/request-middleware.ts +2 -1
- package/src/server/observability/timing.ts +14 -16
- package/src/server/serverFns/admin-billing.ts +107 -12
- package/src/server/serverFns/ai-memory/list-users.ts +2 -1
- package/src/server/serverFns/ai-prompts/activate.ts +2 -1
- package/src/server/serverFns/ai-prompts/get.ts +3 -2
- package/src/server/serverFns/ai-prompts/list.ts +3 -2
- package/src/server/serverFns/ai-prompts/rollback.ts +3 -1
- package/src/server/serverFns/ai-prompts/save.ts +5 -3
- package/src/server/serverFns/ai-provider/create.ts +4 -1
- package/src/server/serverFns/ai-provider/delete.ts +4 -1
- package/src/server/serverFns/ai-provider/get.ts +4 -1
- package/src/server/serverFns/ai-provider/list.ts +28 -1
- package/src/server/serverFns/ai-provider/seed-from-seed.ts +2 -1
- package/src/server/serverFns/ai-provider/update.ts +4 -1
- package/src/server/serverFns/anyhub/market.ts +8 -1
- package/src/server/serverFns/audit.ts +14 -1
- package/src/server/serverFns/auth/setup.ts +3 -1
- package/src/server/serverFns/compliance.ts +32 -3
- package/src/server/serverFns/components/import.ts +69 -14
- package/src/server/serverFns/components/list.ts +4 -1
- package/src/server/serverFns/components/mutations.ts +9 -1
- package/src/server/serverFns/dependency/list.ts +28 -5
- package/src/server/serverFns/dependency/sync.ts +7 -2
- package/src/server/serverFns/dependency/update.ts +4 -1
- package/src/server/serverFns/developer-log-auto-generate.ts +63 -32
- package/src/server/serverFns/developer-logs.ts +5 -1
- package/src/server/serverFns/invitations.ts +7 -1
- package/src/server/serverFns/knowledge-public/bookmarks/create.ts +2 -1
- package/src/server/serverFns/knowledge-public/bookmarks/delete.ts +2 -1
- package/src/server/serverFns/knowledge-public/bookmarks/detail.ts +2 -1
- package/src/server/serverFns/knowledge-public/bookmarks/list.ts +2 -1
- package/src/server/serverFns/knowledge-public/bookmarks/sync-all-github.ts +2 -1
- package/src/server/serverFns/knowledge-public/bookmarks/update.ts +2 -1
- package/src/server/serverFns/knowledge-public/compliance/create.ts +2 -1
- package/src/server/serverFns/knowledge-public/compliance/delete.ts +2 -1
- package/src/server/serverFns/knowledge-public/compliance/detail.ts +2 -1
- package/src/server/serverFns/knowledge-public/compliance/list.ts +2 -1
- package/src/server/serverFns/knowledge-public/compliance/update.ts +2 -1
- package/src/server/serverFns/knowledge-public/legal/create.ts +2 -1
- package/src/server/serverFns/knowledge-public/legal/delete.ts +2 -1
- package/src/server/serverFns/knowledge-public/legal/detail.ts +2 -1
- package/src/server/serverFns/knowledge-public/legal/list.ts +2 -1
- package/src/server/serverFns/knowledge-public/legal/update.ts +2 -1
- package/src/server/serverFns/licenses.ts +8 -2
- package/src/server/serverFns/monitoring/get-errors.ts +2 -1
- package/src/server/serverFns/monitoring/get-health.ts +2 -1
- package/src/server/serverFns/monitoring/get-metrics.ts +2 -1
- package/src/server/serverFns/monitoring/get-request-logs.ts +2 -1
- package/src/server/serverFns/monitoring/report-error.ts +2 -1
- package/src/server/serverFns/monitoring/reset.ts +2 -1
- package/src/server/serverFns/oauth-providers.ts +4 -1
- package/src/server/serverFns/permissions.ts +18 -11
- package/src/server/serverFns/sentry/config.ts +11 -8
- package/src/server/serverFns/sso-config.ts +17 -2
- package/src/server/serverFns/user/create.ts +6 -1
- package/src/server/serverFns/user/delete.ts +6 -1
- package/src/server/serverFns/user/detail.ts +5 -2
- package/src/server/serverFns/user/list.ts +19 -4
- package/src/server/serverFns/user/reset-password.ts +10 -2
- package/src/server/serverFns/user/update-role.ts +10 -2
- package/src/server/serverFns/user/update-status.ts +10 -2
- package/src/styles/globals.css +4 -0
- package/src/components/data-table/data-table-pagination.tsx +0 -112
- package/src/components/data-table/data-table.tsx +0 -218
- package/src/components/data-table/table-detail-layout.tsx +0 -98
- package/src/components/ui/alert-dialog.tsx +0 -187
- package/src/components/ui/alert.tsx +0 -75
- package/src/components/ui/avatar.tsx +0 -106
- package/src/components/ui/badge.tsx +0 -51
- package/src/components/ui/button.tsx +0 -57
- package/src/components/ui/card.tsx +0 -102
- package/src/components/ui/checkbox.tsx +0 -28
- package/src/components/ui/collapsible.tsx +0 -19
- package/src/components/ui/dialog.tsx +0 -155
- package/src/components/ui/dropdown-menu.tsx +0 -267
- package/src/components/ui/field.tsx +0 -236
- package/src/components/ui/input-group.tsx +0 -158
- package/src/components/ui/input.tsx +0 -19
- package/src/components/ui/label.tsx +0 -17
- package/src/components/ui/pagination.tsx +0 -130
- package/src/components/ui/select.tsx +0 -234
- package/src/components/ui/separator.tsx +0 -22
- package/src/components/ui/sheet.tsx +0 -135
- package/src/components/ui/sidebar.tsx +0 -721
- package/src/components/ui/skeleton.tsx +0 -13
- package/src/components/ui/switch.tsx +0 -29
- package/src/components/ui/table.tsx +0 -115
- package/src/components/ui/textarea.tsx +0 -17
- package/src/components/ui/toast.tsx +0 -282
- package/src/components/ui/tooltip.tsx +0 -63
- package/src/hooks/use-mobile.ts +0 -19
- package/src/lib/avatar.ts +0 -41
- /package/src/routes/{api.auth.$.ts → api/auth/$.ts} +0 -0
- /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 '
|
|
26
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '
|
|
27
|
-
import { Input } from '
|
|
28
|
-
import { Label } from '
|
|
29
|
-
import { Alert, AlertDescription, AlertTitle } from '
|
|
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 '
|
|
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 '
|
|
12
|
-
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '
|
|
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
|
|
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 {
|
|
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
|
|
34
|
-
const [
|
|
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
|
-
|
|
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/
|
|
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 '
|
|
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 '
|
|
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
|
|
36
|
-
const [
|
|
37
|
-
const [
|
|
38
|
-
const [
|
|
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
|
-
|
|
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 '
|
|
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 '
|
|
28
|
-
import { toast } from '
|
|
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
|
|
36
|
-
const
|
|
37
|
-
const [
|
|
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
|
-
|
|
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/
|
|
20
|
+
import { formatTimestamp } from '@allbluecn/ui/lib/datetime'
|
|
22
21
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
|
23
|
-
import { useState
|
|
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 {
|
|
31
|
-
import {
|
|
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
|
|
45
|
-
const [
|
|
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
|
-
|
|
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
|
|
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
|
|
36
|
-
const [
|
|
37
|
-
const [
|
|
38
|
-
const [
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 '
|
|
21
|
-
import { Separator } from '
|
|
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
|
-
<
|
|
49
|
+
<Link
|
|
50
50
|
key={item.id}
|
|
51
|
-
|
|
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
|
-
</
|
|
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,
|
|
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 '
|
|
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
|
|