fix: add missing logs pages and @hookform/resolvers dependency for Docker build
Docker Build / Build and Push Docker Image (push) Successful in 4m5s
Docker Build / Build and Push Docker Image (push) Successful in 4m5s
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ upload
|
||||
*.db
|
||||
build
|
||||
*.db-journal
|
||||
logs
|
||||
/logs
|
||||
web/default/dist
|
||||
web/classic/dist
|
||||
web/daisy/dist
|
||||
|
||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Logs
|
||||
logs
|
||||
/logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
"name": "modelstoken-daisy",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.4.0",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@tanstack/react-query": "^5.77.0",
|
||||
"axios": "^1.9.0",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
Vendored
+1
@@ -20,6 +20,7 @@
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.56.4",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-i18next": "^15.5.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Search, Calendar, BarChart3 } from 'lucide-react'
|
||||
import PageHeader from '@/components/common/PageHeader'
|
||||
import SearchInput from '@/components/common/SearchInput'
|
||||
import DataTable, { Column } from '@/components/common/DataTable'
|
||||
import QuotaDisplay from '@/components/common/QuotaDisplay'
|
||||
import { searchSelfLogs, getSelfLogStat } from '@/api/log'
|
||||
import { formatDate } from '@/lib/utils'
|
||||
import type { Log } from '@/types/log'
|
||||
import type { LogSearchParams } from '@/api/log'
|
||||
|
||||
export default function LogList() {
|
||||
const { t } = useTranslation()
|
||||
const [search, setSearch] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(10)
|
||||
const [startDate, setStartDate] = useState('')
|
||||
const [endDate, setEndDate] = useState('')
|
||||
|
||||
const params: LogSearchParams = {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
keyword: search || undefined,
|
||||
start_time: startDate ? Math.floor(new Date(startDate).getTime() / 1000) : undefined,
|
||||
end_time: endDate ? Math.floor(new Date(endDate).getTime() / 1000) : undefined,
|
||||
}
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['logs', params],
|
||||
queryFn: () => searchSelfLogs(params),
|
||||
})
|
||||
|
||||
const { data: stat } = useQuery({
|
||||
queryKey: ['log-stat'],
|
||||
queryFn: getSelfLogStat,
|
||||
})
|
||||
|
||||
const columns: Column<Log & Record<string, unknown>>[] = [
|
||||
{
|
||||
key: 'created_at', header: t('log.time'), sortable: true, render: (row) => (
|
||||
<span className="text-xs">{formatDate(row.created_at)}</span>
|
||||
),
|
||||
},
|
||||
{ key: 'model_name', header: t('log.model'), render: (row) => <span className="text-sm font-medium">{row.model_name}</span> },
|
||||
{ key: 'token_name', header: t('log.token'), render: (row) => <span className="text-sm">{row.token_name}</span> },
|
||||
{
|
||||
key: 'prompt_tokens', header: t('log.promptTokens'), align: 'right' as const, render: (row) => (
|
||||
<span className="text-xs tabular-nums">{row.prompt_tokens?.toLocaleString() ?? '-'}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'completion_tokens', header: t('log.completionTokens'), align: 'right' as const, render: (row) => (
|
||||
<span className="text-xs tabular-nums">{row.completion_tokens?.toLocaleString() ?? '-'}</span>
|
||||
),
|
||||
},
|
||||
{ key: 'quota', header: t('log.quota'), render: (row) => <QuotaDisplay quota={row.quota} /> },
|
||||
{ key: 'channel_name', header: t('log.channel'), render: (row) => <span className="text-xs">{row.channel_name || '-'}</span> },
|
||||
{ key: 'ip', header: t('log.ip'), render: (row) => <span className="text-xs">{row.ip || '-'}</span> },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader title={t('nav.logs')} />
|
||||
|
||||
{/* Stats Summary */}
|
||||
<div className="stats stats-vertical md:stats-horizontal shadow w-full">
|
||||
<div className="stat">
|
||||
<div className="stat-figure"><BarChart3 size={24} className="text-info" /></div>
|
||||
<div className="stat-title">{t('log.totalQuota')}</div>
|
||||
<div className="stat-value text-lg">
|
||||
<QuotaDisplay quota={stat?.quota_data?.reduce((a: number, b: number) => a + b, 0) ?? 0} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="stat">
|
||||
<div className="stat-figure"><Search size={24} className="text-success" /></div>
|
||||
<div className="stat-title">{t('log.totalRequests')}</div>
|
||||
<div className="stat-value text-lg">{stat?.count_data?.reduce((a: number, b: number) => a + b, 0)?.toLocaleString() ?? 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<SearchInput value={search} onChange={(v) => { setSearch(v); setPage(1) }} />
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar size={14} className="text-base-content/50" />
|
||||
<input
|
||||
type="date"
|
||||
className="input input-bordered input-sm"
|
||||
value={startDate}
|
||||
onChange={(e) => { setStartDate(e.target.value); setPage(1) }}
|
||||
/>
|
||||
<span className="text-base-content/50">-</span>
|
||||
<input
|
||||
type="date"
|
||||
className="input input-bordered input-sm"
|
||||
value={endDate}
|
||||
onChange={(e) => { setEndDate(e.target.value); setPage(1) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="card bg-base-100 shadow">
|
||||
<div className="card-body p-0">
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={(data?.data ?? []) as (Log & Record<string, unknown>)[]}
|
||||
loading={isLoading}
|
||||
total={data?.total ?? 0}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setPage}
|
||||
onPageSizeChange={(s) => { setPageSize(s); setPage(1) }}
|
||||
rowKey={(row) => row.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import PageHeader from '@/components/common/PageHeader'
|
||||
import DataTable, { Column } from '@/components/common/DataTable'
|
||||
import { getMidjourneyLogs } from '@/api/log'
|
||||
import { formatDate } from '@/lib/utils'
|
||||
import StatusBadge from '@/components/common/StatusBadge'
|
||||
|
||||
interface MjTask {
|
||||
id: number
|
||||
user_id: number
|
||||
action: string
|
||||
mj_id: string
|
||||
prompt: string
|
||||
prompt_en: string
|
||||
description: string
|
||||
state: string
|
||||
submit_time: number
|
||||
start_time: number
|
||||
finish_time: number
|
||||
image_url: string
|
||||
status: string
|
||||
progress: string
|
||||
fail_reason: string
|
||||
}
|
||||
|
||||
const MjStatusMap: Record<string, { variant: 'success' | 'warning' | 'error' | 'neutral' | 'info'; label: string }> = {
|
||||
SUCCESS: { variant: 'success', label: 'Success' },
|
||||
IN_PROGRESS: { variant: 'info', label: 'In Progress' },
|
||||
FAILURE: { variant: 'error', label: 'Failure' },
|
||||
NOT_START: { variant: 'neutral', label: 'Not Started' },
|
||||
SUBMITTED: { variant: 'warning', label: 'Submitted' },
|
||||
}
|
||||
|
||||
export default function MidjourneyLog() {
|
||||
const { t } = useTranslation()
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(10)
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['mj-logs', page, pageSize],
|
||||
queryFn: () => getMidjourneyLogs({ page, page_size: pageSize }),
|
||||
})
|
||||
|
||||
const rows = (data?.data ?? []) as (MjTask & Record<string, unknown>)[]
|
||||
|
||||
const columns: Column<MjTask & Record<string, unknown>>[] = [
|
||||
{
|
||||
key: 'submit_time', header: t('log.time'), render: (row) => (
|
||||
<span className="text-xs">{formatDate(row.submit_time as number)}</span>
|
||||
),
|
||||
},
|
||||
{ key: 'action', header: 'Action', render: (row) => <span className="text-sm">{String(row.action ?? '-')}</span> },
|
||||
{
|
||||
key: 'prompt', header: 'Prompt', render: (row) => (
|
||||
<span className="text-xs max-w-[200px] truncate block" title={String(row.prompt ?? '')}>
|
||||
{String(row.prompt ?? '-')}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'status', header: t('common.status'), render: (row) => {
|
||||
const s = MjStatusMap[String(row.status)]
|
||||
return s ? <StatusBadge variant={s.variant} label={s.label} /> : <StatusBadge variant="neutral" label={String(row.status)} />
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'progress', header: t('mj.progress'), render: (row) => (
|
||||
<span className="text-xs">{String(row.progress ?? '-')}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'image_url', header: t('mj.image'), render: (row) => {
|
||||
const url = String(row.image_url ?? '')
|
||||
return url ? (
|
||||
<a href={url} target="_blank" rel="noopener noreferrer" className="link link-primary text-xs">{t('mj.viewImage')}</a>
|
||||
) : '-'
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'fail_reason', header: t('mj.failReason'), render: (row) => (
|
||||
<span className="text-xs text-error">{String(row.fail_reason ?? '-')}</span>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader title={t('nav.midjourney')} />
|
||||
|
||||
<div className="card bg-base-100 shadow">
|
||||
<div className="card-body p-0">
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
loading={isLoading}
|
||||
total={data?.total ?? 0}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setPage}
|
||||
onPageSizeChange={(s) => { setPageSize(s); setPage(1) }}
|
||||
rowKey={(row) => row.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Vendored
+95
@@ -0,0 +1,95 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import PageHeader from '@/components/common/PageHeader'
|
||||
import DataTable, { Column } from '@/components/common/DataTable'
|
||||
import StatusBadge from '@/components/common/StatusBadge'
|
||||
import { getTaskLogs } from '@/api/log'
|
||||
import { formatDate } from '@/lib/utils'
|
||||
|
||||
interface TaskItem {
|
||||
id: number
|
||||
user_id: number
|
||||
task_id: string
|
||||
action: string
|
||||
status: string
|
||||
progress: string
|
||||
submit_time: number
|
||||
finish_time: number
|
||||
fail_reason: string
|
||||
result: string
|
||||
}
|
||||
|
||||
const TaskStatusMap: Record<string, { variant: 'success' | 'warning' | 'error' | 'neutral' | 'info'; label: string }> = {
|
||||
SUCCESS: { variant: 'success', label: 'Success' },
|
||||
IN_PROGRESS: { variant: 'info', label: 'In Progress' },
|
||||
FAILURE: { variant: 'error', label: 'Failure' },
|
||||
NOT_START: { variant: 'neutral', label: 'Not Started' },
|
||||
SUBMITTED: { variant: 'warning', label: 'Submitted' },
|
||||
}
|
||||
|
||||
export default function TaskLog() {
|
||||
const { t } = useTranslation()
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(10)
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['task-logs', page, pageSize],
|
||||
queryFn: () => getTaskLogs({ page, page_size: pageSize }),
|
||||
})
|
||||
|
||||
const rows = (data?.data ?? []) as (TaskItem & Record<string, unknown>)[]
|
||||
|
||||
const columns: Column<TaskItem & Record<string, unknown>>[] = [
|
||||
{
|
||||
key: 'submit_time', header: t('log.time'), render: (row) => (
|
||||
<span className="text-xs">{formatDate(row.submit_time as number)}</span>
|
||||
),
|
||||
},
|
||||
{ key: 'task_id', header: 'Task ID', render: (row) => <code className="text-xs">{String(row.task_id ?? '-').slice(0, 12)}</code> },
|
||||
{ key: 'action', header: 'Action', render: (row) => <span className="text-sm">{String(row.action ?? '-')}</span> },
|
||||
{
|
||||
key: 'status', header: t('common.status'), render: (row) => {
|
||||
const s = TaskStatusMap[String(row.status)]
|
||||
return s ? <StatusBadge variant={s.variant} label={s.label} /> : <StatusBadge variant="neutral" label={String(row.status)} />
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'progress', header: t('task.progress'), render: (row) => (
|
||||
<span className="text-xs">{String(row.progress ?? '-')}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'fail_reason', header: t('task.failReason'), render: (row) => (
|
||||
<span className="text-xs text-error">{String(row.fail_reason ?? '-')}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'finish_time', header: t('task.finishTime'), render: (row) => (
|
||||
<span className="text-xs">{row.finish_time ? formatDate(row.finish_time as number) : '-'}</span>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader title={t('nav.tasks')} />
|
||||
|
||||
<div className="card bg-base-100 shadow">
|
||||
<div className="card-body p-0">
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
loading={isLoading}
|
||||
total={data?.total ?? 0}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setPage}
|
||||
onPageSizeChange={(s) => { setPageSize(s); setPage(1) }}
|
||||
rowKey={(row) => row.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user