diff --git a/src/app/api/admin/orders/[id]/cancel/route.ts b/src/app/api/admin/orders/[id]/cancel/route.ts index 9c29315..5c88f3c 100644 --- a/src/app/api/admin/orders/[id]/cancel/route.ts +++ b/src/app/api/admin/orders/[id]/cancel/route.ts @@ -3,7 +3,7 @@ import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth'; import { adminCancelOrder, OrderError } from '@/lib/order/service'; export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - if (!verifyAdminToken(request)) return unauthorizedResponse(); + if (!await verifyAdminToken(request)) return unauthorizedResponse(); try { const { id } = await params; diff --git a/src/app/api/admin/orders/[id]/retry/route.ts b/src/app/api/admin/orders/[id]/retry/route.ts index 3afbd44..a5764fd 100644 --- a/src/app/api/admin/orders/[id]/retry/route.ts +++ b/src/app/api/admin/orders/[id]/retry/route.ts @@ -3,7 +3,7 @@ import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth'; import { retryRecharge, OrderError } from '@/lib/order/service'; export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - if (!verifyAdminToken(request)) return unauthorizedResponse(); + if (!await verifyAdminToken(request)) return unauthorizedResponse(); try { const { id } = await params; diff --git a/src/app/api/admin/orders/[id]/route.ts b/src/app/api/admin/orders/[id]/route.ts index 5127f13..6fe2af1 100644 --- a/src/app/api/admin/orders/[id]/route.ts +++ b/src/app/api/admin/orders/[id]/route.ts @@ -3,7 +3,7 @@ import { prisma } from '@/lib/db'; import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth'; export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - if (!verifyAdminToken(request)) return unauthorizedResponse(); + if (!await verifyAdminToken(request)) return unauthorizedResponse(); const { id } = await params; diff --git a/src/app/api/admin/orders/route.ts b/src/app/api/admin/orders/route.ts index 9d85b07..5e59262 100644 --- a/src/app/api/admin/orders/route.ts +++ b/src/app/api/admin/orders/route.ts @@ -4,7 +4,7 @@ import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth'; import { Prisma, OrderStatus } from '@prisma/client'; export async function GET(request: NextRequest) { - if (!verifyAdminToken(request)) return unauthorizedResponse(); + if (!await verifyAdminToken(request)) return unauthorizedResponse(); const searchParams = request.nextUrl.searchParams; const page = Math.max(1, Number(searchParams.get('page') || '1')); diff --git a/src/app/api/admin/refund/route.ts b/src/app/api/admin/refund/route.ts index e14b185..2a4bd44 100644 --- a/src/app/api/admin/refund/route.ts +++ b/src/app/api/admin/refund/route.ts @@ -10,7 +10,7 @@ const refundSchema = z.object({ }); export async function POST(request: NextRequest) { - if (!verifyAdminToken(request)) return unauthorizedResponse(); + if (!await verifyAdminToken(request)) return unauthorizedResponse(); try { const body = await request.json(); diff --git a/src/lib/admin-auth.ts b/src/lib/admin-auth.ts index 19fa0ff..8fce9dd 100644 --- a/src/lib/admin-auth.ts +++ b/src/lib/admin-auth.ts @@ -2,10 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getEnv } from '@/lib/config'; import crypto from 'crypto'; -export function verifyAdminToken(request: NextRequest): boolean { - const token = request.nextUrl.searchParams.get('token'); - if (!token) return false; - +function isLocalAdminToken(token: string): boolean { const env = getEnv(); const expected = Buffer.from(env.ADMIN_TOKEN); const received = Buffer.from(token); @@ -14,6 +11,31 @@ export function verifyAdminToken(request: NextRequest): boolean { return crypto.timingSafeEqual(expected, received); } +async function isSub2ApiAdmin(token: string): Promise { + try { + const env = getEnv(); + const response = await fetch(`${env.SUB2API_BASE_URL}/api/v1/auth/me`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!response.ok) return false; + const data = await response.json(); + return data.data?.role === 'admin'; + } catch { + return false; + } +} + +export async function verifyAdminToken(request: NextRequest): Promise { + const token = request.nextUrl.searchParams.get('token'); + if (!token) return false; + + // 1. 本地 admin token + if (isLocalAdminToken(token)) return true; + + // 2. Sub2API 管理员 token + return isSub2ApiAdmin(token); +} + export function unauthorizedResponse() { return NextResponse.json({ error: '未授权' }, { status: 401 }); }