fix: P0 安全加固 + 前端错误边界 + ESLint 修复
后端: - 实现登出 API(清除 refresh token) - 清除 videos.py 中已被 Celery 任务取代的死代码 - 添加速率限制中间件(60次/分钟,登录10次/分钟) - 添加 SECRET_KEY/ENCRYPTION_KEY 默认值警告 - OSS STS 方法回退到 Policy 签名(不再抛异常) 前端: - 添加全局 404/error/loading 页面 - 添加三端 error.tsx + loading.tsx 错误边界 - 修复 useId 条件调用违反 Hooks 规则 - 修复未转义引号和 Image 命名冲突 - 添加 ESLint 配置 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a8be7bbca9
commit
8eb8100cf4
@@ -0,0 +1,44 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { AlertTriangle, RefreshCw, Home } from 'lucide-react'
|
||||
|
||||
export default function AgencyError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error('Agency section error:', error)
|
||||
}, [error])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full min-h-[400px] gap-4">
|
||||
<div className="w-14 h-14 bg-accent-coral/15 rounded-2xl flex items-center justify-center">
|
||||
<AlertTriangle className="w-7 h-7 text-accent-coral" />
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-text-primary">页面加载失败</h2>
|
||||
<p className="text-text-secondary text-sm max-w-sm text-center">
|
||||
{error.message || '发生未知错误,请重试'}
|
||||
</p>
|
||||
<div className="flex gap-3 mt-2">
|
||||
<button
|
||||
onClick={() => window.location.href = '/agency'}
|
||||
className="flex items-center gap-2 px-4 py-2.5 bg-bg-elevated text-text-secondary rounded-xl text-sm font-medium hover:bg-bg-card transition-colors border border-border-subtle"
|
||||
>
|
||||
<Home className="w-4 h-4" />
|
||||
回到首页
|
||||
</button>
|
||||
<button
|
||||
onClick={reset}
|
||||
className="flex items-center gap-2 px-4 py-2.5 bg-accent-indigo text-white rounded-xl text-sm font-medium hover:bg-accent-indigo/90 transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function AgencyLoading() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full min-h-[400px]">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-8 h-8 border-2 border-border-subtle border-t-accent-indigo rounded-full animate-spin" />
|
||||
<p className="text-text-tertiary text-sm">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -585,7 +585,7 @@ export default function AgencyCompanyPage() {
|
||||
{isEditing && (
|
||||
<div className="p-3 rounded-lg bg-accent-indigo/10 border border-accent-indigo/20">
|
||||
<p className="text-sm text-accent-indigo">
|
||||
💡 提示:点击右上角"查询企业"按钮,输入公司名称可自动填充工商信息
|
||||
💡 提示:点击右上角“查询企业”按钮,输入公司名称可自动填充工商信息
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { AlertTriangle, RefreshCw, Home } from 'lucide-react'
|
||||
|
||||
export default function BrandError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error('Brand section error:', error)
|
||||
}, [error])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full min-h-[400px] gap-4">
|
||||
<div className="w-14 h-14 bg-accent-coral/15 rounded-2xl flex items-center justify-center">
|
||||
<AlertTriangle className="w-7 h-7 text-accent-coral" />
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-text-primary">页面加载失败</h2>
|
||||
<p className="text-text-secondary text-sm max-w-sm text-center">
|
||||
{error.message || '发生未知错误,请重试'}
|
||||
</p>
|
||||
<div className="flex gap-3 mt-2">
|
||||
<button
|
||||
onClick={() => window.location.href = '/brand'}
|
||||
className="flex items-center gap-2 px-4 py-2.5 bg-bg-elevated text-text-secondary rounded-xl text-sm font-medium hover:bg-bg-card transition-colors border border-border-subtle"
|
||||
>
|
||||
<Home className="w-4 h-4" />
|
||||
回到首页
|
||||
</button>
|
||||
<button
|
||||
onClick={reset}
|
||||
className="flex items-center gap-2 px-4 py-2.5 bg-accent-indigo text-white rounded-xl text-sm font-medium hover:bg-accent-indigo/90 transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function BrandLoading() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full min-h-[400px]">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-8 h-8 border-2 border-border-subtle border-t-accent-indigo rounded-full animate-spin" />
|
||||
<p className="text-text-tertiary text-sm">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -302,7 +302,7 @@ export default function CreateProjectPage() {
|
||||
)}
|
||||
|
||||
<p className="text-xs text-text-tertiary mt-3">
|
||||
仅显示已在"代理商管理"中添加的代理商
|
||||
仅显示已在“代理商管理”中添加的代理商
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ export default function BrandVideoReviewPage() {
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [taskId])
|
||||
}, [taskId, toast])
|
||||
|
||||
useEffect(() => {
|
||||
loadTask()
|
||||
|
||||
@@ -363,7 +363,7 @@ export default function AppealQuotaPage() {
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-sm font-medium text-text-primary">申诉次数规则</span>
|
||||
<span className="text-[13px] text-text-secondary leading-relaxed">
|
||||
每个任务初始有 1 次申诉机会,不同任务独立计算。如需更多次数,可点击"申请增加"向代理商发送请求,无需填写理由。代理商可增加的次数无上限。
|
||||
每个任务初始有 1 次申诉机会,不同任务独立计算。如需更多次数,可点击“申请增加”向代理商发送请求,无需填写理由。代理商可增加的次数无上限。
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
FileText,
|
||||
Image,
|
||||
Image as ImageIcon,
|
||||
Send,
|
||||
AlertTriangle,
|
||||
Loader2
|
||||
@@ -422,7 +422,7 @@ export default function AppealDetailPage() {
|
||||
className="flex items-center gap-3 px-4 py-3 bg-bg-elevated rounded-xl cursor-pointer hover:bg-bg-page transition-colors"
|
||||
>
|
||||
{attachment.type === 'image' ? (
|
||||
<Image className="w-5 h-5 text-accent-indigo" />
|
||||
<ImageIcon className="w-5 h-5 text-accent-indigo" />
|
||||
) : (
|
||||
<FileText className="w-5 h-5 text-accent-indigo" />
|
||||
)}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Upload,
|
||||
X,
|
||||
FileText,
|
||||
Image,
|
||||
Image as ImageIcon,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
Loader2
|
||||
@@ -468,7 +468,7 @@ export default function NewAppealPage() {
|
||||
className="flex items-center gap-2 px-3 py-2 bg-bg-elevated rounded-lg"
|
||||
>
|
||||
{file.type === 'image' ? (
|
||||
<Image className="w-4 h-4 text-accent-indigo" />
|
||||
<ImageIcon className="w-4 h-4 text-accent-indigo" />
|
||||
) : (
|
||||
<FileText className="w-4 h-4 text-accent-indigo" />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { AlertTriangle, RefreshCw, Home } from 'lucide-react'
|
||||
|
||||
export default function CreatorError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error('Creator section error:', error)
|
||||
}, [error])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full min-h-[400px] gap-4">
|
||||
<div className="w-14 h-14 bg-accent-coral/15 rounded-2xl flex items-center justify-center">
|
||||
<AlertTriangle className="w-7 h-7 text-accent-coral" />
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-text-primary">页面加载失败</h2>
|
||||
<p className="text-text-secondary text-sm max-w-sm text-center">
|
||||
{error.message || '发生未知错误,请重试'}
|
||||
</p>
|
||||
<div className="flex gap-3 mt-2">
|
||||
<button
|
||||
onClick={() => window.location.href = '/creator'}
|
||||
className="flex items-center gap-2 px-4 py-2.5 bg-bg-elevated text-text-secondary rounded-xl text-sm font-medium hover:bg-bg-card transition-colors border border-border-subtle"
|
||||
>
|
||||
<Home className="w-4 h-4" />
|
||||
回到首页
|
||||
</button>
|
||||
<button
|
||||
onClick={reset}
|
||||
className="flex items-center gap-2 px-4 py-2.5 bg-accent-indigo text-white rounded-xl text-sm font-medium hover:bg-accent-indigo/90 transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function CreatorLoading() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full min-h-[400px]">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-8 h-8 border-2 border-border-subtle border-t-accent-indigo rounded-full animate-spin" />
|
||||
<p className="text-text-tertiary text-sm">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error('Application error:', error)
|
||||
}, [error])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-bg-page flex items-center justify-center">
|
||||
<div className="text-center max-w-md">
|
||||
<div className="w-16 h-16 bg-accent-coral/15 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||||
<svg className="w-8 h-8 text-accent-coral" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-text-primary mb-2">
|
||||
出错了
|
||||
</h2>
|
||||
<p className="text-text-secondary mb-8">
|
||||
应用遇到了一个错误,请尝试刷新页面
|
||||
</p>
|
||||
<div className="flex gap-3 justify-center">
|
||||
<button
|
||||
onClick={() => window.location.href = '/'}
|
||||
className="px-6 py-3 bg-bg-elevated text-text-secondary rounded-xl font-medium hover:bg-bg-card transition-colors border border-border-subtle"
|
||||
>
|
||||
返回首页
|
||||
</button>
|
||||
<button
|
||||
onClick={reset}
|
||||
className="px-6 py-3 bg-accent-indigo text-white rounded-xl font-medium hover:bg-accent-indigo/90 transition-colors"
|
||||
>
|
||||
重试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="min-h-screen bg-bg-page flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-10 h-10 border-3 border-border-subtle border-t-accent-indigo rounded-full animate-spin" />
|
||||
<p className="text-text-tertiary text-sm">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="min-h-screen bg-bg-page flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-8xl font-bold text-accent-indigo mb-4">404</h1>
|
||||
<h2 className="text-2xl font-semibold text-text-primary mb-2">
|
||||
页面未找到
|
||||
</h2>
|
||||
<p className="text-text-secondary mb-8">
|
||||
您访问的页面不存在或已被移除
|
||||
</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 bg-accent-indigo text-white rounded-xl font-medium hover:bg-accent-indigo/90 transition-colors"
|
||||
>
|
||||
返回首页
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user