From 02fc0676bf1dedff1c7cd05168f131f1fdd5173b Mon Sep 17 00:00:00 2001 From: wmp <17516219072@163.com> Date: Tue, 23 Sep 2025 11:30:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BA=94=E7=94=A8=20eslint=20=E8=A7=84?= =?UTF-8?q?=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.ts | 6 +- postcss.config.mjs | 6 +- src/app/(auth)/login/page.tsx | 54 ++-- src/app/api/auth/login/route.ts | 132 ++++----- src/app/api/auth/logout/route.ts | 62 ++-- src/app/api/stats/route.ts | 279 +++++++++--------- src/app/api/users/route.tsx | 38 +-- .../dashboard/components/allocationStatus.tsx | 57 ++-- .../dashboard/components/cityNodeStats.tsx | 21 +- src/app/dashboard/components/edge.tsx | 108 +++---- .../dashboard/components/gatewayConfig.tsx | 60 ++-- src/app/dashboard/components/gatewayinfo.tsx | 80 ++--- src/app/dashboard/components/settings.tsx | 138 ++++----- src/app/dashboard/page.tsx | 27 +- src/app/globals.css | 3 +- src/app/layout.tsx | 24 +- src/components/ui/alert.tsx | 30 +- src/components/ui/badge.tsx | 28 +- src/components/ui/button.tsx | 42 +-- src/components/ui/card.tsx | 38 +-- src/components/ui/dialog.tsx | 32 +- src/components/ui/errorCard.tsx | 16 +- src/components/ui/form.tsx | 36 +-- src/components/ui/input.tsx | 14 +- src/components/ui/label.tsx | 12 +- src/components/ui/loadingCard.tsx | 2 +- src/components/ui/pagination.tsx | 28 +- src/components/ui/select.tsx | 50 ++-- src/components/ui/skeleton.tsx | 6 +- src/components/ui/sonner.tsx | 16 +- src/components/ui/table.tsx | 46 +-- src/components/ui/tabs.tsx | 20 +- src/lib/formatters.ts | 30 +- src/lib/prisma.ts | 8 +- src/lib/utils.ts | 4 +- src/middleware.ts | 2 +- src/store/auth.ts | 8 +- 37 files changed, 797 insertions(+), 766 deletions(-) diff --git a/next.config.ts b/next.config.ts index 1999f2a..1e4996b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,4 +1,4 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from 'next' const nextConfig: NextConfig = { /* config options here */ @@ -6,6 +6,6 @@ const nextConfig: NextConfig = { ignoreDuringBuilds: true, }, output: 'standalone', -}; +} -export default nextConfig; +export default nextConfig diff --git a/postcss.config.mjs b/postcss.config.mjs index c7bcb4b..86e8e3c 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,5 +1,5 @@ const config = { - plugins: ["@tailwindcss/postcss"], -}; + plugins: ['@tailwindcss/postcss'], +} -export default config; +export default config diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 21466bd..dc728a6 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -13,14 +13,14 @@ import { useAuthStore } from '@/store/auth' import { toast, Toaster } from 'sonner' const formSchema = z.object({ - account: z.string().min(3, '账号至少需要3个字符'), - password: z.string().min(6, '密码至少需要6个字符'), + account: z.string().min(3, '账号至少需要3个字符'), + password: z.string().min(6, '密码至少需要6个字符'), }) export default function LoginPage() { const router = useRouter() const [loading, setLoading] = useState(false) - const setAuth = useAuthStore((state) => state.setAuth) + const setAuth = useAuthStore(state => state.setAuth) const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { @@ -39,7 +39,7 @@ export default function LoginPage() { }, body: JSON.stringify(values), }) - + const data = await response.json() if (!response.ok) { @@ -47,19 +47,21 @@ export default function LoginPage() { } if (data.success) { - toast.success("登录成功", { - description: "正在跳转到仪表盘...", + toast.success('登录成功', { + description: '正在跳转到仪表盘...', }) setAuth(true) await new Promise(resolve => setTimeout(resolve, 1000)) router.push('/dashboard') router.refresh() } - } catch (error) { - toast.error("登录失败", { - description: error instanceof Error ? error.message : "服务器连接失败,请稍后重试", + } + catch (error) { + toast.error('登录失败', { + description: error instanceof Error ? error.message : '服务器连接失败,请稍后重试', }) - } finally { + } + finally { setLoading(false) } } @@ -82,9 +84,9 @@ export default function LoginPage() {
-
@@ -109,20 +111,22 @@ export default function LoginPage() { )} /> - @@ -132,4 +136,4 @@ export default function LoginPage() { ) -} \ No newline at end of file +} diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 1d4d913..0d7afe4 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -4,76 +4,74 @@ import { compare } from 'bcryptjs' import { z } from 'zod' const loginSchema = z.object({ - account: z.string().min(3, '账号至少需要3个字符'), - password: z.string().min(6, '密码至少需要6个字符'), + account: z.string().min(3, '账号至少需要3个字符'), + password: z.string().min(6, '密码至少需要6个字符'), }) export async function POST(request: Request) { - try { - const body = await request.json() - const { account, password } = loginSchema.parse(body) + try { + const body = await request.json() + const { account, password } = loginSchema.parse(body) + const user = await prisma.user.findFirst({ + where: { + OR: [ + { account: account.trim() }, + { password: account.trim() }, + ], + }, + }) - const user = await prisma.user.findFirst({ - where: { - OR: [ - { account: account.trim() }, - { password: account.trim() } - ] - }, - }) - - - if (!user) { - return NextResponse.json( - { success: false, error: '用户不存在' }, - { status: 401 } - ) - } - - // 验证密码 - const passwordMatch = await compare(password, user.password || '') - - if (!passwordMatch) { - return NextResponse.json({ - success: false, - error: '密码错误' - }, { status: 401 }) - } - - // 创建会话 - const sessionToken = crypto.randomUUID() - await prisma.session.create({ - data: { - id: sessionToken, - userId: user.id, - expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - } - }) - - // 设置cookie - const response = NextResponse.json({ - success: true, - user: { - id: user.id, - account: user.account, - name: user.name - } - }) - - response.cookies.set('session', sessionToken, { - httpOnly: true, - // secure: process.env.NODE_ENV === 'production', - maxAge: 60 * 60 * 24 * 7 - }) - - return response - - } catch (error) { - console.error('登录错误:', error) - return NextResponse.json( - { success: false, error: '服务器错误,请稍后重试' }, - { status: 500 } - ) + if (!user) { + return NextResponse.json( + { success: false, error: '用户不存在' }, + { status: 401 }, + ) } -} \ No newline at end of file + + // 验证密码 + const passwordMatch = await compare(password, user.password || '') + + if (!passwordMatch) { + return NextResponse.json({ + success: false, + error: '密码错误', + }, { status: 401 }) + } + + // 创建会话 + const sessionToken = crypto.randomUUID() + await prisma.session.create({ + data: { + id: sessionToken, + userId: user.id, + expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), + }, + }) + + // 设置cookie + const response = NextResponse.json({ + success: true, + user: { + id: user.id, + account: user.account, + name: user.name, + }, + }) + + response.cookies.set('session', sessionToken, { + httpOnly: true, + // secure: process.env.NODE_ENV === 'production', + maxAge: 60 * 60 * 24 * 7, + }) + + return response + } + catch (error) { + console.error('登录错误:', error) + return NextResponse.json( + { success: false, error: '服务器错误,请稍后重试' }, + { status: 500 }, + ) + } +} diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index 0a24e7a..13d18f4 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -3,36 +3,36 @@ import { cookies } from 'next/headers' import { prisma } from '@/lib/prisma' export async function POST() { - try { - const cookieStore = await cookies() - const sessionToken = cookieStore.get('session')?.value + try { + const cookieStore = await cookies() + const sessionToken = cookieStore.get('session')?.value - // 删除数据库中的session(如果存在) - if (sessionToken) { - await prisma.session.deleteMany({ - where: { id: sessionToken } - }).catch(() => { - // 忽略删除错误,确保cookie被清除 - }) - } - - // 清除cookie - const response = NextResponse.json({ success: true }) - response.cookies.set('session', '', { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - maxAge: 0, // 立即过期 - path: '/', - }) - - return response - - } catch (error) { - console.error('退出错误:', error) - return NextResponse.json( - { success: false, error: '退出失败' }, - { status: 500 } - ) + // 删除数据库中的session(如果存在) + if (sessionToken) { + await prisma.session.deleteMany({ + where: { id: sessionToken }, + }).catch(() => { + // 忽略删除错误,确保cookie被清除 + }) } -} \ No newline at end of file + + // 清除cookie + const response = NextResponse.json({ success: true }) + response.cookies.set('session', '', { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 0, // 立即过期 + path: '/', + }) + + return response + } + catch (error) { + console.error('退出错误:', error) + return NextResponse.json( + { success: false, error: '退出失败' }, + { status: 500 }, + ) + } +} diff --git a/src/app/api/stats/route.ts b/src/app/api/stats/route.ts index 93aaf18..fdff272 100644 --- a/src/app/api/stats/route.ts +++ b/src/app/api/stats/route.ts @@ -3,100 +3,103 @@ import { prisma } from '@/lib/prisma' // 处理 BigInt 序列化 function safeSerialize(data: unknown) { - return JSON.parse(JSON.stringify(data, (key, value) => - typeof value === 'bigint' ? value.toString() : value - )) + return JSON.parse(JSON.stringify(data, (key, value) => + typeof value === 'bigint' ? value.toString() : value, + )) } export async function GET(request: NextRequest) { - try { - const { searchParams } = new URL(request.url) - const reportType = searchParams.get('type') + try { + const { searchParams } = new URL(request.url) + const reportType = searchParams.get('type') - switch (reportType) { - case 'gateway_info': - return await getGatewayInfo() - case 'gateway_config': - return await getGatewayConfig(request) - case 'city_config_count': - return await getCityConfigCount() - case 'city_node_count': - return await getCityNodeCount() - case 'allocation_status': - return await getAllocationStatus(request) - case 'edge_nodes': - return await getEdgeNodes(request) - default: - return NextResponse.json({ error: 'Invalid report type' }, { status: 400 }) - } - } catch (error) { - console.error('API Error:', error) - return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) + switch (reportType) { + case 'gateway_info': + return await getGatewayInfo() + case 'gateway_config': + return await getGatewayConfig(request) + case 'city_config_count': + return await getCityConfigCount() + case 'city_node_count': + return await getCityNodeCount() + case 'allocation_status': + return await getAllocationStatus(request) + case 'edge_nodes': + return await getEdgeNodes(request) + default: + return NextResponse.json({ error: 'Invalid report type' }, { status: 400 }) } + } + catch (error) { + console.error('API Error:', error) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) + } } // 获取网关基本信息 async function getGatewayInfo() { - try { - const result = await prisma.$queryRaw` + try { + const result = await prisma.$queryRaw` SELECT macaddr, inner_ip, setid, enable FROM token ORDER BY macaddr ` - return NextResponse.json(safeSerialize(result)) - } catch (error) { - console.error('Gateway info query error:', error) - return NextResponse.json({ error: '查询网关信息失败' }, { status: 500 }) - } + return NextResponse.json(safeSerialize(result)) + } + catch (error) { + console.error('Gateway info query error:', error) + return NextResponse.json({ error: '查询网关信息失败' }, { status: 500 }) + } } -// 网关配置 +// 网关配置 async function getGatewayConfig(request: NextRequest) { - try { - const { searchParams } = new URL(request.url) - const macAddress = searchParams.get('mac') || '' - const offset = parseInt(searchParams.get('offset') || '0') - const limit = parseInt(searchParams.get('limit') || '100') + try { + const { searchParams } = new URL(request.url) + const macAddress = searchParams.get('mac') || '' + const offset = parseInt(searchParams.get('offset') || '0') + const limit = parseInt(searchParams.get('limit') || '100') - // 定义类型接口 - interface GatewayRecord { - edge: string | null; - city: string | null; - user: string | null; - public: string | null; - inner_ip: string | null; - ischange: boolean | number | null; - isonline: boolean | number | null; - } + // 定义类型接口 + interface GatewayRecord { + edge: string | null + city: string | null + user: string | null + public: string | null + inner_ip: string | null + ischange: boolean | number | null + isonline: boolean | number | null + } - // 获取总数 - let totalCountQuery = '' - let totalCountParams: (string | number)[] = [] + // 获取总数 + let totalCountQuery = '' + let totalCountParams: (string | number)[] = [] - if (macAddress) { - totalCountQuery = ` + if (macAddress) { + totalCountQuery = ` SELECT COUNT(*) as total FROM gateway LEFT JOIN cityhash ON cityhash.hash = gateway.cityhash LEFT JOIN edge ON edge.macaddr = gateway.edge WHERE gateway.macaddr = ? ` - totalCountParams = [macAddress] - } else { - totalCountQuery = ` + totalCountParams = [macAddress] + } + else { + totalCountQuery = ` SELECT COUNT(*) as total FROM gateway ` - } + } - const totalCountResult = await prisma.$queryRawUnsafe<[{ total: bigint }]>( - totalCountQuery, - ...totalCountParams - ) - const totalCount = Number(totalCountResult[0]?.total || 0) + const totalCountResult = await prisma.$queryRawUnsafe<[{ total: bigint }]>( + totalCountQuery, + ...totalCountParams, + ) + const totalCount = Number(totalCountResult[0]?.total || 0) - // 获取分页数据 - let query = ` + // 获取分页数据 + let query = ` select edge, city, user, public, inner_ip, ischange, isonline from gateway @@ -105,51 +108,54 @@ async function getGatewayConfig(request: NextRequest) { left join edge on edge.macaddr = gateway.edge ` - let params: (string | number)[] = [] + let params: (string | number)[] = [] - if (macAddress) { - query += ' WHERE gateway.macaddr = ?' - params = [macAddress] - } else { - query += ' LIMIT ? OFFSET ?' - params.push(limit, offset) - } - - // 指定返回类型 - const result = await prisma.$queryRawUnsafe(query, ...params) - - return NextResponse.json({ - data: safeSerialize(result), - totalCount: totalCount, - currentPage: Math.floor(offset / limit) + 1, - totalPages: Math.ceil(totalCount / limit) - }) - } catch (error) { - console.error('Gateway config query error:', error) - return NextResponse.json({ error: '查询网关配置失败' }, { status: 500 }) + if (macAddress) { + query += ' WHERE gateway.macaddr = ?' + params = [macAddress] } + else { + query += ' LIMIT ? OFFSET ?' + params.push(limit, offset) + } + + // 指定返回类型 + const result = await prisma.$queryRawUnsafe(query, ...params) + + return NextResponse.json({ + data: safeSerialize(result), + totalCount: totalCount, + currentPage: Math.floor(offset / limit) + 1, + totalPages: Math.ceil(totalCount / limit), + }) + } + catch (error) { + console.error('Gateway config query error:', error) + return NextResponse.json({ error: '查询网关配置失败' }, { status: 500 }) + } } // 城市节点配置数量统计 async function getCityConfigCount() { - try { - const result = await prisma.$queryRaw` + try { + const result = await prisma.$queryRaw` SELECT c.city, COUNT(e.id) as node_count FROM cityhash c LEFT JOIN edge e ON c.id = e.city_id GROUP BY c.city ` - return NextResponse.json(safeSerialize(result)) - } catch (error) { - console.error('City config count query error:', error) - return NextResponse.json({ error: '查询城市配置失败' }, { status: 500 }) - } + return NextResponse.json(safeSerialize(result)) + } + catch (error) { + console.error('City config count query error:', error) + return NextResponse.json({ error: '查询城市配置失败' }, { status: 500 }) + } } // 城市节点数量分布 async function getCityNodeCount() { - try { - const result = await prisma.$queryRaw` + try { + const result = await prisma.$queryRaw` select c.city, c.hash, c.label, e.count, c.\`offset\` from cityhash c @@ -168,22 +174,23 @@ async function getCityNodeCount() { order by count desc ` - return NextResponse.json(safeSerialize(result)) - } catch (error) { - console.error('City node count query error:', error) - return NextResponse.json({ error: '查询城市节点失败' }, { status: 500 }) - } + return NextResponse.json(safeSerialize(result)) + } + catch (error) { + console.error('City node count query error:', error) + return NextResponse.json({ error: '查询城市节点失败' }, { status: 500 }) + } } // 城市分配状态 async function getAllocationStatus(request: NextRequest) { - try { - const { searchParams } = new URL(request.url) - const hours = searchParams.get('hours') || '24' - const hoursNum = parseInt(hours) || 24 + try { + const { searchParams } = new URL(request.url) + const hours = searchParams.get('hours') || '24' + const hoursNum = parseInt(hours) || 24 - // 使用参数化查询防止SQL注入 - const result = await prisma.$queryRaw` + // 使用参数化查询防止SQL注入 + const result = await prisma.$queryRaw` SELECT city, c1.count AS count, @@ -215,34 +222,34 @@ async function getAllocationStatus(request: NextRequest) { WHERE cityhash.macaddr IS NOT NULL; ` - return NextResponse.json(safeSerialize(result)) - - } catch (error) { - console.error('Allocation status query error:', error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - return NextResponse.json( - { error: '查询分配状态失败: ' + errorMessage }, - { status: 500 } - ) - } + return NextResponse.json(safeSerialize(result)) + } + catch (error) { + console.error('Allocation status query error:', error) + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + return NextResponse.json( + { error: '查询分配状态失败: ' + errorMessage }, + { status: 500 }, + ) + } } // 获取节点信息 async function getEdgeNodes(request: NextRequest) { - try { - const { searchParams } = new URL(request.url) - const offset = parseInt(searchParams.get('offset') || '0') - const limit = parseInt(searchParams.get('limit') || '100') - // 获取总数 - 使用类型断言 - const totalCountResult = await prisma.$queryRaw<[{ total: bigint }]>` + try { + const { searchParams } = new URL(request.url) + const offset = parseInt(searchParams.get('offset') || '0') + const limit = parseInt(searchParams.get('limit') || '100') + // 获取总数 - 使用类型断言 + const totalCountResult = await prisma.$queryRaw<[{ total: bigint }]>` SELECT COUNT(*) as total FROM edge WHERE active = true ` - const totalCount = Number(totalCountResult[0]?.total || 0) + const totalCount = Number(totalCountResult[0]?.total || 0) - // 获取分页数据 - const result = await prisma.$queryRaw` + // 获取分页数据 + const result = await prisma.$queryRaw` SELECT edge.id, edge.macaddr, city, public, isp, single, sole, arch, online FROM edge LEFT JOIN cityhash ON cityhash.id = edge.city_id @@ -250,15 +257,15 @@ async function getEdgeNodes(request: NextRequest) { ORDER BY edge.id LIMIT ${limit} OFFSET ${offset} ` - return NextResponse.json({ - data: safeSerialize(result), - totalCount: totalCount, - currentPage: Math.floor(offset / limit) + 1, - totalPages: Math.ceil(totalCount / limit) - }) - - } catch (error) { - console.error('Edge nodes query error:', error) - return NextResponse.json({ error: '查询边缘节点失败' }, { status: 500 }) - } -} \ No newline at end of file + return NextResponse.json({ + data: safeSerialize(result), + totalCount: totalCount, + currentPage: Math.floor(offset / limit) + 1, + totalPages: Math.ceil(totalCount / limit), + }) + } + catch (error) { + console.error('Edge nodes query error:', error) + return NextResponse.json({ error: '查询边缘节点失败' }, { status: 500 }) + } +} diff --git a/src/app/api/users/route.tsx b/src/app/api/users/route.tsx index 303031b..f97ae5d 100644 --- a/src/app/api/users/route.tsx +++ b/src/app/api/users/route.tsx @@ -20,14 +20,14 @@ export async function GET() { return NextResponse.json({ success: true, - users + users, }) - - } catch (error) { + } + catch (error) { console.error('获取用户列表错误:', error) return NextResponse.json( { success: false, error: '服务器错误,请稍后重试' }, - { status: 500 } + { status: 500 }, ) } } @@ -39,13 +39,13 @@ export async function POST(request: Request) { // 检查用户是否已存在 const existingUser = await prisma.user.findUnique({ - where: { account } + where: { account }, }) if (existingUser) { return NextResponse.json( { success: false, error: '用户账号已存在' }, - { status: 400 } + { status: 400 }, ) } @@ -66,19 +66,18 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - user: userWithoutPassword + user: userWithoutPassword, }) - - } catch (error) { + } + catch (error) { console.error('创建用户错误:', error) return NextResponse.json( { success: false, error: '服务器错误,请稍后重试' }, - { status: 500 } + { status: 500 }, ) } } - // 删除用户 export async function DELETE(request: Request) { try { @@ -89,7 +88,7 @@ export async function DELETE(request: Request) { if (!id) { return NextResponse.json( { success: false, error: '用户ID不能为空' }, - { status: 400 } + { status: 400 }, ) } @@ -97,36 +96,37 @@ export async function DELETE(request: Request) { if (isNaN(userId)) { return NextResponse.json( { success: false, error: '无效的用户ID' }, - { status: 400 } + { status: 400 }, ) } // 检查用户是否存在 const existingUser = await prisma.user.findUnique({ - where: { id: userId } + where: { id: userId }, }) if (!existingUser) { return NextResponse.json( { success: false, error: '用户不存在' }, - { status: 404 } + { status: 404 }, ) } // 删除用户 await prisma.user.delete({ - where: { id: userId } + where: { id: userId }, }) return NextResponse.json({ success: true, - message: '用户删除成功' + message: '用户删除成功', }) - } catch (error) { + } + catch (error) { console.error('删除用户错误:', error) return NextResponse.json( { success: false, error: '服务器错误,请稍后重试' }, - { status: 500 } + { status: 500 }, ) } } diff --git a/src/app/dashboard/components/allocationStatus.tsx b/src/app/dashboard/components/allocationStatus.tsx index a68ef06..b1b8af4 100644 --- a/src/app/dashboard/components/allocationStatus.tsx +++ b/src/app/dashboard/components/allocationStatus.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useState, useCallback } from 'react' +import { useEffect, useState, useCallback } from 'react' import { formatNumber, validateNumber } from '@/lib/formatters' import LoadingCard from '@/components/ui/loadingCard' import ErrorCard from '@/components/ui/errorCard' @@ -28,48 +28,49 @@ export default function AllocationStatus({ detailed = false }: { detailed?: bool // 获取时间参数(小时数) const getTimeHours = useCallback(() => { - if (timeFilter === 'custom' && customHours) { const hours = parseInt(customHours) return isNaN(hours) ? 24 : Math.max(1, hours) // 默认24小时,最少1小时 } - + return parseInt(timeFilter) || 24 // 默认24小时 }, [timeFilter, customHours]) // 计算超额量 const calculateOverage = (assigned: number, count: number) => { - const overage = assigned - count; - return Math.max(0, overage); + const overage = assigned - count + return Math.max(0, overage) } const fetchData = useCallback(async () => { try { setError(null) setLoading(true) - + const hours = getTimeHours() - + const response = await fetch(`/api/stats?type=allocation_status&hours=${hours}`) - + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`) - + const result = await response.json() - + // 数据验证 - const validatedData = (result as ApiAllocationStatus[]).map((item) => ({ + const validatedData = (result as ApiAllocationStatus[]).map(item => ({ city: item.city || '未知', count: validateNumber(item.count), assigned: validateNumber(item.assigned), })) - + const sortedData = validatedData.sort((a, b) => b.count - a.count) - + setData(sortedData) - } catch (error) { + } + catch (error) { console.error('Failed to fetch allocation status:', error) setError(error instanceof Error ? error.message : 'Unknown error') - } finally { + } + finally { setLoading(false) } }, [getTimeHours]) @@ -86,13 +87,13 @@ export default function AllocationStatus({ detailed = false }: { detailed?: bool return (

节点分配状态

- + {/* 时间筛选器 */}
- setTimeFilter(e.target.value)} className="border rounded p-2" > @@ -102,7 +103,7 @@ export default function AllocationStatus({ detailed = false }: { detailed?: bool - + {timeFilter === 'custom' && (
setCustomHours(e.target.value)} + onChange={e => setCustomHours(e.target.value)} placeholder="输入小时数" className="border rounded p-2 w-24" /> 小时 (1-720)
)} - -
- -
+ +
@@ -141,8 +142,8 @@ export default function AllocationStatus({ detailed = false }: { detailed?: bool {data.map((item, index) => { const overage = calculateOverage(item.assigned, item.count) return ( - {item.city} @@ -162,4 +163,4 @@ export default function AllocationStatus({ detailed = false }: { detailed?: bool ) -} \ No newline at end of file +} diff --git a/src/app/dashboard/components/cityNodeStats.tsx b/src/app/dashboard/components/cityNodeStats.tsx index 6d7d705..7b08616 100644 --- a/src/app/dashboard/components/cityNodeStats.tsx +++ b/src/app/dashboard/components/cityNodeStats.tsx @@ -24,9 +24,11 @@ export default function CityNodeStats() { const response = await fetch('/api/stats?type=city_node_count') const result = await response.json() setData(result) - } catch (error) { + } + catch (error) { console.error('获取城市节点数据失败:', error) - } finally { + } + finally { setLoading(false) } } @@ -50,7 +52,7 @@ export default function CityNodeStats() {
-
+
@@ -63,8 +65,8 @@ export default function CityNodeStats() { {data.map((item, index) => ( - {item.city} @@ -72,15 +74,16 @@ export default function CityNodeStats() { {item.hash} - {item.label} - + {item.label} + + {item.offset} ))} -
+
) -} \ No newline at end of file +} diff --git a/src/app/dashboard/components/edge.tsx b/src/app/dashboard/components/edge.tsx index 58cc8cc..b98130e 100644 --- a/src/app/dashboard/components/edge.tsx +++ b/src/app/dashboard/components/edge.tsx @@ -21,7 +21,7 @@ export default function Edge() { const [data, setData] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) - + // 分页状态 const [currentPage, setCurrentPage] = useState(1) const [itemsPerPage, setItemsPerPage] = useState(100) // 默认100条 @@ -35,13 +35,13 @@ export default function Edge() { try { setError(null) setLoading(true) - + // 计算偏移量 const offset = (currentPage - 1) * itemsPerPage - + const response = await fetch(`/api/stats?type=edge_nodes&offset=${offset}&limit=${itemsPerPage}`) if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`) - + const result = await response.json() type ResultEdge = { id: number @@ -55,7 +55,7 @@ export default function Edge() { online: number } - const validatedData = (result.data as ResultEdge[]).map((item) => ({ + const validatedData = (result.data as ResultEdge[]).map(item => ({ id: validateNumber(item.id), macaddr: item.macaddr || '', city: item.city || '', @@ -64,60 +64,62 @@ export default function Edge() { single: item.single, sole: item.sole, arch: validateNumber(item.arch), - online: validateNumber(item.online) + online: validateNumber(item.online), })) - + setData(validatedData) setTotalItems(result.totalCount || 0) - } catch (error) { + } + catch (error) { console.error('Failed to fetch edge nodes:', error) setError(error instanceof Error ? error.message : '获取边缘节点数据失败') - } finally { + } + finally { setLoading(false) } } -// 多IP节点格式化 -const formatMultiIP = (value: number | boolean): string => { - if (typeof value === 'number') { - switch (value) { - case 1: return '是' - case 0: return '否' - case -1: return '未知' - default: return `未知 (${value})` + // 多IP节点格式化 + const formatMultiIP = (value: number | boolean): string => { + if (typeof value === 'number') { + switch (value) { + case 1: return '是' + case 0: return '否' + case -1: return '未知' + default: return `未知 (${value})` + } } + return value ? '是' : '否' } - return value ? '是' : '否' -} -// 独享IP节点格式化 -const formatExclusiveIP = (value: number | boolean): string => { - if (typeof value === 'number') { - return value === 1 ? '是' : '否' - } - return value ? '是' : '否' -} - -// 多IP节点颜色 -const getMultiIPColor = (value: number | boolean): string => { - if (typeof value === 'number') { - switch (value) { - case 1: return 'bg-red-100 text-red-800' - case 0: return 'bg-green-100 text-green-800' - case -1: return 'bg-gray-100 text-gray-800' - default: return 'bg-gray-100 text-gray-800' + // 独享IP节点格式化 + const formatExclusiveIP = (value: number | boolean): string => { + if (typeof value === 'number') { + return value === 1 ? '是' : '否' } + return value ? '是' : '否' } - return value ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' -} -// 独享IP节点颜色 -const getExclusiveIPColor = (value: number | boolean): string => { - if (typeof value === 'number') { - return value === 1 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' + // 多IP节点颜色 + const getMultiIPColor = (value: number | boolean): string => { + if (typeof value === 'number') { + switch (value) { + case 1: return 'bg-red-100 text-red-800' + case 0: return 'bg-green-100 text-green-800' + case -1: return 'bg-gray-100 text-gray-800' + default: return 'bg-gray-100 text-gray-800' + } + } + return value ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' + } + + // 独享IP节点颜色 + const getExclusiveIPColor = (value: number | boolean): string => { + if (typeof value === 'number') { + return value === 1 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' + } + return value ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' } - return value ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' -} const formatArchType = (arch: number): string => { switch (arch) { @@ -186,7 +188,7 @@ const getExclusiveIPColor = (value: number | boolean): string => {
) : ( <> -
+
@@ -209,11 +211,15 @@ const getExclusiveIPColor = (value: number | boolean): string => { {item.public} + item.isp === '移动' + ? 'bg-blue-100 text-blue-800' + : item.isp === '电信' + ? 'bg-purple-100 text-purple-800' + : item.isp === '联通' + ? 'bg-red-100 text-red-800' + : 'bg-gray-100 text-gray-800' + }`} + > {item.isp} @@ -239,7 +245,7 @@ const getExclusiveIPColor = (value: number | boolean): string => {
- + {/* 分页 */} { )}
) -} \ No newline at end of file +} diff --git a/src/app/dashboard/components/gatewayConfig.tsx b/src/app/dashboard/components/gatewayConfig.tsx index bbe9bdc..8f282c8 100644 --- a/src/app/dashboard/components/gatewayConfig.tsx +++ b/src/app/dashboard/components/gatewayConfig.tsx @@ -7,11 +7,11 @@ import { Pagination } from '@/components/ui/pagination' interface GatewayConfig { city: string edge: string - user: string + user: string public: string - inner_ip: string - ischange: number - isonline: number + inner_ip: string + ischange: number + isonline: number } interface ApiResponse { @@ -22,12 +22,12 @@ interface ApiResponse { } function GatewayConfigContent() { - const [data, setData] = useState([]) - const [loading, setLoading] = useState(false) + const [data, setData] = useState([]) + const [loading, setLoading] = useState(false) const [macAddress, setMacAddress] = useState('') const [error, setError] = useState('') const searchParams = useSearchParams() - + // 分页状态 const [currentPage, setCurrentPage] = useState(1) const [itemsPerPage, setItemsPerPage] = useState(100) @@ -43,7 +43,8 @@ function GatewayConfigContent() { setMacAddress(urlMac) setCurrentPage(1) // 重置到第一页 fetchData(urlMac, 1, itemsPerPage) - } else { + } + else { setMacAddress('') setCurrentPage(1) // 重置到第一页 fetchData('', 1, itemsPerPage) @@ -53,45 +54,48 @@ function GatewayConfigContent() { const fetchData = async (mac: string, page: number = 1, limit: number = itemsPerPage) => { setLoading(true) setError('') - + try { // 计算偏移量 const offset = (page - 1) * limit - + // 构建API URL let apiUrl = `/api/stats?type=gateway_config&offset=${offset}&limit=${limit}` if (mac.trim()) { apiUrl += `&mac=${encodeURIComponent(mac)}` } - + const response = await fetch(apiUrl) if (!response.ok) { throw new Error(`HTTP错误! 状态: ${response.status}`) } - + const result: ApiResponse = await response.json() - + // 检查返回的数据是否有效 if (!result.data || result.data.length === 0) { if (mac.trim()) { setError(`未找到MAC地址为 ${mac} 的网关配置信息`) - } else { + } + else { setError('未找到任何网关配置信息') } setData([]) setTotalItems(0) return } - + setData(result.data) - + setTotalItems(result.totalCount || 0) - } catch (error) { + } + catch (error) { console.error('获取网关配置失败:', error) setError(error instanceof Error ? error.message : '获取网关配置失败') setData([]) setTotalItems(0) - } finally { + } + finally { setLoading(false) } } @@ -143,14 +147,14 @@ function GatewayConfigContent() {

查询和管理网关设备的配置信息

- + {/* 查询表单 */}
setMacAddress(e.target.value)} + onChange={e => setMacAddress(e.target.value)} placeholder="输入MAC地址查询" className="px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" /> @@ -161,7 +165,7 @@ function GatewayConfigContent() { 查询
- + {error && (
@@ -173,15 +177,15 @@ function GatewayConfigContent() {
)} - + {loading ? (

正在查询网关配置信息...

) : data.length > 0 ? ( - <> -
+ <> +
@@ -243,7 +247,7 @@ function GatewayConfigContent() { - + {/* 分页组件 - 仅在非MAC查询时显示 */} {!isMacQuery && (

加载搜索参数...

- }> + )}> ) -} \ No newline at end of file +} diff --git a/src/app/dashboard/components/gatewayinfo.tsx b/src/app/dashboard/components/gatewayinfo.tsx index 281e242..1fee98b 100644 --- a/src/app/dashboard/components/gatewayinfo.tsx +++ b/src/app/dashboard/components/gatewayinfo.tsx @@ -25,11 +25,11 @@ type FilterSchema = z.infer // IP地址排序函数 const sortByIpAddress = (a: string, b: string): number => { const ipToNumber = (ip: string): number => { - const parts = ip.split('.').map(part => parseInt(part, 10)); - return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3]; - }; - - return ipToNumber(a) - ipToNumber(b); + const parts = ip.split('.').map(part => parseInt(part, 10)) + return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3] + } + + return ipToNumber(a) - ipToNumber(b) } export default function Gatewayinfo() { @@ -38,14 +38,14 @@ export default function Gatewayinfo() { const [loading, setLoading] = useState(true) const [error, setError] = useState('') const router = useRouter() - + const form = useForm({ resolver: zodResolver(filterSchema), defaultValues: { status: '1', }, }) - + const { watch } = form const statusFilter = watch('status') @@ -53,21 +53,23 @@ export default function Gatewayinfo() { fetchData() }, []) -useEffect(() => { - if (!data.length) return - - if (statusFilter === 'all') { - setFilteredData(data) - } else { - const enableValue = parseInt(statusFilter) - // 添加 NaN 检查 - if (isNaN(enableValue)) { + useEffect(() => { + if (!data.length) return + + if (statusFilter === 'all') { setFilteredData(data) - } else { - setFilteredData(data.filter(item => item.enable === enableValue)) } - } -}, [data, statusFilter]) + else { + const enableValue = parseInt(statusFilter) + // 添加 NaN 检查 + if (isNaN(enableValue)) { + setFilteredData(data) + } + else { + setFilteredData(data.filter(item => item.enable === enableValue)) + } + } + }, [data, statusFilter]) const fetchData = async () => { try { @@ -79,15 +81,17 @@ useEffect(() => { } const result = await response.json() // const sortedData = result.sort(( a, b) => Number(a.inner_ip) - Number(b.inner_ip)) - const sortedData = result.sort((a: GatewayInfo, b: GatewayInfo) => - sortByIpAddress(a.inner_ip, b.inner_ip) + const sortedData = result.sort((a: GatewayInfo, b: GatewayInfo) => + sortByIpAddress(a.inner_ip, b.inner_ip), ) setData(sortedData) setFilteredData(sortedData) // 初始化时设置filteredData - } catch (error) { + } + catch (error) { console.error('Failed to fetch gateway info:', error) setError(error instanceof Error ? error.message : '获取网关信息失败') - } finally { + } + finally { setLoading(false) } } @@ -97,8 +101,8 @@ useEffect(() => { } const getStatusClass = (enable: number) => { - return enable === 1 - ? 'bg-green-100 text-green-800' + return enable === 1 + ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' } @@ -118,12 +122,12 @@ useEffect(() => {
{error}
) - } + } return (
-
-
+
+
网关基本信息
@@ -133,8 +137,8 @@ useEffect(() => { render={({ field }) => (
状态筛选: -
@@ -169,14 +173,14 @@ useEffect(() => { {filteredData.map((item, index) => ( - -
@@ -298,7 +299,7 @@ const handleDeleteUser = async (userId: number) => { ) : ( - filteredUsers.map((user) => ( + filteredUsers.map(user => ( {user.account} {new Date(user.createdAt).toLocaleDateString()} @@ -309,7 +310,8 @@ const handleDeleteUser = async (userId: number) => { size="sm" className="h-5 border-0 hover:bg-transparent" onClick={() => handleDeleteUser(Number(user.id))} - > + > + @@ -323,4 +325,4 @@ const handleDeleteUser = async (userId: number) => { ) -} \ No newline at end of file +} diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 89d75df..3ed9238 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -16,7 +16,7 @@ const tabs = [ { id: 'city', label: '城市信息' }, { id: 'allocation', label: '分配状态' }, { id: 'edge', label: '节点信息' }, - { id: 'setting', label: '设置'} + { id: 'setting', label: '设置' }, ] function DashboardContent() { @@ -24,7 +24,7 @@ function DashboardContent() { const [isLoading, setIsLoading] = useState(false) const router = useRouter() const searchParams = useSearchParams() - + // 监听URL参数变化 useEffect(() => { const urlTab = searchParams.get('tab') @@ -40,17 +40,20 @@ function DashboardContent() { const response = await fetch('/api/auth/logout', { method: 'POST', }) - + if (response.ok) { // 退出成功后跳转到登录页 router.push('/login') router.refresh() - } else { + } + else { console.error('退出失败') } - } catch (error) { + } + catch (error) { console.error('退出错误:', error) - } finally { + } + finally { setIsLoading(false) } } @@ -71,7 +74,7 @@ function DashboardContent() {

网络节点管理系统

- + {/* 简化的退出按钮 */} ) -} \ No newline at end of file +} diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx index 524b986..6233f94 100644 --- a/src/components/ui/form.tsx +++ b/src/components/ui/form.tsx @@ -1,8 +1,8 @@ -"use client" +'use client' -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { Slot } from "@radix-ui/react-slot" +import * as React from 'react' +import * as LabelPrimitive from '@radix-ui/react-label' +import { Slot } from '@radix-ui/react-slot' import { Controller, FormProvider, @@ -11,10 +11,10 @@ import { type ControllerProps, type FieldPath, type FieldValues, -} from "react-hook-form" +} from 'react-hook-form' -import { cn } from "@/lib/utils" -import { Label } from "@/components/ui/label" +import { cn } from '@/lib/utils' +import { Label } from '@/components/ui/label' const Form = FormProvider @@ -26,7 +26,7 @@ type FormFieldContextValue< } const FormFieldContext = React.createContext( - {} as FormFieldContextValue + {} as FormFieldContextValue, ) const FormField = < @@ -50,7 +50,7 @@ const useFormField = () => { const fieldState = getFieldState(fieldContext.name, formState) if (!fieldContext) { - throw new Error("useFormField should be used within ") + throw new Error('useFormField should be used within ') } const { id } = itemContext @@ -70,17 +70,17 @@ type FormItemContextValue = { } const FormItemContext = React.createContext( - {} as FormItemContextValue + {} as FormItemContextValue, ) -function FormItem({ className, ...props }: React.ComponentProps<"div">) { +function FormItem({ className, ...props }: React.ComponentProps<'div'>) { const id = React.useId() return (
@@ -97,7 +97,7 @@ function FormLabel({
) -} \ No newline at end of file +} diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx index c04ce9a..91968c2 100644 --- a/src/components/ui/pagination.tsx +++ b/src/components/ui/pagination.tsx @@ -1,6 +1,6 @@ 'use client' import * as React from 'react' -import {useState, useEffect} from 'react' +import { useState, useEffect } from 'react' import { ChevronLeftIcon, @@ -8,7 +8,7 @@ import { MoreHorizontalIcon, } from 'lucide-react' -import {cn} from '@/lib/utils' +import { cn } from '@/lib/utils' import { Select, @@ -53,7 +53,7 @@ function Pagination({ if (totalPages <= 7) { // 总页数少于7,全部显示 - return Array.from({length: totalPages}, (_, i) => i + 1) + return Array.from({ length: totalPages }, (_, i) => i + 1) } // 是否需要显示左边的省略号 @@ -68,20 +68,20 @@ function Pagination({ const rightSiblingIndex = Math.min(currentPage + SIBLINGS, totalPages) return [1, DOTS, ...Array.from( - {length: rightSiblingIndex - leftSiblingIndex + 1}, + { length: rightSiblingIndex - leftSiblingIndex + 1 }, (_, i) => leftSiblingIndex + i, ), DOTS, totalPages] } if (!showLeftDots && showRightDots) { // 只有右边有省略号 - return [...Array.from({length: 3 + SIBLINGS * 2}, (_, i) => i + 1), DOTS, totalPages] + return [...Array.from({ length: 3 + SIBLINGS * 2 }, (_, i) => i + 1), DOTS, totalPages] } if (showLeftDots && !showRightDots) { // 只有左边有省略号 return [1, DOTS, ...Array.from( - {length: 3 + SIBLINGS * 2}, + { length: 3 + SIBLINGS * 2 }, (_, i) => totalPages - (3 + SIBLINGS * 2) + i + 1, )] } @@ -119,7 +119,7 @@ function Pagination({ onValueChange={handlePageSizeChange} > - + {sizeOptions.map(option => ( @@ -146,7 +146,7 @@ function Pagination({ if (pageNum === -1) { return ( - + ) } @@ -176,7 +176,7 @@ function Pagination({ ) } -function PaginationLayout({className, ...props}: React.ComponentProps<'nav'>) { +function PaginationLayout({ className, ...props }: React.ComponentProps<'nav'>) { return (
) } -function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { +function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) { return ( ) } -function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { +function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) { return ( ) } -function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { +function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) { return ( tr]:last:border-b-0", - className + 'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', + className, )} {...props} /> ) } -function TableRow({ className, ...props }: React.ComponentProps<"tr">) { +function TableRow({ className, ...props }: React.ComponentProps<'tr'>) { return ( ) } -function TableHead({ className, ...props }: React.ComponentProps<"th">) { +function TableHead({ className, ...props }: React.ComponentProps<'th'>) { return (
[role=checkbox]]:translate-y-[2px]", - className + 'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', + className, )} {...props} /> ) } -function TableCell({ className, ...props }: React.ComponentProps<"td">) { +function TableCell({ className, ...props }: React.ComponentProps<'td'>) { return ( [role=checkbox]]:translate-y-[2px]", - className + 'p-2 h-10 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', + className, )} {...props} /> @@ -94,11 +94,11 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) { function TableCaption({ className, ...props -}: React.ComponentProps<"caption">) { +}: React.ComponentProps<'caption'>) { return (
) diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index 497ba5e..cdd7eae 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -1,9 +1,9 @@ -"use client" +'use client' -import * as React from "react" -import * as TabsPrimitive from "@radix-ui/react-tabs" +import * as React from 'react' +import * as TabsPrimitive from '@radix-ui/react-tabs' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' function Tabs({ className, @@ -12,7 +12,7 @@ function Tabs({ return ( ) @@ -26,8 +26,8 @@ function TabsList({ @@ -42,8 +42,8 @@ function TabsTrigger({ @@ -57,7 +57,7 @@ function TabsContent({ return ( ) diff --git a/src/lib/formatters.ts b/src/lib/formatters.ts index 9e5bc2c..f0a857b 100644 --- a/src/lib/formatters.ts +++ b/src/lib/formatters.ts @@ -1,26 +1,26 @@ // 数字格式化工具函数 export const formatNumber = (num: number | string): string => { - const numberValue = typeof num === 'string' ? parseInt(num) || 0 : num - return numberValue.toLocaleString('zh-CN') + const numberValue = typeof num === 'string' ? parseInt(num) || 0 : num + return numberValue.toLocaleString('zh-CN') } export const formatLargeNumber = (num: number | string): string => { - const numberValue = typeof num === 'string' ? parseInt(num) || 0 : num + const numberValue = typeof num === 'string' ? parseInt(num) || 0 : num - if (numberValue > 1e9) return `${(numberValue / 1e9).toFixed(1)}亿` - if (numberValue > 1e6) return `${(numberValue / 1e6).toFixed(1)}百万` - if (numberValue > 1e4) return `${(numberValue / 1e4).toFixed(1)}万` - if (numberValue > 1e3) return `${(numberValue / 1e3).toFixed(1)}千` + if (numberValue > 1e9) return `${(numberValue / 1e9).toFixed(1)}亿` + if (numberValue > 1e6) return `${(numberValue / 1e6).toFixed(1)}百万` + if (numberValue > 1e4) return `${(numberValue / 1e4).toFixed(1)}万` + if (numberValue > 1e3) return `${(numberValue / 1e3).toFixed(1)}千` - return numberValue.toLocaleString('zh-CN') + return numberValue.toLocaleString('zh-CN') } // 数据验证函数 export const validateNumber = (value: unknown): number => { - if (typeof value === 'number') return value - if (typeof value === 'string') { - const num = parseInt(value) - return isNaN(num) ? 0 : num - } - return 0 -} \ No newline at end of file + if (typeof value === 'number') return value + if (typeof value === 'string') { + const num = parseInt(value) + return isNaN(num) ? 0 : num + } + return 0 +} diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 1861698..3ea6fa9 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -1,13 +1,13 @@ -import { PrismaClient } from "@/generated/prisma/client" +import { PrismaClient } from '@/generated/prisma/client' const globalForPrisma = global as unknown as { - prisma: PrismaClient | undefined + prisma: PrismaClient | undefined } export const prisma = globalForPrisma.prisma ?? new PrismaClient() if (process.env.NODE_ENV !== 'production') { - globalForPrisma.prisma = prisma + globalForPrisma.prisma = prisma } -export default prisma \ No newline at end of file +export default prisma diff --git a/src/lib/utils.ts b/src/lib/utils.ts index bd0c391..fed2fe9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,5 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" +import { clsx, type ClassValue } from 'clsx' +import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) diff --git a/src/middleware.ts b/src/middleware.ts index e7aa41d..42aa09e 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -9,7 +9,7 @@ export const config = { const isIgnored = [ '/login', - "/api/auth/login" + '/api/auth/login', ] export async function middleware(request: NextRequest) { diff --git a/src/store/auth.ts b/src/store/auth.ts index c322e1b..2f995a8 100644 --- a/src/store/auth.ts +++ b/src/store/auth.ts @@ -8,12 +8,12 @@ interface AuthState { export const useAuthStore = create()( persist( - (set) => ({ + set => ({ isAuthenticated: false, - setAuth: (state) => set({ isAuthenticated: state }), + setAuth: state => set({ isAuthenticated: state }), }), { name: 'auth-storage', - } - ) + }, + ), )