88 lines
3.1 KiB
TypeScript
88 lines
3.1 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
||
import { generateFingerprint } from '@/lib/license/fingerprint-service';
|
||
import { updateLocalLicense, getLocalLicense, clearLocalLicense } from '@/lib/license/license-service';
|
||
import { verifyLicenseSignature } from '@/lib/license/crypto-service';
|
||
import { buildLicenseServerLicenseApiUrl } from '@/lib/license/server-config';
|
||
import type { VerifyResponse } from '@/lib/license/types';
|
||
|
||
// 宽限期(7天)
|
||
const GRACE_PERIOD_DAYS = 7;
|
||
const GRACE_PERIOD_MS = GRACE_PERIOD_DAYS * 24 * 60 * 60 * 1000;
|
||
|
||
function isWithinGracePeriod(lastVerified: Date | string | null): boolean {
|
||
if (!lastVerified) return false;
|
||
const lastVerifiedDate = typeof lastVerified === 'string' ? new Date(lastVerified) : lastVerified;
|
||
const now = new Date();
|
||
return (now.getTime() - lastVerifiedDate.getTime()) < GRACE_PERIOD_MS;
|
||
}
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
// 先检查本地缓存
|
||
const localLicense = await getLocalLicense();
|
||
|
||
if (!localLicense) {
|
||
return NextResponse.json({ valid: false, error: '未找到许可证' });
|
||
}
|
||
|
||
// ✅ 安全改进:在后端生成硬件指纹(不暴露给客户端)
|
||
const fingerprint = await generateFingerprint();
|
||
|
||
// ✅ 安全改进:代理请求到许可证服务器(隐藏服务器地址)
|
||
const response = await fetch(buildLicenseServerLicenseApiUrl('verify'), {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
instanceId: fingerprint.instanceId,
|
||
fingerprint,
|
||
timestamp: Date.now(),
|
||
}),
|
||
});
|
||
|
||
const data: VerifyResponse = await response.json();
|
||
|
||
if (!data.valid || !data.license) {
|
||
// 检查是否是被吊销(禁用/删除/过期)
|
||
const isRevoked = response.status === 403 || response.status === 404;
|
||
|
||
if (isRevoked) {
|
||
// 许可证被吊销,清除本地缓存
|
||
console.log('[License] 许可证已被吊销:', data.error);
|
||
await clearLocalLicense();
|
||
return NextResponse.json({
|
||
valid: false,
|
||
error: data.error || '许可证已被吊销',
|
||
revoked: true
|
||
});
|
||
}
|
||
|
||
// 其他错误(如网络问题),检查宽限期
|
||
if (isWithinGracePeriod(localLicense.lastVerified)) {
|
||
return NextResponse.json({ valid: true });
|
||
}
|
||
return NextResponse.json({ valid: false, error: data.error || '验证失败' });
|
||
}
|
||
|
||
// ✅ 在后端验证签名
|
||
if (!verifyLicenseSignature(data.license.data, data.license.signature)) {
|
||
return NextResponse.json({ valid: false, error: '许可证签名验证失败' });
|
||
}
|
||
|
||
// ✅ 在后端更新本地缓存
|
||
await updateLocalLicense(data.license);
|
||
|
||
return NextResponse.json({ valid: true });
|
||
} catch (error) {
|
||
console.error('验证失败:', error);
|
||
|
||
// 网络错误时检查宽限期
|
||
const localLicense = await getLocalLicense();
|
||
if (localLicense && isWithinGracePeriod(localLicense.lastVerified)) {
|
||
return NextResponse.json({ valid: true });
|
||
}
|
||
|
||
return NextResponse.json({ valid: false, error: '网络错误' });
|
||
}
|
||
}
|
||
|