| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- 'use strict';
- // 门店采集路由:店铺清单、抓取任务控制、任务/系统日志
- const fs = require('fs');
- const { sendJson, readBody } = require('../util.js');
- function handleShopsManual(ctx, body, res) {
- const lines = String((body && body.text) || '').split(/\r?\n/);
- const parsed = [];
- for (const line of lines) {
- const t = line.trim();
- if (!t) continue;
- const parts = t.split(/[\s,,]+/).filter(Boolean);
- const idPart = parts.find((p) => /^\d+$/.test(p));
- if (!idPart) continue;
- const shopId = Number(idPart);
- const shopName = parts.filter((p) => p !== idPart).join(' ') || String(shopId);
- parsed.push({ shopId, shopName, source: 'manual' });
- }
- const previousShops = ctx.shops.getList();
- const mergedShops = ctx.shops.mergeShops(parsed);
- try { ctx.shops.saveManualShops(mergedShops); ctx.shops.setList(mergedShops); }
- catch (e) { ctx.shops.setList(previousShops); throw e; }
- sendJson(res, 200, { shops: ctx.shops.getList(), addedCount: parsed.length });
- }
- function handleShopsList(ctx, root, res) {
- if (root && !ctx.workspace.isWorkspaceRoot(root)) return sendJson(res, 403, { error: '仅允许访问当前输出根目录' });
- const completed = root && fs.existsSync(root) ? ctx.crawl.readCompletionState(root) : {};
- sendJson(res, 200, {
- shops: ctx.shops.getList().map((shop) => ({ ...shop, completed: completed[String(shop.shopId)] || null })),
- });
- }
- function register(app, ctx) {
- app.get('/api/shops', async ({ res, query }) => {
- // 前端把 root 做 base64url 编码传入,避免中文路径在 URL 里出问题
- const encodedRoot = query.root;
- const root = encodedRoot ? Buffer.from(encodedRoot, 'base64url').toString('utf8') : '';
- return handleShopsList(ctx, root, res);
- });
- app.post('/api/shops/manual', async ({ req, res }) => {
- const body = await readBody(req);
- return handleShopsManual(ctx, body, res);
- });
- app.post('/api/crawl/start', async ({ req, res }) => {
- const body = await readBody(req);
- return ctx.crawl.startCrawl(body, res);
- });
- app.post('/api/crawl/stop', async ({ res }) => {
- return ctx.crawl.stopCrawl(res);
- });
- app.get('/api/crawl/status', async ({ res }) => {
- sendJson(res, 200, ctx.crawl.getState());
- });
- // 采集通道探测:客户端调试端口是否就绪 + Cookie 是否已配置(采集页提示用)
- app.get('/api/crawl/channel', async ({ res }) => {
- const kit = require('../../napos/kit.js');
- const p = await kit.cdp.probe().catch(() => ({ ok: false }));
- sendJson(res, 200, {
- channel: ctx.config.crawlChannel || 'auto',
- clientUsable: !!p.ok,
- clientBrowser: (p.ok && p.version && p.version.Browser) || null,
- cookieConfigured: !!ctx.session.cookie && !!ctx.session.getKsid(),
- });
- });
- app.get('/api/logs', async ({ res }) => {
- sendJson(res, 200, { logs: ctx.logs.mergeLogs(ctx.crawl.getLog()) });
- });
- app.post('/api/logs/clear', async ({ res }) => {
- ctx.logs.clearAll(ctx.crawl.getLog());
- ctx.logs.pushSystemLog('日志已清空', 'warn');
- return sendJson(res, 200, { ok: true });
- });
- }
- module.exports = { register };
|