126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
||
import { generateFingerprint } from '@/lib/license/fingerprint-service';
|
||
import { verifyLicenseSignature } from '@/lib/license/crypto-service';
|
||
import { saveLicenseLocally, initializeSystem, startHeartbeat } from '@/lib/license/license-service';
|
||
import { buildLicenseServerLicenseApiUrl } from '@/lib/license/server-config';
|
||
import { buildSignedLicenseRequest } from '@/lib/license/request-signature';
|
||
import { syncPremiumModules } from '@/lib/license/premium-module-service';
|
||
import { requireSuperAdminAfterInitialSetup } from '@/lib/license/license-route-guards';
|
||
import { validateSignedLicenseBinding } from '@/lib/license/license-binding';
|
||
import type { ActivateResponse } from '@/lib/license/types';
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const guard = await requireSuperAdminAfterInitialSetup();
|
||
if (!guard.allowed) {
|
||
return NextResponse.json(
|
||
{ success: false, error: guard.error },
|
||
{ status: guard.status }
|
||
);
|
||
}
|
||
|
||
const body = await request.json();
|
||
// 支持两种参数名:activationCode(新)和 licenseKey(旧)
|
||
const licenseKey = body.activationCode || body.licenseKey;
|
||
|
||
if (!licenseKey) {
|
||
return NextResponse.json(
|
||
{ success: false, error: '请输入激活码' },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// ✅ 安全改进:在后端生成硬件指纹(不暴露给客户端)
|
||
const fingerprint = await generateFingerprint();
|
||
|
||
// ✅ 安全改进:代理请求到许可证服务器(隐藏服务器地址)
|
||
const signedRequest = buildSignedLicenseRequest('activate', {
|
||
licenseKey,
|
||
fingerprint,
|
||
});
|
||
|
||
const response = await fetch(buildLicenseServerLicenseApiUrl('activate'), {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(signedRequest),
|
||
});
|
||
|
||
const data: ActivateResponse = await response.json();
|
||
|
||
if (!data.success || !data.license) {
|
||
return NextResponse.json(
|
||
{ success: false, error: data.error || '激活失败' },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// ✅ 在后端验证签名
|
||
if (!verifyLicenseSignature(data.license.data, data.license.signature)) {
|
||
return NextResponse.json(
|
||
{ success: false, error: '许可证签名验证失败' },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
const bindingResult = validateSignedLicenseBinding(data.license, {
|
||
licenseKey,
|
||
instanceId: fingerprint.instanceId,
|
||
});
|
||
if (!bindingResult.valid) {
|
||
return NextResponse.json(
|
||
{ success: false, error: bindingResult.error },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// ✅ 在后端保存许可证
|
||
await saveLicenseLocally(licenseKey, data.license);
|
||
const moduleSyncResult = await syncPremiumModules();
|
||
if (!moduleSyncResult.synced && !moduleSyncResult.skipped) {
|
||
console.warn('[License] 付费模块清单同步失败:', moduleSyncResult.error);
|
||
}
|
||
|
||
// 初始化系统(创建默认管理员、部门、店铺等)
|
||
const initResult = await initializeSystem();
|
||
|
||
// 启动心跳
|
||
startHeartbeat();
|
||
|
||
const result = NextResponse.json({
|
||
success: true,
|
||
initialAdmin: initResult.admin?.created
|
||
? {
|
||
email: initResult.admin.email,
|
||
username: initResult.admin.username,
|
||
password: initResult.admin.passwordSource === 'generated' ? initResult.admin.initialPassword || null : null,
|
||
mustChangePassword: initResult.admin.mustChangePassword,
|
||
passwordSource: initResult.admin.passwordSource || null,
|
||
}
|
||
: null,
|
||
});
|
||
|
||
// 自动检测协议:根据请求的协议决定 cookie 的 secure 属性
|
||
// - HTTP 请求:secure = false(内网部署)
|
||
// - HTTPS 请求:secure = true(Pro 版外网穿透)
|
||
const isHttps = request.headers.get('x-forwarded-proto') === 'https'
|
||
|| request.url.startsWith('https://');
|
||
|
||
// 设置许可证激活 cookie(用于 middleware 快速检查)
|
||
result.cookies.set('license_activated', 'true', {
|
||
httpOnly: true,
|
||
secure: isHttps,
|
||
sameSite: 'lax',
|
||
maxAge: 365 * 24 * 60 * 60, // 1年
|
||
path: '/',
|
||
});
|
||
|
||
return result;
|
||
} catch (error) {
|
||
console.error('激活失败:', error);
|
||
return NextResponse.json(
|
||
{ success: false, error: '服务器错误' },
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|