WebPreview / app.js
hanxuan's picture
细节优化
08d597b verified
const express = require('express');
const puppeteer = require('puppeteer');
const app = express();
const port = 7860;
const colorCodes = {
reset: "\x1b[0m",
green: "\x1b[32m"
};
let requestCounter = 0;
let browserInstance;
app.use(express.json());
app.get('/api', async (req, res) => {
const { url, time } = req.query;
const timestamp = new Date().toLocaleString();
const start = Date.now();
const requestId = ++requestCounter;
let delayTime = parseInt(time) || 0;
let delayDuration = 0;
try {
if (!isNaN(delayTime) && delayTime >= 0 && delayTime <= 60) {
const formattedURL = formatURL(url);
if (!formattedURL) {
console.log(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] URL格式错误 ${colorizeURLAndMS(url)}`);
return res.status(400).send(`请求错误:URL格式错误`);
}
console.log(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] 请求截图 ${delayTime} ${colorizeURLAndMS(formattedURL)}`);
const browser = await getBrowserInstance();
const page = await browser.newPage();
await page.goto(formattedURL, { timeout: 60000 });
//await page.setViewport({ width: 1920, height: 1080 });
if (delayTime > 0) {
await new Promise(resolve => setTimeout(resolve, delayTime * 1000));
delayDuration = Date.now() - start - delayTime * 1000;
}
const screenshot = await page.screenshot({ fullPage: true });
res.set('Content-Type', 'image/png');
res.send(screenshot);
// 关闭页面
await page.close();
} else {
console.log(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] 延时错误 ${colorizeURLAndMS(url)}`);
res.status(400).send(`请求错误:延时时间不正确!仅支持0到60秒之间`);
}
const end = Date.now();
const totalTime = end - start - delayDuration;
console.log(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] 处理完成 ${colorCodes.green}${totalTime}ms${colorCodes.reset}`);
// 记录完整请求信息
console.log(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] ${JSON.stringify(req.query)}`);
} catch (error) {
console.error(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] 处理错误:${error}`);
res.status(500).send(`处理错误:${error}`);
// 记录完整请求信息
console.log(`[${colorizeTimestamp(timestamp)}] [${colorCodes.green}${requestId}${colorCodes.reset}] ${JSON.stringify(req.query)}`);
}
});
app.listen(port, () => {
console.log(`服务已运行 监听端口${port}`);
});
async function getBrowserInstance() {
if (!browserInstance) {
browserInstance = await puppeteer.launch({
headless: "new",
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-first-run',
'--no-sandbox',
'--no-zygote',
'--single-process',
'--lang=zh-CN,zh'
],
});
}
const browser = await browserInstance;
await browser.pages().then(async (pages) => {
if (pages.length > 0) {
await pages[0].evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "language", {
get: function() {
return "zh-CN";
}
});
Object.defineProperty(navigator, "languages", {
get: function() {
return ["zh-CN", "zh"];
}
});
});
}
});
return browserInstance;
}
function formatURL(url) {
const regex = /^(?:https?:\/\/)?([\w.-]+\.\w+(\.\w+)?)(.*)$/;
const match = url.match(regex);
if (match) {
const domain = match[1];
const path = match[3] || '/';
return `https://${domain}${path}`;
}
return null;
}
function colorizeTimestamp(text) {
const regex = /\[(.*?)\]/g;
return text.replace(regex, (match, p1) => {
return `[${colorCodes.green}${p1}${colorCodes.reset}]`;
});
}
function colorizeURLAndMS(text) {
const regex = /(?:https?:\/\/[^\s]+)/g;
const msRegex = /\b\d+ms\b/g;
return text
.replace(regex, (match) => {
return colorCodes.green + match + colorCodes.reset;
})
.replace(msRegex, (match) => {
return colorCodes.green + match + colorCodes.reset;
});
}