import { NextRequest, NextResponse } from 'next/server'; import { getPublicProjectDb } from '@/lib/project-auth'; import { verifyLicenseRequestSignature } from '@/lib/request-signature'; import { generateLicenseConfig, type HardwareFingerprint } from '@/lib/types'; import { calculateFingerprintMatchScore, createPendingAlertOnce, isSamePersistentInstance, } from '@/lib/abuse-detection'; import { getAuthorizedModuleGrant } from '@/lib/module-grants'; import { parseRegistryImage } from '@/lib/premium-module-catalog'; import { createModulePullCredential, hashModulePullToken, } from '@/lib/module-pull-token'; import { z } from 'zod'; const premiumModulePullTokenSchema = z.object({ instanceId: z.string(), fingerprint: z.object({ machineId: z.string(), hostname: z.string(), macAddress: z.string(), cpuId: z.string().optional(), instanceId: z.string(), }), timestamp: z.number(), }); export async function POST( request: NextRequest, { params }: { params: Promise<{ projectCode: string; moduleId: string }> } ) { try { const { projectCode, moduleId } = await params; const projectDbResult = await getPublicProjectDb(projectCode); if (!projectDbResult.success) { return projectDbResult.response; } const body = await request.json().catch(() => ({})); const signatureResult = verifyLicenseRequestSignature('premium-module-pull-token', projectCode, { ...body, moduleId, }); if (!signatureResult.valid) { return NextResponse.json( { success: false, error: '请求签名无效' }, { status: signatureResult.response.status } ); } const parsed = premiumModulePullTokenSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { success: false, error: '请求参数无效' }, { status: 400 } ); } const { instanceId, fingerprint, timestamp } = parsed.data; if (Math.abs(Date.now() - timestamp) > 5 * 60 * 1000) { return NextResponse.json( { success: false, error: '请求已过期' }, { status: 400 } ); } const activation = await projectDbResult.prisma.activation.findFirst({ where: { instanceId, isActive: true, }, include: { license: true, }, }); if (!activation) { return NextResponse.json( { success: false, error: '激活记录不存在' }, { status: 404 } ); } const { license } = activation; if (license.status === 'disabled') { return NextResponse.json( { success: false, error: '许可证已被禁用' }, { status: 403 } ); } if (license.status === 'expired' || (license.expiresAt && license.expiresAt < new Date())) { return NextResponse.json( { success: false, error: '许可证已过期' }, { status: 403 } ); } const storedFingerprint = activation.fingerprint as unknown as HardwareFingerprint; const matchScore = calculateFingerprintMatchScore(storedFingerprint, fingerprint); if (!isSamePersistentInstance(storedFingerprint, fingerprint) && matchScore < 70) { await createPendingAlertOnce(projectDbResult.prisma, license.id, 'premium_module_pull_token_fingerprint_mismatch', { instanceId, moduleId, matchScore, }); return NextResponse.json( { success: false, error: '硬件指纹不匹配,无法获取模块拉取凭证' }, { status: 403 } ); } const grant = await getAuthorizedModuleGrant(projectDbResult.prisma, { id: license.id, licenseType: license.licenseType, expiresAt: license.expiresAt, }, moduleId); if (!grant) { return NextResponse.json( { success: false, error: '当前许可证不允许拉取该授权模块' }, { status: 403 } ); } const config = generateLicenseConfig(license.licenseType, license.expiresAt); const leaseExpiresAt = new Date(config.leaseExpiresAt || Date.now()); if (leaseExpiresAt <= new Date()) { return NextResponse.json( { success: false, error: '许可证租约已过期' }, { status: 403 } ); } const parsedImage = parseRegistryImage(grant.artifact.image); const { credential } = createModulePullCredential({ projectCode, licenseId: license.id, activationId: activation.id, instanceId, moduleId, registryHost: parsedImage.registryHost, repository: parsedImage.repository, tag: parsedImage.tag, digest: grant.artifact.digest || null, }); await projectDbResult.prisma.modulePullTokenAudit.create({ data: { licenseId: license.id, activationId: activation.id, moduleId, repository: parsedImage.repository, digest: grant.artifact.digest || null, tokenHash: hashModulePullToken(credential.pullToken), expiresAt: new Date(credential.expiresAt), }, }); return NextResponse.json({ success: true, pullCredential: credential, }); } catch (error) { console.error('模块拉取凭证生成失败:', error); return NextResponse.json( { success: false, error: '服务器内部错误' }, { status: 500 } ); } }