fix build

This commit is contained in:
songtianlun 2025-08-06 22:21:45 +08:00
parent 0681738a27
commit 5b36748048
11 changed files with 60 additions and 69 deletions

View File

@ -166,3 +166,18 @@ import { useUser } from '@/hooks/useUser'
4. **错误率**:监控缓存相关的错误
这个优化显著改善了应用的性能和用户体验,同时降低了服务器负载和运营成本。
## 构建状态
**构建成功** - 所有 TypeScript 错误已修复,应用可以正常构建和部署。
剩余的警告(非关键):
- Google Analytics 脚本建议使用 `next/script` 组件
- Avatar 组件建议使用 `next/image` 优化图片加载
## 部署建议
1. **生产环境验证**:在生产环境中监控 API 调用频率和缓存命中率
2. **性能监控**:使用 `/debug/cache` 页面定期检查缓存性能
3. **错误监控**:关注缓存相关的错误日志
4. **用户反馈**:收集用户对页面加载速度改善的反馈

View File

@ -48,7 +48,7 @@ export async function POST(request: NextRequest) {
break
case 'invoice.payment_failed':
await handlePaymentFailed(event.data.object as unknown as Record<string, unknown>)
await handlePaymentFailed()
break
default:
@ -277,7 +277,7 @@ async function handlePaymentSucceeded(invoice: Record<string, unknown>) {
}
}
async function handlePaymentFailed(_invoice: Record<string, unknown>) {
async function handlePaymentFailed() {
try {
// 这里可以添加额外的逻辑,比如发送提醒邮件等

View File

@ -6,8 +6,8 @@ import { Header } from '@/components/layout/Header'
import { Button } from '@/components/ui/button'
import { Check, Crown, Star } from 'lucide-react'
import { SubscribeButton } from '@/components/subscription/SubscribeButton'
import { useEffect, useState } from 'react'
import type { SubscriptionPlan } from '@prisma/client'
import { useEffect, useState, useCallback } from 'react'
// Remove unused import
import {
isPlanPro,
isPlanFree,
@ -20,16 +20,32 @@ import {
export default function PricingPage() {
const { user, userData } = useAuthUser()
const t = useTranslations('pricing')
const [plans, setPlans] = useState<any[]>([])
const [plans, setPlans] = useState<Array<{
id: string
name: string
displayName?: string
description: string
price: number
currency: string
interval: string
features: string[]
stripePriceId: string
isPopular?: boolean
}>>([])
const [loading, setLoading] = useState(true)
const fetchPlans = async () => {
const isCurrentPlan = useCallback((planId: string) => {
if (!userData) return false
return userData.subscriptionPlanId === planId
}, [userData])
const fetchPlans = useCallback(async () => {
try {
const response = await fetch('/api/subscription-plans')
if (response.ok) {
const data = await response.json()
// 过滤套餐:只显示真正的免费套餐、用户当前套餐,以及有 stripePriceId 的付费套餐
const filteredPlans = (data.plans || []).filter((plan: SubscriptionPlan) => {
const filteredPlans = (data.plans || []).filter((plan: { id: string; name: string; price: number; stripePriceId?: string }) => {
// 只显示官方的免费套餐ID为'free'或名称为'free'
if (isPlanFree(plan) && (plan.id === 'free' || plan.name.toLowerCase() === 'free')) {
return true
@ -52,18 +68,13 @@ export default function PricingPage() {
} finally {
setLoading(false)
}
}
}, [userData, isCurrentPlan])
useEffect(() => {
fetchPlans()
}, [userData]) // 依赖 userData确保用户数据加载后再过滤套餐
const isCurrentPlan = (planId: string) => {
if (!userData) return false
return userData.subscriptionPlanId === planId
}
}, [fetchPlans]) // 依赖 fetchPlans
@ -136,7 +147,7 @@ export default function PricingPage() {
</div>
</div>
<h3 className="text-2xl font-bold text-card-foreground mb-2">
{plan.displayName}
{plan.displayName || plan.name}
</h3>
<div className="text-4xl font-bold text-card-foreground mb-2">
{formatPlanPrice(plan, t)}
@ -217,7 +228,7 @@ export default function PricingPage() {
return (
<SubscribeButton
priceId={plan.stripePriceId}
planName={plan.displayName}
planName={plan.displayName || plan.name}
className={`w-full ${isPro ? 'bg-primary hover:bg-primary/90' : ''}`}
>
{t('subscribeNow')}

View File

@ -10,7 +10,7 @@ import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { LegacyAvatar } from '@/components/ui/avatar'
import { LoadingSpinner, LoadingOverlay, GradientLoading } from '@/components/ui/loading-spinner'
import { LoadingSpinner, LoadingOverlay } from '@/components/ui/loading-spinner'
import { AvatarSkeleton, FormFieldSkeleton, TextAreaSkeleton } from '@/components/ui/skeleton'
import { Save, Eye, EyeOff, CreditCard, Crown, Star } from 'lucide-react'

View File

@ -24,7 +24,7 @@ interface SubscriptionData {
}
export default function SubscriptionPage() {
const { user, userData, loading, triggerSubscriptionUpdate } = useAuthUser()
const { user, userData, loading } = useAuthUser()
const router = useRouter()
const t = useTranslations('subscription')
@ -111,40 +111,7 @@ export default function SubscriptionPage() {
}
}
const handleManageSubscription = async () => {
setActionLoading(true)
try {
const response = await fetch('/api/subscription/manage', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'portal'
}),
})
if (response.ok) {
const { url } = await response.json()
window.location.href = url
} else {
const errorData = await response.json()
console.error('Portal error:', errorData)
// 如果是客户门户未配置的错误,提供替代方案
if (errorData.details?.includes('billing portal') || errorData.details?.includes('portal')) {
alert('Billing portal is not yet configured. Please contact support for subscription management.')
} else {
throw new Error(errorData.error || 'Failed to create portal session')
}
}
} catch (error) {
console.error('Portal failed:', error)
alert('Failed to open billing portal. Please try again.')
} finally {
setActionLoading(false)
}
}
const handleSyncSubscription = async () => {
setActionLoading(true)

View File

@ -126,7 +126,7 @@ export function PromptCard({ prompt, onViewIncrement }: PromptCardProps) {
try {
const errorData = await response.json()
errorMessage = errorData.error || `HTTP ${response.status}: ${response.statusText}`
} catch (e) {
} catch {
errorMessage = `HTTP ${response.status}: ${response.statusText}`
}
@ -144,7 +144,7 @@ export function PromptCard({ prompt, onViewIncrement }: PromptCardProps) {
}, 300)
}, 3000)
}
} catch (error) {
} catch {
// Create error notification for network/other errors
const notification = document.createElement('div')
notification.className = 'fixed top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-md shadow-lg z-50 transition-all duration-300'

View File

@ -128,7 +128,7 @@ export function PromptDetailModal({
try {
const errorData = await response.json()
errorMessage = errorData.error || `HTTP ${response.status}: ${response.statusText}`
} catch (e) {
} catch {
errorMessage = `HTTP ${response.status}: ${response.statusText}`
}
@ -146,7 +146,7 @@ export function PromptDetailModal({
}, 300)
}, 3000)
}
} catch (error) {
} catch {
// Create error notification for network/other errors
const notification = document.createElement('div')
notification.className = 'fixed top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-md shadow-lg z-50 transition-all duration-300'

View File

@ -116,7 +116,7 @@ export function QuickUpgradeButton({ className, children }: QuickUpgradeButtonPr
} else {
setError('Failed to load Pro plan')
}
} catch (err) {
} catch {
setError('Failed to load Pro plan')
} finally {
setLoading(false)

View File

@ -122,7 +122,7 @@ export function useAuthUser() {
const syncOptions: SyncOptions = { trigger }
// 并行执行同步和获取用户数据
const [, userData] = await Promise.all([
await Promise.all([
syncUserToDatabase(user.id, syncOptions),
fetchUserData(user.id, syncOptions),
])

View File

@ -1,11 +1,9 @@
import type { SubscriptionPlan } from '@prisma/client'
/**
*
*/
// 支持部分套餐数据的类型
type PlanLike = { price: number } | SubscriptionPlan | null | undefined
type PlanLike = { price: number; id?: string; name?: string; features?: unknown; limits?: unknown } | null | undefined
/**
* Pro 19
@ -104,7 +102,7 @@ export function formatPlanPrice(plan: PlanLike & { interval?: string }, t?: (key
/**
* 访
*/
export function getPlanLimits(plan: SubscriptionPlan | null | undefined): {
export function getPlanLimits(plan: PlanLike): {
promptLimit: number
maxVersionLimit: number
creditMonthly: number
@ -117,7 +115,7 @@ export function getPlanLimits(plan: SubscriptionPlan | null | undefined): {
}
}
const limits = plan.limits as Record<string, unknown>
const limits = (plan.limits as Record<string, unknown>) || {}
return {
promptLimit: (limits?.promptLimit as number) || 500,
maxVersionLimit: (limits?.maxVersionLimit as number) || 3,
@ -128,17 +126,17 @@ export function getPlanLimits(plan: SubscriptionPlan | null | undefined): {
/**
* 访
*/
export function getPlanFeatures(plan: SubscriptionPlan | null | undefined): string[] {
export function getPlanFeatures(plan: PlanLike): string[] {
if (!plan) return []
const features = plan.features as Record<string, unknown>
const features = (plan.features as Record<string, unknown>) || {}
return Object.keys(features).filter(key => features[key] === true)
}
/**
*
*/
export function planHasFeature(plan: SubscriptionPlan | null | undefined, feature: string): boolean {
export function planHasFeature(plan: PlanLike, feature: string): boolean {
const features = getPlanFeatures(plan)
return features.includes(feature)
}

View File

@ -37,7 +37,7 @@ const CACHE_CONFIG = {
class UserCache {
private userDataCache = new Map<string, CacheItem<UserData>>()
private syncTimestamps = new Map<string, number>()
private syncPromises = new Map<string, Promise<any>>()
private syncPromises = new Map<string, Promise<void>>()
// 获取缓存的用户数据
getUserData(userId: string): UserData | null {
@ -85,7 +85,7 @@ class UserCache {
}
// 获取或创建同步Promise防止重复同步
getSyncPromise(userId: string, syncFn: () => Promise<any>): Promise<any> {
getSyncPromise(userId: string, syncFn: () => Promise<void>): Promise<void> {
const existing = this.syncPromises.get(userId)
if (existing) return existing