const API = require("../../lib/API") const AccessControl = require("../../lib/AccessControl") const { BaseStdResponse } = require("../../BaseStdResponse") const db = require("../../plugin/DataBase/db") const simpleGit = require('simple-git') const crypto = require("crypto") class GitContributors extends API { constructor() { super() this.setMethod("GET") this.setPath("/Repos/Contributors") } getGravatarUrl(email) { if (!email) return null const hash = crypto.createHash("md5").update(email.trim().toLowerCase()).digest("hex") return `https://gravatar.loli.net/avatar/${hash}?s=512&r=pg` } async analyzeGitContributors(repoPath) { try { const git = simpleGit() await git.cwd(repoPath) // 获取提交日志,包括作者姓名和邮箱 const logs = await git.raw([ "log", "--format=%an <%ae>", "--shortstat" ]) const lines = logs.split("\n").map(l => l.trim()).filter(l => l) let contributors = {} let currentAuthor = null for (const line of lines) { if (line.includes("<") && line.includes(">")) { const match = line.match(/(.*?) <(.*?)>/) if (!match) continue const name = match[1].trim() const email = match[2].trim() currentAuthor = name if (!contributors[currentAuthor]) { contributors[currentAuthor] = { name, email, avatar: this.getGravatarUrl(email), commits: 0, linesChanged: 0 } } contributors[currentAuthor].commits += 1 } else if (line.includes("file changed")) { const match = line.match(/(\d+) insertions?\(\+\)|(\d+) deletions?\(-\)/g) if (match) { const changes = match.map(m => parseInt(m.match(/\d+/)[0], 10)).reduce((a, b) => a + b, 0) contributors[currentAuthor].linesChanged += changes } } } const sortedContributors = Object.values(contributors) .sort((a, b) => b.linesChanged - a.linesChanged || b.commits - a.commits) return sortedContributors } catch (error) { console.error("获取仓库开发者失败:", error) throw new Error("获取仓库开发者失败") } } async onRequest(req, res) { let { uuid, session, id } = req.query if ([uuid, session, id].some(value => value === '' || value === null || value === undefined)) return res.json({ ...BaseStdResponse.MISSING_PARAMETER }) // 检查 session if (!await AccessControl.checkSession(uuid, session)) return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED }) let sql = 'SELECT state, path FROM repos WHERE create_user = ? AND id = ?' let r = await db.query(sql, [uuid, id]) if (!r || r.length === 0) return res.json({ ...BaseStdResponse.ERR, msg: '未找到仓库' }) if (r[0].state !== 1 || !r[0].path) return res.json({ ...BaseStdResponse.ERR, msg: '仓库未成功克隆!' }) let contributors try { contributors = await this.analyzeGitContributors(r[0].path) } catch (error) { this.logger.error('获取仓库开发者失败!' + error.stack) return res.json({ ...BaseStdResponse.ERR, msg: '获取仓库开发者失败!' }) } res.json({ ...BaseStdResponse.OK, data: contributors }) } } module.exports.GitContributors = GitContributors