171 lines
5.9 KiB
JavaScript
171 lines
5.9 KiB
JavaScript
|
|
// scripts/bridge/check-queue.js
|
|||
|
|
// 🌉 桥接·调度队列检查
|
|||
|
|
//
|
|||
|
|
// 查询 Notion 调度队列数据库中指定类型+状态的任务
|
|||
|
|
// 用法: node scripts/bridge/check-queue.js [BROADCAST_READY|SYSLOG_RECEIVED|HEARTBEAT]
|
|||
|
|
//
|
|||
|
|
// 环境变量:
|
|||
|
|
// NOTION_TOKEN Notion API token
|
|||
|
|
// BRIDGE_QUEUE_DB_ID 桥接调度队列数据库 ID
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const https = require('https');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const NOTION_VERSION = '2022-06-28';
|
|||
|
|
const NOTION_API_HOSTNAME = 'api.notion.com';
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// Notion API 基础调用
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function notionPost(endpoint, body, token) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const payload = JSON.stringify(body);
|
|||
|
|
const opts = {
|
|||
|
|
hostname: NOTION_API_HOSTNAME,
|
|||
|
|
port: 443,
|
|||
|
|
path: endpoint,
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': 'Bearer ' + token,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Notion-Version': NOTION_VERSION,
|
|||
|
|
'Content-Length': Buffer.byteLength(payload),
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const req = https.request(opts, res => {
|
|||
|
|
let data = '';
|
|||
|
|
res.on('data', chunk => data += chunk);
|
|||
|
|
res.on('end', () => {
|
|||
|
|
try {
|
|||
|
|
const 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();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// 主逻辑
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
async function checkQueue(taskType) {
|
|||
|
|
const token = process.env.NOTION_TOKEN;
|
|||
|
|
const dbId = process.env.BRIDGE_QUEUE_DB_ID;
|
|||
|
|
|
|||
|
|
if (!token) {
|
|||
|
|
console.log('⚠️ 缺少 NOTION_TOKEN,跳过调度队列检查');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
if (!dbId) {
|
|||
|
|
console.log('⚠️ 缺少 BRIDGE_QUEUE_DB_ID,跳过调度队列检查');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`🌉 检查调度队列 · 类型=${taskType} · 状态=待处理`);
|
|||
|
|
|
|||
|
|
const filter = {
|
|||
|
|
and: [
|
|||
|
|
{ property: '类型', select: { equals: taskType } },
|
|||
|
|
{ property: '处理状态', status: { equals: '待处理' } },
|
|||
|
|
],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const body = {
|
|||
|
|
filter,
|
|||
|
|
sorts: [{ property: '创建时间', direction: 'ascending' }],
|
|||
|
|
page_size: 50,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await notionPost(`/v1/databases/${dbId}/query`, body, token);
|
|||
|
|
const tasks = result.results || [];
|
|||
|
|
|
|||
|
|
console.log(`📋 发现 ${tasks.length} 条待处理任务`);
|
|||
|
|
|
|||
|
|
// 将任务列表写入临时文件供后续步骤使用
|
|||
|
|
const outputDir = path.join('data', 'bridge-logs');
|
|||
|
|
if (!fs.existsSync(outputDir)) {
|
|||
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const outputFile = path.join(outputDir, `queue-${taskType.toLowerCase()}-${Date.now()}.json`);
|
|||
|
|
const taskSummaries = tasks.map(t => ({
|
|||
|
|
id: t.id,
|
|||
|
|
task_name: extractTitle(t),
|
|||
|
|
task_type: taskType,
|
|||
|
|
dev_id: extractSelect(t, 'DEV编号'),
|
|||
|
|
broadcast_id: extractRichText(t, '广播编号'),
|
|||
|
|
source: extractRichText(t, '来源渠道'),
|
|||
|
|
payload: extractRichText(t, 'Payload'),
|
|||
|
|
created_time: t.created_time,
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
fs.writeFileSync(outputFile, JSON.stringify(taskSummaries, null, 2));
|
|||
|
|
console.log(`📁 任务列表已写入: ${outputFile}`);
|
|||
|
|
|
|||
|
|
// 输出到 GITHUB_OUTPUT(供 workflow 使用)
|
|||
|
|
if (process.env.GITHUB_OUTPUT) {
|
|||
|
|
fs.appendFileSync(process.env.GITHUB_OUTPUT,
|
|||
|
|
`task_count=${tasks.length}\n` +
|
|||
|
|
`queue_file=${outputFile}\n`
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return taskSummaries;
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error(`❌ 查询调度队列失败: ${e.message}`);
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// Notion 属性提取辅助
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function extractTitle(page) {
|
|||
|
|
const prop = page.properties && page.properties['任务名称'];
|
|||
|
|
if (prop && prop.title && prop.title.length > 0) {
|
|||
|
|
return prop.title.map(t => t.plain_text).join('');
|
|||
|
|
}
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function extractSelect(page, name) {
|
|||
|
|
const prop = page.properties && page.properties[name];
|
|||
|
|
if (prop && prop.select) {
|
|||
|
|
return prop.select.name || '';
|
|||
|
|
}
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function extractRichText(page, name) {
|
|||
|
|
const prop = page.properties && page.properties[name];
|
|||
|
|
if (prop && prop.rich_text && prop.rich_text.length > 0) {
|
|||
|
|
return prop.rich_text.map(t => t.plain_text).join('');
|
|||
|
|
}
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// 入口
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
const taskType = process.argv[2] || 'BROADCAST_READY';
|
|||
|
|
checkQueue(taskType).catch(e => { console.error(e); process.exit(1); });
|