2026-05-10 13:12:44 +08:00

222 lines
6.0 KiB
JavaScript
Raw Permalink 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.

/**
* GitHub API 服务封装
*
* 通过 GitHub API 获取仓库状态、提交记录等信息。
* 使用 GITHUB_TOKEN 环境变量认证。
* 版权:国作登字-2026-A-00037559
*/
'use strict';
var https = require('https');
var GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';
var ORG = 'qinfendebingshuo';
// ====== 内存缓存5分钟有效======
var cache = new Map();
var CACHE_TTL = 5 * 60 * 1000;
function getCached(key) {
var item = cache.get(key);
if (item && Date.now() - item.time < CACHE_TTL) return item.data;
return null;
}
function setCache(key, data) {
cache.set(key, { data: data, time: Date.now() });
}
function githubRequest(apiPath) {
return new Promise(function(resolve, reject) {
var headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'guanghu-api-server/1.0'
};
if (GITHUB_TOKEN) {
headers['Authorization'] = 'Bearer ' + GITHUB_TOKEN;
}
var options = {
hostname: 'api.github.com',
port: 443,
path: apiPath,
method: 'GET',
headers: headers
};
var req = https.request(options, function(res) {
var data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {
try { resolve(JSON.parse(data)); }
catch (e) { resolve({ error: 'JSON parse failed' }); }
});
});
req.on('error', reject);
req.end();
});
}
// ====== 获取仓库信息 ======
async function getRepoStatus(repoName) {
repoName = repoName || 'guanghulab';
var cacheKey = 'repo:' + repoName;
var cached = getCached(cacheKey);
if (cached) return cached;
var repo = await githubRequest('/repos/' + ORG + '/' + repoName);
var result = {
name: repo.name,
full_name: repo.full_name,
description: repo.description,
default_branch: repo.default_branch,
open_issues_count: repo.open_issues_count,
stargazers_count: repo.stargazers_count,
updated_at: repo.updated_at,
pushed_at: repo.pushed_at
};
setCache(cacheKey, result);
return result;
}
// ====== 获取最近提交 ======
async function getRecentCommits(repoName, count) {
repoName = repoName || 'guanghulab';
count = count || 10;
var cacheKey = 'commits:' + repoName + ':' + count;
var cached = getCached(cacheKey);
if (cached) return cached;
var commits = await githubRequest('/repos/' + ORG + '/' + repoName + '/commits?per_page=' + count);
var result = Array.isArray(commits) ? commits.map(function(c) {
return {
sha: c.sha ? c.sha.substring(0, 7) : '',
message: c.commit ? c.commit.message.split('\n')[0] : '',
date: c.commit && c.commit.author ? c.commit.author.date : '',
author: c.commit && c.commit.author ? c.commit.author.name : ''
};
}) : [];
setCache(cacheKey, result);
return result;
}
// ====== 获取 Workflow 状态 ======
async function getWorkflowRuns(repoName, count) {
repoName = repoName || 'guanghulab';
count = count || 10;
var cacheKey = 'workflows:' + repoName + ':' + count;
var cached = getCached(cacheKey);
if (cached) return cached;
var runs = await githubRequest('/repos/' + ORG + '/' + repoName + '/actions/runs?per_page=' + count);
var result = (runs.workflow_runs || []).map(function(r) {
return {
id: r.id,
name: r.name,
status: r.status,
conclusion: r.conclusion,
created_at: r.created_at,
updated_at: r.updated_at,
html_url: r.html_url
};
});
setCache(cacheKey, result);
return result;
}
// ====== 触发 GitHub Actions Workflow ======
function triggerWorkflow(workflowFile, inputs) {
return new Promise(function(resolve, reject) {
var data = JSON.stringify({
ref: 'main',
inputs: inputs || {}
});
var headers = {
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json',
'User-Agent': 'guanghu-api-server/1.0',
'Content-Length': Buffer.byteLength(data)
};
if (GITHUB_TOKEN) {
headers['Authorization'] = 'Bearer ' + GITHUB_TOKEN;
}
var options = {
hostname: 'api.github.com',
port: 443,
path: '/repos/' + ORG + '/guanghulab/actions/workflows/' + workflowFile + '/dispatches',
method: 'POST',
headers: headers
};
var req = https.request(options, function(res) {
if (res.statusCode === 204) {
resolve({ success: true });
} else {
var body = '';
res.on('data', function(chunk) { body += chunk; });
res.on('end', function() {
reject(new Error('GitHub API ' + res.statusCode + ': ' + body));
});
}
});
req.on('error', reject);
req.write(data);
req.end();
});
}
// ====== 创建 GitHub 文件 ======
function createFile(filePath, content, message) {
return new Promise(function(resolve, reject) {
var data = JSON.stringify({
message: message,
content: Buffer.from(content).toString('base64'),
committer: {
name: '铸渊 (ZhùYuān)',
email: 'zhuyuan@guanghulab.com'
}
});
var headers = {
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json',
'User-Agent': 'guanghu-api-server/1.0',
'Content-Length': Buffer.byteLength(data)
};
if (GITHUB_TOKEN) {
headers['Authorization'] = 'Bearer ' + GITHUB_TOKEN;
}
var options = {
hostname: 'api.github.com',
port: 443,
path: '/repos/' + ORG + '/guanghulab/contents/' + filePath,
method: 'PUT',
headers: headers
};
var req = https.request(options, function(res) {
var body = '';
res.on('data', function(chunk) { body += chunk; });
res.on('end', function() {
try { resolve(JSON.parse(body)); }
catch (e) { resolve({ raw: body }); }
});
});
req.on('error', reject);
req.write(data);
req.end();
});
}
module.exports = {
getRepoStatus: getRepoStatus,
getRecentCommits: getRecentCommits,
getWorkflowRuns: getWorkflowRuns,
triggerWorkflow: triggerWorkflow,
createFile: createFile
};