222 lines
6.0 KiB
JavaScript
222 lines
6.0 KiB
JavaScript
/**
|
||
* 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
|
||
};
|