这是第一次基于http域名成功的部署版本0718v1,修改了http与https都兼容的认证方式
This commit is contained in:
+2
-1
@@ -1,4 +1,5 @@
|
||||
import { normalizeError } from '../errors'
|
||||
import { createIdempotencyKey } from '../idempotencyKey'
|
||||
import { logger } from '../logging'
|
||||
import { tokenSchema, type AuthSession } from '../schemas/auth'
|
||||
import { activity } from './activity'
|
||||
@@ -15,7 +16,7 @@ type PendingRefresh = { refreshToken: string; idempotencyKey: string }
|
||||
const pendingFor = (refreshToken: string): PendingRefresh => {
|
||||
const saved = JSON.parse(localStorage.getItem(pendingKey) || 'null') as PendingRefresh | null
|
||||
if (saved?.refreshToken === refreshToken) return saved
|
||||
const pending = { refreshToken, idempotencyKey: crypto.randomUUID() }
|
||||
const pending = { refreshToken, idempotencyKey: createIdempotencyKey() }
|
||||
localStorage.setItem(pendingKey, JSON.stringify(pending))
|
||||
return pending
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export function createIdempotencyKey(): string {
|
||||
const source = globalThis.crypto
|
||||
if (typeof source?.randomUUID === 'function') return source.randomUUID()
|
||||
if (typeof source?.getRandomValues !== 'function') throw new Error('当前浏览器不支持安全随机数')
|
||||
|
||||
const bytes = source.getRandomValues(new Uint8Array(16))
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
||||
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')
|
||||
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Alert, Button, Card, Empty, Input, Modal, Progress, Select, Steps, Tabs, Tag, message } from 'antd'
|
||||
@@ -26,9 +26,7 @@ export function RunDetailPage() {
|
||||
useFinalResultsSync(Boolean(run.data?.terminal), results.refetch)
|
||||
const report = useQuery({ queryKey: ['report', id], queryFn: () => services.reports.get(id), enabled: Boolean(run.data?.terminal) })
|
||||
const refresh = () => void queryClient.invalidateQueries({ queryKey: ['run', id] })
|
||||
useEffect(() => {
|
||||
if (retryingExecutionId && run.data?.terminal) setRetryingExecutionId(null)
|
||||
}, [retryingExecutionId, run.data?.terminal])
|
||||
const activeRetryingExecutionId = run.data?.terminal ? null : retryingExecutionId
|
||||
const retryResult = useMutation({
|
||||
mutationFn: (executionId: string) => services.runs.retryResult(id, executionId),
|
||||
onSuccess: (response) => {
|
||||
@@ -85,7 +83,7 @@ export function RunDetailPage() {
|
||||
<div className="stat-strip"><span><strong>{data.processed_count}</strong>已处理</span><span><strong>{data.completed_count}</strong>已完成</span><span><strong>{data.error_count}</strong>执行错误</span><span><strong>{data.selected_count}</strong>总样例</span></div>
|
||||
</Card>
|
||||
<Tabs items={[
|
||||
{ key: 'results', label: `逐条结果 (${filtered.length} / ${results.data?.length || 0})`, children: <ResultsPanel rows={filtered} executionFilter={executionFilter} verdictFilter={verdictFilter} executionIdQuery={executionIdQuery} onExecutionFilter={setExecutionFilter} onVerdictFilter={setVerdictFilter} onExecutionIdQuery={setExecutionIdQuery} loading={results.isLoading} canRetry={['completed', 'completed_with_errors'].includes(data.status)} retryingExecutionId={retryingExecutionId} followedExecutionId={followedExecutionId} submittingExecutionId={retryResult.isPending ? retryResult.variables : null} onStopFollowing={() => setFollowedExecutionId(null)} onRetry={(executionId) => Modal.confirm({ title: `确定重新执行 ${executionId} 吗?`, content: '当前测试结果将被新结果替换。', onOk: () => retryResult.mutate(executionId) })} /> },
|
||||
{ key: 'results', label: `逐条结果 (${filtered.length} / ${results.data?.length || 0})`, children: <ResultsPanel rows={filtered} executionFilter={executionFilter} verdictFilter={verdictFilter} executionIdQuery={executionIdQuery} onExecutionFilter={setExecutionFilter} onVerdictFilter={setVerdictFilter} onExecutionIdQuery={setExecutionIdQuery} loading={results.isLoading} canRetry={['completed', 'completed_with_errors'].includes(data.status)} retryingExecutionId={activeRetryingExecutionId} followedExecutionId={followedExecutionId} submittingExecutionId={retryResult.isPending ? retryResult.variables : null} onStopFollowing={() => setFollowedExecutionId(null)} onRetry={(executionId) => Modal.confirm({ title: `确定重新执行 ${executionId} 吗?`, content: '当前测试结果将被新结果替换。', onOk: () => retryResult.mutate(executionId) })} /> },
|
||||
{ key: 'report', label: '汇总报告', children: report.data ? <ReportSummary summary={report.data.summary} /> : <Empty description={data.terminal ? '暂无报告' : '运行结束后生成报告'} /> },
|
||||
]} />
|
||||
</>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { ApiClient } from '../api/client'
|
||||
import { createIdempotencyKey } from '../idempotencyKey'
|
||||
import { resultListSchema, retryErrorsSchema, retryResultSchema, runDetailSchema, runListSchema, runResponseSchema, startRunSchema, resumeRunSchema, type StartRunInput } from '../schemas/runs'
|
||||
|
||||
const idempotencyKey = () => crypto.randomUUID()
|
||||
|
||||
export const runService = (api: ApiClient) => ({
|
||||
list: (limit = 50) => api.request(`/runs?limit=${limit}`, { schema: runListSchema }),
|
||||
get: (id: number) => api.request(`/runs/${id}`, { schema: runDetailSchema }),
|
||||
start: (input: StartRunInput, key = idempotencyKey()) =>
|
||||
start: (input: StartRunInput, key = createIdempotencyKey()) =>
|
||||
api.request('/runs', { method: 'POST', headers: { 'Idempotency-Key': key }, body: startRunSchema.parse(input), schema: runResponseSchema }),
|
||||
cancel: (id: number) => api.request(`/runs/${id}`, { method: 'PATCH', body: { status: 'cancelled' }, schema: runDetailSchema }),
|
||||
remove: (id: number) => api.request(`/runs/${id}`, { method: 'DELETE' }),
|
||||
|
||||
Reference in New Issue
Block a user