feat: move verify device to cron
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable no-console,@typescript-eslint/no-explicit-any */
|
/* eslint-disable no-console,@typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
import { getConfig, refineConfig } from '@/lib/config';
|
import { getConfig, refineConfig } from '@/lib/config';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
@@ -9,6 +10,307 @@ import { SearchResult } from '@/lib/types';
|
|||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
// 认证相关接口定义
|
||||||
|
export interface APIResponse {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data?: any;
|
||||||
|
timestamp: number;
|
||||||
|
signature: string;
|
||||||
|
server_fingerprint: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_SECRET = 'moontv-is-the-best';
|
||||||
|
// 验证服务器地址
|
||||||
|
const AUTH_SERVER = 'https://moontv-auth.ihtw.moe';
|
||||||
|
|
||||||
|
// 全局变量存储公钥和指纹
|
||||||
|
let serverPublicKey: crypto.KeyObject | null = null;
|
||||||
|
let expectedFingerprint = '';
|
||||||
|
|
||||||
|
// 验证相关的全局变量
|
||||||
|
let networkFailureCount = 0;
|
||||||
|
const MAX_NETWORK_FAILURES = 3;
|
||||||
|
let currentMachineCode = '';
|
||||||
|
|
||||||
|
// 设备认证初始化状态
|
||||||
|
let isDeviceAuthInitialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证响应签名
|
||||||
|
*/
|
||||||
|
async function verifyResponse(apiResp: APIResponse, requestTimestamp: string): Promise<void> {
|
||||||
|
if (!serverPublicKey) {
|
||||||
|
throw new Error('服务器公钥未初始化');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证服务器指纹
|
||||||
|
if (apiResp.server_fingerprint !== expectedFingerprint) {
|
||||||
|
throw new Error('服务器指纹验证失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const timestampToVerify = requestTimestamp;
|
||||||
|
const verified = await verifyTimestampSignature(timestampToVerify, apiResp.signature);
|
||||||
|
|
||||||
|
if (!verified) {
|
||||||
|
throw new Error('时间戳签名验证失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`签名验证失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyTimestampSignature(timestamp: string, signature: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
if (!serverPublicKey) {
|
||||||
|
console.error('❌ 服务器公钥未初始化');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将时间戳转换为字符串(与Go服务端保持一致)
|
||||||
|
const timestampString = String(timestamp);
|
||||||
|
|
||||||
|
// 将十六进制签名转换为Buffer
|
||||||
|
const signatureBuffer = Buffer.from(signature, 'hex');
|
||||||
|
|
||||||
|
// 使用正确的方法:验证原始时间戳字符串
|
||||||
|
// Go服务端实际上是对原始时间戳字符串进行签名的
|
||||||
|
const verifier = crypto.createVerify('RSA-SHA256');
|
||||||
|
verifier.update(timestampString, 'utf8');
|
||||||
|
|
||||||
|
const result = verifier.verify(serverPublicKey, signatureBuffer);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 时间戳签名验证出错:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServerInfo {
|
||||||
|
encrypted_public_key: string;
|
||||||
|
fingerprint: string;
|
||||||
|
encryption_method: string;
|
||||||
|
note: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从验证服务器获取公钥
|
||||||
|
*/
|
||||||
|
async function fetchServerPublicKey(): Promise<{ publicKey: string, fingerprint: string }> {
|
||||||
|
try {
|
||||||
|
// 设置10秒超时
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||||
|
|
||||||
|
const response = await fetch(`${AUTH_SERVER}/api/public_key`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'MoonTV/1.0.0'
|
||||||
|
},
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiResp: APIResponse = await response.json();
|
||||||
|
|
||||||
|
if (!apiResp.success) {
|
||||||
|
throw new Error(`获取公钥失败: ${apiResp.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverInfo = apiResp.data as ServerInfo;
|
||||||
|
const encryptedPublicKey = serverInfo.encrypted_public_key;
|
||||||
|
const serverFingerprint = serverInfo.fingerprint;
|
||||||
|
const decryptedPublicKeyPem = decryptWithAES(encryptedPublicKey, API_SECRET);
|
||||||
|
|
||||||
|
return {
|
||||||
|
publicKey: decryptedPublicKeyPem,
|
||||||
|
fingerprint: serverFingerprint
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`获取公钥失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用AES-GCM解密数据
|
||||||
|
*/
|
||||||
|
function decryptWithAES(encryptedData: string, key: string): string {
|
||||||
|
try {
|
||||||
|
// 将密钥转换为32字节(SHA256哈希)
|
||||||
|
const keyHash = crypto.createHash('sha256').update(key).digest();
|
||||||
|
|
||||||
|
// Base64解码密文
|
||||||
|
const encryptedBytes = Buffer.from(encryptedData, 'base64');
|
||||||
|
|
||||||
|
// 提取nonce(前12字节)和密文
|
||||||
|
const nonceSize = 12;
|
||||||
|
const nonce = encryptedBytes.slice(0, nonceSize);
|
||||||
|
const ciphertext = encryptedBytes.slice(nonceSize, -16); // 除去最后16字节的认证标签
|
||||||
|
const tag = encryptedBytes.slice(-16); // 最后16字节是认证标签
|
||||||
|
|
||||||
|
// 创建AES-GCM解密器
|
||||||
|
const decipher = crypto.createDecipheriv('aes-256-gcm', keyHash, nonce);
|
||||||
|
decipher.setAuthTag(tag);
|
||||||
|
|
||||||
|
const decrypted = decipher.update(ciphertext);
|
||||||
|
const final = decipher.final();
|
||||||
|
|
||||||
|
// 合并 Buffer 并转换为字符串
|
||||||
|
const result = Buffer.concat([decrypted, final]);
|
||||||
|
return result.toString('utf8');
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`AES解密失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证设备状态
|
||||||
|
*/
|
||||||
|
async function verifyDevice(): Promise<void> {
|
||||||
|
try {
|
||||||
|
console.log('🔄 开始设备验证...');
|
||||||
|
|
||||||
|
const config = await getConfig();
|
||||||
|
|
||||||
|
// 用户数量设置为0
|
||||||
|
const userCount = config.UserConfig?.Users?.length || 0;
|
||||||
|
|
||||||
|
// 生成请求时间戳
|
||||||
|
const requestTimestamp = Date.now().toString();
|
||||||
|
|
||||||
|
// 设置10秒超时
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||||
|
|
||||||
|
const response = await fetch(`${AUTH_SERVER}/api/verify_device`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'MoonTV/1.0.0'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
device_code: currentMachineCode,
|
||||||
|
auth_code: process.env.AUTH_TOKEN || '',
|
||||||
|
user_count: userCount,
|
||||||
|
timestamp: requestTimestamp
|
||||||
|
}),
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseBody = await response.text();
|
||||||
|
const apiResp: APIResponse = JSON.parse(responseBody);
|
||||||
|
|
||||||
|
// 验证响应签名(使用我们发送的时间戳)
|
||||||
|
await verifyResponse(apiResp, requestTimestamp);
|
||||||
|
|
||||||
|
if (!apiResp.success) {
|
||||||
|
console.error('❌ 设备验证失败');
|
||||||
|
console.error(`验证失败原因: ${apiResp.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置网络失败计数
|
||||||
|
networkFailureCount = 0;
|
||||||
|
console.log(`✅ 设备验证通过,用户数量: ${userCount}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : '未知错误';
|
||||||
|
|
||||||
|
// 判断是否为网络问题
|
||||||
|
const isNetworkError = errorMessage.includes('fetch') ||
|
||||||
|
errorMessage.includes('timeout') ||
|
||||||
|
errorMessage.includes('ECONNREFUSED') ||
|
||||||
|
errorMessage.includes('ETIMEDOUT') ||
|
||||||
|
errorMessage.includes('aborted');
|
||||||
|
|
||||||
|
if (isNetworkError) {
|
||||||
|
networkFailureCount++;
|
||||||
|
console.warn(`⚠️ 网络验证失败 (${networkFailureCount}/${MAX_NETWORK_FAILURES}): ${errorMessage}`);
|
||||||
|
|
||||||
|
if (networkFailureCount >= MAX_NETWORK_FAILURES) {
|
||||||
|
console.error('❌ 网络验证失败次数超过限制,重置认证信息');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非网络错误,直接退出
|
||||||
|
console.error('❌ 设备验证失败');
|
||||||
|
console.error(`验证失败原因: ${errorMessage}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化设备认证信息
|
||||||
|
*/
|
||||||
|
async function initializeDeviceAuth(): Promise<void> {
|
||||||
|
// 如果已经初始化过,直接返回
|
||||||
|
if (isDeviceAuthInitialized) {
|
||||||
|
console.log('🔑 设备认证信息已初始化,跳过重复初始化');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取环境变量
|
||||||
|
const authToken = process.env.AUTH_TOKEN;
|
||||||
|
const username = process.env.USERNAME;
|
||||||
|
const password = process.env.PASSWORD;
|
||||||
|
|
||||||
|
if (!authToken || !username || !password) {
|
||||||
|
console.log('⚠️ 缺少认证环境变量,跳过设备验证');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成机器码
|
||||||
|
const combinedString = authToken + username + password;
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const data = encoder.encode(combinedString);
|
||||||
|
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
||||||
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||||
|
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
const machineCode = hashHex.substring(0, 16);
|
||||||
|
currentMachineCode = machineCode;
|
||||||
|
|
||||||
|
// 从验证服务器获取公钥
|
||||||
|
const { publicKey, fingerprint } = await fetchServerPublicKey();
|
||||||
|
|
||||||
|
// 设置全局变量供签名验证使用
|
||||||
|
try {
|
||||||
|
serverPublicKey = crypto.createPublicKey({
|
||||||
|
key: publicKey,
|
||||||
|
format: 'pem',
|
||||||
|
type: 'spki'
|
||||||
|
});
|
||||||
|
} catch (keyError) {
|
||||||
|
console.error('❌ 公钥KeyObject创建失败:', keyError);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
expectedFingerprint = fingerprint;
|
||||||
|
|
||||||
|
// 标记为已初始化
|
||||||
|
isDeviceAuthInitialized = true;
|
||||||
|
console.log('🔑 设备认证信息初始化成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 设备认证信息初始化失败:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
console.log(request.url);
|
console.log(request.url);
|
||||||
try {
|
try {
|
||||||
@@ -37,6 +339,13 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function cronJob() {
|
async function cronJob() {
|
||||||
|
// 初始化设备认证信息
|
||||||
|
await initializeDeviceAuth();
|
||||||
|
|
||||||
|
// 执行设备验证
|
||||||
|
await verifyDevice();
|
||||||
|
|
||||||
|
// 执行其他定时任务
|
||||||
await refreshConfig();
|
await refreshConfig();
|
||||||
await refreshRecordAndFavorites();
|
await refreshRecordAndFavorites();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,13 @@ export interface ServerInfo {
|
|||||||
const API_SECRET = 'moontv-is-the-best';
|
const API_SECRET = 'moontv-is-the-best';
|
||||||
|
|
||||||
// 验证服务器地址
|
// 验证服务器地址
|
||||||
const AUTH_SERVER = process.env.AUTH_SERVER || 'https://moontv-auth.ihtw.moe';
|
const AUTH_SERVER = 'https://moontv-auth.ihtw.moe';
|
||||||
|
|
||||||
// 全局变量存储公钥和指纹
|
// 全局变量存储公钥和指纹
|
||||||
let serverPublicKey: crypto.KeyObject | null = null;
|
let serverPublicKey: crypto.KeyObject | null = null;
|
||||||
let expectedFingerprint = '';
|
let expectedFingerprint = '';
|
||||||
|
|
||||||
// 验证相关的全局变量
|
// 验证相关的全局变量
|
||||||
let verificationTimer: NodeJS.Timeout | null = null;
|
|
||||||
let networkFailureCount = 0;
|
|
||||||
const MAX_NETWORK_FAILURES = 3;
|
|
||||||
let currentMachineCode = '';
|
let currentMachineCode = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,9 +229,6 @@ function serializeAsGoJsonMarshal(obj: any): string {
|
|||||||
*/
|
*/
|
||||||
async function registerDevice(authCode: string, deviceCode: string) {
|
async function registerDevice(authCode: string, deviceCode: string) {
|
||||||
try {
|
try {
|
||||||
// 用户数量设置为0
|
|
||||||
const userCount = 0;
|
|
||||||
|
|
||||||
// 生成请求时间戳
|
// 生成请求时间戳
|
||||||
const requestTimestamp = Date.now().toString();
|
const requestTimestamp = Date.now().toString();
|
||||||
|
|
||||||
@@ -251,7 +245,6 @@ async function registerDevice(authCode: string, deviceCode: string) {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
auth_code: authCode,
|
auth_code: authCode,
|
||||||
device_code: deviceCode,
|
device_code: deviceCode,
|
||||||
user_count: userCount,
|
|
||||||
timestamp: requestTimestamp
|
timestamp: requestTimestamp
|
||||||
}),
|
}),
|
||||||
signal: controller.signal
|
signal: controller.signal
|
||||||
@@ -279,125 +272,9 @@ async function registerDevice(authCode: string, deviceCode: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证设备状态
|
|
||||||
*/
|
|
||||||
async function verifyDevice(): Promise<void> {
|
|
||||||
try {
|
|
||||||
console.log('🔄 开始设备验证...');
|
|
||||||
|
|
||||||
// 用户数量设置为0
|
|
||||||
const userCount = 0;
|
|
||||||
|
|
||||||
// 生成请求时间戳
|
|
||||||
const requestTimestamp = Date.now().toString();
|
|
||||||
|
|
||||||
// 设置10秒超时
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
||||||
|
|
||||||
const response = await fetch(`${AUTH_SERVER}/api/verify_device`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': 'MoonTV/1.0.0'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
device_code: currentMachineCode,
|
|
||||||
auth_code: process.env.AUTH_TOKEN || '',
|
|
||||||
user_count: userCount,
|
|
||||||
timestamp: requestTimestamp
|
|
||||||
}),
|
|
||||||
signal: controller.signal
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseBody = await response.text();
|
|
||||||
const apiResp: APIResponse = JSON.parse(responseBody);
|
|
||||||
|
|
||||||
// 验证响应签名(使用我们发送的时间戳)
|
|
||||||
await verifyResponse(apiResp, requestTimestamp);
|
|
||||||
|
|
||||||
if (!apiResp.success) {
|
|
||||||
console.error('❌ 设备验证失败,服务器即将退出');
|
|
||||||
console.error(`验证失败原因: ${apiResp.message}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置网络失败计数
|
|
||||||
networkFailureCount = 0;
|
|
||||||
console.log(`✅ 设备验证通过,用户数量: ${userCount}`);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : '未知错误';
|
|
||||||
|
|
||||||
// 判断是否为网络问题
|
|
||||||
const isNetworkError = errorMessage.includes('fetch') ||
|
|
||||||
errorMessage.includes('timeout') ||
|
|
||||||
errorMessage.includes('ECONNREFUSED') ||
|
|
||||||
errorMessage.includes('ETIMEDOUT') ||
|
|
||||||
errorMessage.includes('aborted');
|
|
||||||
|
|
||||||
if (isNetworkError) {
|
|
||||||
networkFailureCount++;
|
|
||||||
console.warn(`⚠️ 网络验证失败 (${networkFailureCount}/${MAX_NETWORK_FAILURES}): ${errorMessage}`);
|
|
||||||
|
|
||||||
if (networkFailureCount >= MAX_NETWORK_FAILURES) {
|
|
||||||
console.error('❌ 网络验证失败次数超过限制,服务器即将退出');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5分钟后重试
|
|
||||||
console.log('🔄 将在 5 分钟后重试验证...');
|
|
||||||
setTimeout(() => {
|
|
||||||
verifyDevice().catch(err => {
|
|
||||||
console.error('验证重试失败:', err);
|
|
||||||
});
|
|
||||||
}, 5 * 60 * 1000); // 5分钟
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// 非网络错误,直接退出
|
|
||||||
console.error('❌ 设备验证失败,服务器即将退出');
|
|
||||||
console.error(`验证失败原因: ${errorMessage}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动定时验证
|
|
||||||
*/
|
|
||||||
function startPeriodicVerification(): void {
|
|
||||||
console.log('⏰ 启动定时设备验证 (每小时一次)');
|
|
||||||
|
|
||||||
// 清除现有的定时器(如果有)
|
|
||||||
if (verificationTimer) {
|
|
||||||
clearInterval(verificationTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置每小时验证一次
|
|
||||||
verificationTimer = setInterval(() => {
|
|
||||||
verifyDevice().catch(err => {
|
|
||||||
console.error('定时验证失败:', err);
|
|
||||||
});
|
|
||||||
}, 60 * 60 * 1000); // 1小时
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 停止定时验证
|
|
||||||
*/
|
|
||||||
function stopPeriodicVerification(): void {
|
|
||||||
if (verificationTimer) {
|
|
||||||
clearInterval(verificationTimer);
|
|
||||||
verificationTimer = null;
|
|
||||||
console.log('⏹️ 定时验证已停止');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 环境变量检查
|
* 环境变量检查
|
||||||
@@ -539,9 +416,6 @@ async function checkAuthentication(): Promise<void> {
|
|||||||
await registerDevice(authToken, deviceCode);
|
await registerDevice(authToken, deviceCode);
|
||||||
|
|
||||||
console.log('🎉 设备认证流程完成');
|
console.log('🎉 设备认证流程完成');
|
||||||
|
|
||||||
// 启动定时验证
|
|
||||||
startPeriodicVerification();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 认证流程失败:', error instanceof Error ? error.message : '未知错误');
|
console.error('❌ 认证流程失败:', error instanceof Error ? error.message : '未知错误');
|
||||||
console.error('🚨 认证检查失败,服务器即将退出');
|
console.error('🚨 认证检查失败,服务器即将退出');
|
||||||
@@ -653,13 +527,11 @@ export async function register() {
|
|||||||
// 注册进程退出事件处理
|
// 注册进程退出事件处理
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
console.log('\n🛑 收到 SIGINT 信号,正在优雅关闭...');
|
console.log('\n🛑 收到 SIGINT 信号,正在优雅关闭...');
|
||||||
stopPeriodicVerification();
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', () => {
|
process.on('SIGTERM', () => {
|
||||||
console.log('\n🛑 收到 SIGTERM 信号,正在优雅关闭...');
|
console.log('\n🛑 收到 SIGTERM 信号,正在优雅关闭...');
|
||||||
stopPeriodicVerification();
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -668,7 +540,6 @@ export async function register() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('💥 启动检查过程中发生未预期错误:', error);
|
console.error('💥 启动检查过程中发生未预期错误:', error);
|
||||||
console.error('🚨 服务器即将退出');
|
console.error('🚨 服务器即将退出');
|
||||||
stopPeriodicVerification();
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -681,8 +552,6 @@ export {
|
|||||||
checkEnvironment,
|
checkEnvironment,
|
||||||
decryptWithAES,
|
decryptWithAES,
|
||||||
fetchServerPublicKey,
|
fetchServerPublicKey,
|
||||||
startPeriodicVerification,
|
|
||||||
stopPeriodicVerification,
|
|
||||||
verifyResponse,
|
verifyResponse,
|
||||||
verifyTimestampSignature,
|
verifyTimestampSignature,
|
||||||
serializeAsGoJsonMarshal
|
serializeAsGoJsonMarshal
|
||||||
|
|||||||
16
start.js
16
start.js
@@ -27,9 +27,8 @@ generateManifest();
|
|||||||
require('./server.js');
|
require('./server.js');
|
||||||
|
|
||||||
// 每 1 秒轮询一次,直到请求成功
|
// 每 1 秒轮询一次,直到请求成功
|
||||||
const TARGET_URL = `http://${process.env.HOSTNAME || 'localhost'}:${
|
const TARGET_URL = `http://${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000
|
||||||
process.env.PORT || 3000
|
}/login`;
|
||||||
}/login`;
|
|
||||||
|
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
console.log(`Fetching ${TARGET_URL} ...`);
|
console.log(`Fetching ${TARGET_URL} ...`);
|
||||||
@@ -40,8 +39,10 @@ const intervalId = setInterval(() => {
|
|||||||
console.log('Server is up, stop polling.');
|
console.log('Server is up, stop polling.');
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
|
|
||||||
// 服务器启动后,立即执行一次 cron 任务
|
setTimeout(() => {
|
||||||
executeCronJob();
|
// 服务器启动后,立即执行一次 cron 任务
|
||||||
|
executeCronJob();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
// 然后设置每小时执行一次 cron 任务
|
// 然后设置每小时执行一次 cron 任务
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@@ -57,9 +58,8 @@ const intervalId = setInterval(() => {
|
|||||||
|
|
||||||
// 执行 cron 任务的函数
|
// 执行 cron 任务的函数
|
||||||
function executeCronJob() {
|
function executeCronJob() {
|
||||||
const cronUrl = `http://${process.env.HOSTNAME || 'localhost'}:${
|
const cronUrl = `http://${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000
|
||||||
process.env.PORT || 3000
|
}/api/cron`;
|
||||||
}/api/cron`;
|
|
||||||
|
|
||||||
console.log(`Executing cron job: ${cronUrl}`);
|
console.log(`Executing cron job: ${cronUrl}`);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user