删除咸鱼旧链路

This commit is contained in:
yml
2026-05-25 21:46:39 +08:00
parent 9aafddd624
commit e7aa194dde
142 changed files with 539 additions and 17373 deletions
@@ -1,71 +0,0 @@
import { runDatabaseMigrations } from "../db/migrate.js";
import { ensureAdminUsersBootstrapped } from "../services/admin/admin-auth-service.js";
import { ensureFulfillmentCatalogBootstrapped } from "../services/bootstrap/fulfillment-bootstrap-service.js";
import { startScheduledJobs } from "../services/scheduler/scheduler-service.js";
import { logError, logInfo } from "../utils/logger.js";
import { formatStartupError, type StartupState } from "./state.js";
const CORE_BOOT_RETRY_DELAY_MS = 5_000;
export async function bootstrapKuaishouCoreServices(
startupState: StartupState,
isShutdownStarted: () => boolean
) {
if (startupState.core.running || isShutdownStarted()) {
return;
}
startupState.core.running = true;
startupState.browser.status = "skipped";
startupState.browser.message = "kuaishou lite mode";
startupState.ocr.status = "skipped";
startupState.ocr.message = "kuaishou lite mode";
while (!isShutdownStarted() && !startupState.core.ready) {
startupState.phase =
startupState.core.attemptCount === 0 ? "starting" : "retrying";
startupState.core.attemptCount += 1;
startupState.core.lastAttemptAt = new Date().toISOString();
try {
logInfo("[startup:kuaishou]", "开始执行快手轻量核心启动步骤", {
attempt: startupState.core.attemptCount,
});
await runDatabaseMigrations();
await ensureFulfillmentCatalogBootstrapped();
await ensureAdminUsersBootstrapped();
startupState.core.ready = true;
startupState.core.lastError = "";
startupState.phase = "ready";
startupState.core.readyAt = new Date().toISOString();
logInfo("[startup:kuaishou]", "快手轻量核心启动步骤完成,服务已就绪", {
attempt: startupState.core.attemptCount,
});
startScheduledJobs();
break;
} catch (error) {
const message = formatStartupError(error);
startupState.phase = "retrying";
startupState.core.lastError = message;
logError("[startup:kuaishou]", "快手轻量核心启动步骤失败,将自动重试", {
attempt: startupState.core.attemptCount,
retryDelayMs: CORE_BOOT_RETRY_DELAY_MS,
error,
});
await sleep(CORE_BOOT_RETRY_DELAY_MS);
}
}
startupState.core.running = false;
}
function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
+10 -93
View File
@@ -1,13 +1,8 @@
import process from "node:process";
import { runtimeConfig } from "../config/runtime.js";
import { runDatabaseMigrations } from "../db/migrate.js";
import { ensureAdminUsersBootstrapped } from "../services/admin/admin-auth-service.js";
import { ensureFulfillmentCatalogBootstrapped } from "../services/bootstrap/fulfillment-bootstrap-service.js";
import { startScheduledJobs } from "../services/scheduler/scheduler-service.js";
import { warmupOcrService } from "../services/session/ocr.js";
import { warmupTencentBrowser } from "../services/session/session.js";
import { logError, logInfo, logWarn } from "../utils/logger.js";
import { logError, logInfo } from "../utils/logger.js";
import { formatStartupError, type StartupState } from "./state.js";
const CORE_BOOT_RETRY_DELAY_MS = 5_000;
@@ -21,14 +16,19 @@ export async function bootstrapCoreServices(
}
startupState.core.running = true;
startupState.browser.status = "skipped";
startupState.browser.message = "kuaishou lite mode";
startupState.ocr.status = "skipped";
startupState.ocr.message = "kuaishou lite mode";
while (!isShutdownStarted() && !startupState.core.ready) {
startupState.phase = startupState.core.attemptCount === 0 ? "starting" : "retrying";
startupState.phase =
startupState.core.attemptCount === 0 ? "starting" : "retrying";
startupState.core.attemptCount += 1;
startupState.core.lastAttemptAt = new Date().toISOString();
try {
logInfo("[startup]", "开始执行核心启动步骤", {
logInfo("[startup]", "开始执行快手核心启动步骤", {
attempt: startupState.core.attemptCount,
});
@@ -41,19 +41,17 @@ export async function bootstrapCoreServices(
startupState.phase = "ready";
startupState.core.readyAt = new Date().toISOString();
logInfo("[startup]", "核心启动步骤完成,服务已就绪", {
logInfo("[startup]", "快手核心启动步骤完成,服务已就绪", {
attempt: startupState.core.attemptCount,
});
startScheduledJobs();
void bootstrapBrowser(startupState);
void bootstrapOcr(startupState);
break;
} catch (error) {
const message = formatStartupError(error);
startupState.phase = "retrying";
startupState.core.lastError = message;
logError("[startup]", "核心启动步骤失败,将自动重试", {
logError("[startup]", "快手核心启动步骤失败,将自动重试", {
attempt: startupState.core.attemptCount,
retryDelayMs: CORE_BOOT_RETRY_DELAY_MS,
error,
@@ -66,87 +64,6 @@ export async function bootstrapCoreServices(
startupState.core.running = false;
}
async function bootstrapBrowser(startupState: StartupState) {
startupState.browser.status = "starting";
startupState.browser.lastAttemptAt = new Date().toISOString();
try {
const result = await warmupTencentBrowser();
if (!result.warmed) {
startupState.browser.status = "skipped";
startupState.browser.message = "browser prewarm skipped";
logInfo("[startup]", "browser prewarm skipped");
return;
}
startupState.browser.status = "ready";
startupState.browser.message = "browser prewarm ready";
logInfo("[startup]", "browser prewarm ready");
} catch (error) {
if (isMissingPlaywrightBrowserError(error)) {
const installCommand = resolveBrowserInstallCommand();
startupState.browser.status = "degraded";
startupState.browser.message = formatErrorMessage(error);
logWarn("[startup]", `browser prewarm skipped: ${formatErrorMessage(error)}`);
logWarn("[startup]", `请先安装浏览器依赖: ${installCommand}`);
if (String(runtimeConfig.browser.chromePath || "").trim()) {
logWarn("[startup]", "当前设置了 CHROME_PATH,也请确认该路径指向的浏览器可执行文件真实存在");
}
return;
}
startupState.browser.status = "degraded";
startupState.browser.message = formatStartupError(error);
logError("[startup]", "browser prewarm failed", error);
}
}
async function bootstrapOcr(startupState: StartupState) {
startupState.ocr.status = "starting";
startupState.ocr.lastAttemptAt = new Date().toISOString();
try {
await warmupOcrService();
startupState.ocr.status = "ready";
startupState.ocr.message = "OCR service ready";
logInfo("[startup]", "OCR service ready");
} catch (error) {
startupState.ocr.status = "degraded";
startupState.ocr.message = formatStartupError(error);
logWarn("[startup]", `OCR service skipped: ${formatStartupError(error)}`);
}
}
function isMissingPlaywrightBrowserError(error: unknown) {
const message = formatErrorMessage(error).toLowerCase();
return (
message.includes("executable doesn't exist") ||
message.includes("please run the following command to download new browsers") ||
(message.includes("browserType.launch".toLowerCase()) &&
message.includes("please run"))
);
}
function formatErrorMessage(error: unknown) {
if (error instanceof Error && error.message) {
return error.message.split("\n")[0].trim();
}
return String(error || "未知错误").trim();
}
function resolveBrowserInstallCommand() {
if (process.platform === "linux") {
return "npm run browser:install:linux";
}
return "npm run browser:install";
}
function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
@@ -1,44 +0,0 @@
import type { Server } from "node:http";
import { stopScheduledJobs } from "../services/scheduler/scheduler-service.js";
import { logError, logInfo } from "../utils/logger.js";
import type { StartupState } from "./state.js";
export function createKuaishouShutdownController(
server: Server,
startupState: StartupState
) {
let shutdownStarted = false;
async function shutdown(signal: string) {
if (shutdownStarted) {
return;
}
shutdownStarted = true;
startupState.phase = "shutting_down";
logInfo("[shutdown:kuaishou]", `received ${signal}, closing HTTP server`);
stopScheduledJobs();
if (!server.listening) {
return;
}
await new Promise<void>((resolve) => {
server.close((error) => {
if (error) {
logError("[shutdown:kuaishou]", "failed to close HTTP server", error);
process.exitCode = 1;
}
resolve();
});
});
}
return {
isShutdownStarted: () => shutdownStarted,
shutdown,
};
}
+5 -16
View File
@@ -1,12 +1,13 @@
import type { Server } from "node:http";
import { stopScheduledJobs } from "../services/scheduler/scheduler-service.js";
import { closeOcrService } from "../services/session/ocr.js";
import { closeAllTencentBrowserSessions } from "../services/session/session.js";
import { logError, logInfo } from "../utils/logger.js";
import type { StartupState } from "./state.js";
export function createShutdownController(server: Server, startupState: StartupState) {
export function createShutdownController(
server: Server,
startupState: StartupState
) {
let shutdownStarted = false;
async function shutdown(signal: string) {
@@ -16,22 +17,10 @@ export function createShutdownController(server: Server, startupState: StartupSt
shutdownStarted = true;
startupState.phase = "shutting_down";
logInfo("[shutdown]", `received ${signal}, closing browser sessions and HTTP server`);
logInfo("[shutdown]", `received ${signal}, closing HTTP server`);
stopScheduledJobs();
try {
await closeAllTencentBrowserSessions({ markClosed: true });
} catch (error) {
logError("[shutdown]", "failed to close browser sessions", error);
}
try {
await closeOcrService();
} catch (error) {
logError("[shutdown]", "failed to close OCR service", error);
}
if (!server.listening) {
return;
}