guanghulab/scripts/server-diagnose-report.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

202 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// scripts/server-diagnose-report.js
// 铸渊 · PM2 服务诊断报告 → Notion 工单
//
// 将 PM2 诊断和系统健康检查结果写入 Notion 工单队列。
// 由 pm2-server-diagnose.yml 工作流调用。
//
// 环境变量:
// NOTION_TOKEN Notion API token
// NOTION_TICKET_DB_ID 工单队列数据库 ID
// PM2_REPORT PM2 诊断输出
// CLEANUP_REPORT 清理操作输出(可选)
// HEALTH_REPORT 健康检查输出
'use strict';
const https = require('https');
const NOTION_TOKEN = process.env.NOTION_TOKEN || '';
const NOTION_TICKET_DB_ID = process.env.NOTION_TICKET_DB_ID || '';
const PM2_REPORT = process.env.PM2_REPORT || '';
const CLEANUP_REPORT = process.env.CLEANUP_REPORT || '';
const HEALTH_REPORT = process.env.HEALTH_REPORT || '';
const NOTION_VERSION = '2022-06-28';
const NOTION_API_HOSTNAME = 'api.notion.com';
const NOTION_RICH_TEXT_MAX = 2000;
const MAX_PM2_REPORT_LENGTH = 3000;
const MAX_CLEANUP_REPORT_LENGTH = 2000;
const MAX_HEALTH_REPORT_LENGTH = 3000;
// ══════════════════════════════════════════════════════════
// Notion API 工具
// ══════════════════════════════════════════════════════════
function notionPost(endpoint, body) {
return new Promise(function (resolve, reject) {
var payload = JSON.stringify(body);
var opts = {
hostname: NOTION_API_HOSTNAME,
port: 443,
path: endpoint,
method: 'POST',
headers: {
'Authorization': 'Bearer ' + NOTION_TOKEN,
'Content-Type': 'application/json',
'Notion-Version': NOTION_VERSION,
'Content-Length': Buffer.byteLength(payload),
},
};
var req = https.request(opts, function (res) {
var data = '';
res.on('data', function (chunk) { data += chunk; });
res.on('end', function () {
try {
var parsed = JSON.parse(data);
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(parsed);
} else {
reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data)));
}
} catch (e) {
reject(new Error('Notion API parse error: ' + data));
}
});
});
req.on('error', reject);
req.write(payload);
req.end();
});
}
function richTextChunks(content) {
var str = String(content || '');
var chunks = [];
for (var i = 0; i < str.length; i += NOTION_RICH_TEXT_MAX) {
chunks.push({ type: 'text', text: { content: str.slice(i, i + NOTION_RICH_TEXT_MAX) } });
}
if (chunks.length === 0) {
chunks.push({ type: 'text', text: { content: '' } });
}
return chunks;
}
// ══════════════════════════════════════════════════════════
// 构建报告内容
// ══════════════════════════════════════════════════════════
function buildReportContent() {
var now = new Date().toISOString();
var dateStr = now.slice(0, 10);
var sections = [];
sections.push('## 📋 PM2 服务排查报告 · ' + dateStr);
sections.push('');
sections.push('| 字段 | 值 |');
sections.push('|------|-----|');
sections.push('| 执行者 | 铸渊Agent |');
sections.push('| 执行时间 | ' + now + ' |');
sections.push('| 来源 | GitHub Actions · pm2-server-diagnose |');
sections.push('');
if (PM2_REPORT) {
sections.push('## 阶段一 · PM2 诊断结果');
sections.push('');
sections.push(PM2_REPORT.slice(0, MAX_PM2_REPORT_LENGTH));
sections.push('');
}
if (CLEANUP_REPORT) {
sections.push('## 清理操作结果');
sections.push('');
sections.push(CLEANUP_REPORT.slice(0, MAX_CLEANUP_REPORT_LENGTH));
sections.push('');
}
if (HEALTH_REPORT) {
sections.push('## 阶段二 · 全系统健康检查');
sections.push('');
sections.push(HEALTH_REPORT.slice(0, MAX_HEALTH_REPORT_LENGTH));
sections.push('');
}
return sections.join('\n');
}
// ══════════════════════════════════════════════════════════
// 主函数 — 创建 Notion 诊断工单
// ══════════════════════════════════════════════════════════
async function createDiagnoseTicket() {
if (!NOTION_TOKEN || !NOTION_TICKET_DB_ID) {
console.log('⚠️ Notion credentials not configured, skipping ticket creation');
console.log(' NOTION_TOKEN: ' + (NOTION_TOKEN ? '✅' : '❌'));
console.log(' NOTION_TICKET_DB_ID: ' + (NOTION_TICKET_DB_ID ? '✅' : '❌'));
process.exit(0);
}
var now = new Date();
var dateStr = now.toISOString().slice(0, 10);
var title = 'PM2服务排查报告 · ' + dateStr;
var contentText = buildReportContent();
// 将内容拆分成多个段落块Notion 单个 rich_text 限制 2000 字符)
var contentBlocks = [];
var remaining = contentText;
while (remaining.length > 0) {
var chunk = remaining.slice(0, NOTION_RICH_TEXT_MAX);
remaining = remaining.slice(NOTION_RICH_TEXT_MAX);
contentBlocks.push({
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: [{ type: 'text', text: { content: chunk } }],
},
});
}
// 确保至少有一个内容块
if (contentBlocks.length === 0) {
contentBlocks.push({
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: [{ type: 'text', text: { content: '诊断报告(无详细输出)' } }],
},
});
}
var body = {
parent: { database_id: NOTION_TICKET_DB_ID },
properties: {
'标题': { title: [{ type: 'text', text: { content: title } }] },
'操作类型': { select: { name: '其他' } },
'提交者': { rich_text: [{ type: 'text', text: { content: '铸渊Agent' } }] },
'状态': { select: { name: '已完成' } },
'优先级': { select: { name: 'P1' } },
},
children: contentBlocks,
};
console.log('📋 创建 PM2 诊断工单...');
console.log(' 标题: ' + title);
console.log(' 数据库: ' + NOTION_TICKET_DB_ID);
console.log(' 内容长度: ' + contentText.length + ' 字符');
console.log(' 内容块数: ' + contentBlocks.length);
try {
var result = await notionPost('/v1/pages', body);
console.log('✅ Notion 诊断工单已创建: ' + result.id);
console.log(' URL: ' + (result.url || 'N/A'));
} catch (err) {
console.error('❌ 工单创建失败: ' + err.message);
// 不以失败退出,诊断本身已成功
console.log('⚠️ 诊断结果已输出到 Actions 日志Notion 写入失败不影响诊断');
process.exit(0);
}
}
createDiagnoseTicket();