1
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import type { BackgroundCommand } from './types';
|
||||
import { getPlatformById } from '@/config/platforms';
|
||||
import type { CrawlProgressStep, CrawlTaskState } from '@/types';
|
||||
import type { BackgroundCommand, BackgroundResponse, CrawlStateResponse } from './types';
|
||||
|
||||
const CRAWL_TASK_STORAGE_KEY = 'crawlTaskState';
|
||||
|
||||
export async function handleInstalled(): Promise<void> {
|
||||
console.log('[background] installed');
|
||||
@@ -10,9 +14,148 @@ export async function handleStartup(): Promise<void> {
|
||||
|
||||
export async function handleWindowRemoved(windowId: number): Promise<void> {
|
||||
console.log('[background] window removed', windowId);
|
||||
|
||||
const state = await getCrawlTaskState();
|
||||
|
||||
if (state?.windowId === windowId && state.status === 'running') {
|
||||
await setCrawlTaskState({
|
||||
...state,
|
||||
status: 'canceled',
|
||||
steps: state.steps.map((step, index) =>
|
||||
index === state.currentStepIndex ? { ...step, status: 'failed', message: '爬取窗口已关闭' } : step,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleBackgroundCommand(message: BackgroundCommand): Promise<unknown> {
|
||||
console.log('[background] message', message);
|
||||
return { ok: true };
|
||||
export async function handleBackgroundCommand(
|
||||
message: BackgroundCommand,
|
||||
): Promise<BackgroundResponse | CrawlStateResponse> {
|
||||
switch (message.action) {
|
||||
case 'START_CRAWL':
|
||||
return startCrawl(message.payload.platformId);
|
||||
case 'GET_CRAWL_STATE':
|
||||
return { ok: true, data: await getCrawlTaskState() };
|
||||
case 'CANCEL_CRAWL':
|
||||
return cancelCrawl();
|
||||
default:
|
||||
return { ok: false, error: '未知的后台指令' };
|
||||
}
|
||||
}
|
||||
|
||||
async function startCrawl(platformId: string): Promise<CrawlStateResponse> {
|
||||
const platform = getPlatformById(platformId);
|
||||
|
||||
if (!platform) {
|
||||
return { ok: false, error: '平台配置不存在' };
|
||||
}
|
||||
|
||||
const startedAt = Date.now();
|
||||
const nextState: CrawlTaskState = {
|
||||
id: `${platform.id}-${startedAt}`,
|
||||
platformId: platform.id,
|
||||
platformName: platform.name,
|
||||
startedAt,
|
||||
status: 'running',
|
||||
currentStepIndex: 0,
|
||||
steps: platform.steps.map<CrawlProgressStep>((step, index) => ({
|
||||
name: step.name,
|
||||
uniqueKey: step.uniqueKey,
|
||||
status: index === 0 ? 'running' : 'pending',
|
||||
})),
|
||||
};
|
||||
|
||||
await setCrawlTaskState(nextState);
|
||||
|
||||
try {
|
||||
const windowInfo = await createCrawlWindow(platform.baseUrl);
|
||||
const stateWithWindow = { ...nextState, windowId: windowInfo.id };
|
||||
await setCrawlTaskState(stateWithWindow);
|
||||
return { ok: true, data: stateWithWindow };
|
||||
} catch (error: unknown) {
|
||||
const failedState: CrawlTaskState = {
|
||||
...nextState,
|
||||
status: 'failed',
|
||||
steps: nextState.steps.map((step, index) =>
|
||||
index === 0 ? { ...step, status: 'failed', message: '打开平台窗口失败' } : step,
|
||||
),
|
||||
};
|
||||
await setCrawlTaskState(failedState);
|
||||
return { ok: false, data: failedState, error: error instanceof Error ? error.message : '打开平台窗口失败' };
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelCrawl(): Promise<CrawlStateResponse> {
|
||||
const state = await getCrawlTaskState();
|
||||
|
||||
if (!state) {
|
||||
return { ok: true, data: null };
|
||||
}
|
||||
|
||||
const canceledState: CrawlTaskState = {
|
||||
...state,
|
||||
status: 'canceled',
|
||||
steps: state.steps.map((step, index) =>
|
||||
index === state.currentStepIndex ? { ...step, status: 'failed', message: '用户已取消' } : step,
|
||||
),
|
||||
};
|
||||
|
||||
await setCrawlTaskState(canceledState);
|
||||
|
||||
if (state.windowId) {
|
||||
await removeWindow(state.windowId);
|
||||
}
|
||||
|
||||
return { ok: true, data: canceledState };
|
||||
}
|
||||
|
||||
async function getCrawlTaskState(): Promise<CrawlTaskState | null> {
|
||||
const result = await chrome.storage.local.get(CRAWL_TASK_STORAGE_KEY);
|
||||
const state = result[CRAWL_TASK_STORAGE_KEY];
|
||||
return isCrawlTaskState(state) ? state : null;
|
||||
}
|
||||
|
||||
async function setCrawlTaskState(state: CrawlTaskState): Promise<void> {
|
||||
await chrome.storage.local.set({ [CRAWL_TASK_STORAGE_KEY]: state });
|
||||
}
|
||||
|
||||
function createCrawlWindow(url: string): Promise<chrome.windows.Window> {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.windows.create(
|
||||
{
|
||||
url,
|
||||
type: 'normal',
|
||||
focused: true,
|
||||
width: 1280,
|
||||
height: 900,
|
||||
},
|
||||
(windowInfo) => {
|
||||
const runtimeError = chrome.runtime.lastError;
|
||||
|
||||
if (runtimeError) {
|
||||
reject(new Error(runtimeError.message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!windowInfo?.id) {
|
||||
reject(new Error('窗口创建失败'));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(windowInfo);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function removeWindow(windowId: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.windows.remove(windowId, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isCrawlTaskState(value: unknown): value is CrawlTaskState {
|
||||
return typeof value === 'object' && value !== null && 'id' in value && 'steps' in value;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
export interface BackgroundCommand {
|
||||
action: string;
|
||||
payload?: unknown;
|
||||
import type { CrawlTaskState } from '@/types';
|
||||
|
||||
export interface StartCrawlCommand {
|
||||
action: 'START_CRAWL';
|
||||
payload: {
|
||||
platformId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetCrawlStateCommand {
|
||||
action: 'GET_CRAWL_STATE';
|
||||
}
|
||||
|
||||
export interface CancelCrawlCommand {
|
||||
action: 'CANCEL_CRAWL';
|
||||
}
|
||||
|
||||
export type BackgroundCommand = StartCrawlCommand | GetCrawlStateCommand | CancelCrawlCommand;
|
||||
|
||||
export interface BackgroundResponse<T = unknown> {
|
||||
ok: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export type CrawlStateResponse = BackgroundResponse<CrawlTaskState | null>;
|
||||
|
||||
Reference in New Issue
Block a user