fix: API 路由安全加固与架构优化 — 认证、错误处理、Registry 统一
- /api/user 添加 token 认证,防止用户枚举 - Admin token 支持 Authorization header - /api/orders/my 区分认证失败和服务端错误 - Admin orders userId/date 参数校验 - Decimal 字段统一 Number() 转换 - 抽取 handleApiError/extractHeaders 工具函数 - Webhook 路由改用 Registry 获取 Provider - PaymentRegistry lazy init 自动初始化 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth';
|
||||
import { adminCancelOrder, OrderError } from '@/lib/order/service';
|
||||
import { adminCancelOrder } from '@/lib/order/service';
|
||||
import { handleApiError } from '@/lib/utils/api';
|
||||
|
||||
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
if (!(await verifyAdminToken(request))) return unauthorizedResponse();
|
||||
@@ -13,10 +14,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
}
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
if (error instanceof OrderError) {
|
||||
return NextResponse.json({ error: error.message, code: error.code }, { status: error.statusCode });
|
||||
}
|
||||
console.error('Admin cancel order error:', error);
|
||||
return NextResponse.json({ error: '取消订单失败' }, { status: 500 });
|
||||
return handleApiError(error, '取消订单失败');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth';
|
||||
import { retryRecharge, OrderError } from '@/lib/order/service';
|
||||
import { retryRecharge } from '@/lib/order/service';
|
||||
import { handleApiError } from '@/lib/utils/api';
|
||||
|
||||
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
if (!(await verifyAdminToken(request))) return unauthorizedResponse();
|
||||
@@ -10,10 +11,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
await retryRecharge(id);
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
if (error instanceof OrderError) {
|
||||
return NextResponse.json({ error: error.message, code: error.code }, { status: error.statusCode });
|
||||
}
|
||||
console.error('Retry recharge error:', error);
|
||||
return NextResponse.json({ error: '重试充值失败' }, { status: 500 });
|
||||
return handleApiError(error, '重试充值失败');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
|
||||
return NextResponse.json({
|
||||
...order,
|
||||
amount: Number(order.amount),
|
||||
payAmount: order.payAmount ? Number(order.payAmount) : null,
|
||||
feeRate: order.feeRate ? Number(order.feeRate) : null,
|
||||
refundAmount: order.refundAmount ? Number(order.refundAmount) : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,11 +16,38 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const where: Prisma.OrderWhereInput = {};
|
||||
if (status && status in OrderStatus) where.status = status as OrderStatus;
|
||||
if (userId) where.userId = Number(userId);
|
||||
|
||||
// userId 校验:忽略无效值(NaN)
|
||||
if (userId) {
|
||||
const parsedUserId = Number(userId);
|
||||
if (Number.isFinite(parsedUserId)) {
|
||||
where.userId = parsedUserId;
|
||||
}
|
||||
}
|
||||
|
||||
// 日期校验:忽略无效日期
|
||||
if (dateFrom || dateTo) {
|
||||
where.createdAt = {};
|
||||
if (dateFrom) where.createdAt.gte = new Date(dateFrom);
|
||||
if (dateTo) where.createdAt.lte = new Date(dateTo);
|
||||
const createdAt: Prisma.DateTimeFilter = {};
|
||||
let hasValidDate = false;
|
||||
|
||||
if (dateFrom) {
|
||||
const d = new Date(dateFrom);
|
||||
if (!isNaN(d.getTime())) {
|
||||
createdAt.gte = d;
|
||||
hasValidDate = true;
|
||||
}
|
||||
}
|
||||
if (dateTo) {
|
||||
const d = new Date(dateTo);
|
||||
if (!isNaN(d.getTime())) {
|
||||
createdAt.lte = d;
|
||||
hasValidDate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidDate) {
|
||||
where.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
const [orders, total] = await Promise.all([
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth';
|
||||
import { processRefund, OrderError } from '@/lib/order/service';
|
||||
import { processRefund } from '@/lib/order/service';
|
||||
import { handleApiError } from '@/lib/utils/api';
|
||||
|
||||
const refundSchema = z.object({
|
||||
order_id: z.string().min(1),
|
||||
@@ -28,10 +29,6 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
return NextResponse.json(result);
|
||||
} catch (error) {
|
||||
if (error instanceof OrderError) {
|
||||
return NextResponse.json({ error: error.message, code: error.code }, { status: error.statusCode });
|
||||
}
|
||||
console.error('Refund error:', error);
|
||||
return NextResponse.json({ error: '退款失败' }, { status: 500 });
|
||||
return handleApiError(error, '退款失败');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user