newechoes/src/pages/api/git-projects.ts
lsy f110beb5dc 1.全部改成参数式部件
2.可以用脚本创建文章
2025-03-10 17:22:18 +08:00

353 lines
9.6 KiB
TypeScript

import type { APIContext } from 'astro';
import { Octokit } from 'octokit';
import fetch from 'node-fetch';
import { GitPlatform } from '@/components/GitProjectCollection';
interface GitProject {
name: string;
description: string;
url: string;
stars: number;
forks: number;
language: string;
updatedAt: string;
owner: string;
avatarUrl: string;
platform: GitPlatform;
}
interface Pagination {
current: number;
total: number;
hasNext: boolean;
hasPrev: boolean;
}
export const prerender = false;
export async function GET({ request }: APIContext) {
try {
const url = new URL(request.url);
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Content-Type': 'application/json'
};
const platformParam = url.searchParams.get('platform');
const page = parseInt(url.searchParams.get('page') || '1');
const username = url.searchParams.get('username') || '';
const organization = url.searchParams.get('organization') || '';
const configStr = url.searchParams.get('config');
if (!platformParam) {
return new Response(JSON.stringify({
error: '无效的平台参数',
receivedPlatform: platformParam,
}), { status: 400, headers });
}
if (!configStr) {
return new Response(JSON.stringify({
error: '缺少配置参数'
}), { status: 400, headers });
}
const config = JSON.parse(configStr);
if (!Object.values(GitPlatform).includes(platformParam as GitPlatform)) {
return new Response(JSON.stringify({
error: '无效的平台参数',
receivedPlatform: platformParam,
}), { status: 400, headers });
}
const platform = platformParam as GitPlatform;
let projects: GitProject[] = [];
let pagination: Pagination = { current: page, total: 1, hasNext: false, hasPrev: page > 1 };
if (platform === GitPlatform.GITHUB) {
const result = await fetchGithubProjects(username, organization, page, config);
projects = result.projects;
pagination = result.pagination;
} else if (platform === GitPlatform.GITEA) {
const result = await fetchGiteaProjects(username, organization, page, config);
projects = result.projects;
pagination = result.pagination;
} else if (platform === GitPlatform.GITEE) {
const result = await fetchGiteeProjects(username, organization, page, config);
projects = result.projects;
pagination = result.pagination;
}
return new Response(JSON.stringify({ projects, pagination }), {
status: 200,
headers
});
} catch (error) {
return new Response(JSON.stringify({
error: '处理请求错误',
message: error instanceof Error ? error.message : '未知错误'
}), {
status: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
});
}
}
export function OPTIONS() {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
}
async function fetchGithubProjects(username: string, organization: string, page: number, config: any) {
const maxRetries = 3;
let retryCount = 0;
while (retryCount < maxRetries) {
try {
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
request: {
timeout: 10000
}
});
const perPage = config.perPage || 10;
let repos;
if (organization) {
const { data } = await octokit.request('GET /orgs/{org}/repos', {
org: organization,
per_page: perPage,
page: page,
sort: 'updated',
direction: 'desc'
});
repos = data;
} else if (username) {
const { data } = await octokit.request('GET /users/{username}/repos', {
username: username,
per_page: perPage,
page: page,
sort: 'updated',
direction: 'desc'
});
repos = data;
} else {
const { data } = await octokit.request('GET /users/{username}/repos', {
username: config.username,
per_page: perPage,
page: page,
sort: 'updated',
direction: 'desc'
});
repos = data;
}
let hasNext = false;
let hasPrev = page > 1;
let totalPages = 1;
if (repos.length === perPage) {
hasNext = true;
totalPages = page + 1;
}
if (repos.length > 0 && repos[0].owner) {
hasNext = repos.length === perPage;
totalPages = hasNext ? page + 1 : page;
}
const projects = repos.map((repo: any) => ({
name: repo.name,
description: repo.description,
url: repo.html_url,
stars: repo.stargazers_count,
forks: repo.forks_count,
language: repo.language,
updatedAt: repo.updated_at,
owner: repo.owner.login,
avatarUrl: repo.owner.avatar_url,
platform: GitPlatform.GITHUB
}));
return {
projects,
pagination: {
current: page,
total: totalPages,
hasNext,
hasPrev
}
};
} catch (error) {
retryCount++;
if (retryCount >= maxRetries) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 2000 * retryCount));
}
}
return {
projects: [],
pagination: {
current: page,
total: 1,
hasNext: false,
hasPrev: page > 1
}
};
}
async function fetchGiteaProjects(username: string, organization: string, page: number, config: any) {
try {
const perPage = config.perPage || 10;
const giteaUrl = config.url;
if (!giteaUrl) {
throw new Error('Gitea URL 不存在');
}
let apiUrl;
if (organization) {
apiUrl = `${giteaUrl}/api/v1/orgs/${organization}/repos?page=${page}&per_page=${perPage}`;
} else if (username) {
apiUrl = `${giteaUrl}/api/v1/users/${username}/repos?page=${page}&per_page=${perPage}`;
} else {
apiUrl = `${giteaUrl}/api/v1/users/${config.username}/repos?page=${page}&per_page=${perPage}`;
}
const response = await fetch(apiUrl, {
headers: {
'Accept': 'application/json',
...(config.token ? { 'Authorization': `token ${config.token}` } : {})
}
});
if (!response.ok) {
throw new Error(`Gitea API 请求失败: ${response.statusText}`);
}
const data = await response.json() as any;
const repos = Array.isArray(data) ? data : [];
const totalCount = parseInt(response.headers.get('X-Total-Count') || '0');
const totalPages = Math.ceil(totalCount / perPage) || 1;
const projects = repos.map((repo: any) => ({
name: repo.name,
description: repo.description || '',
url: repo.html_url || `${giteaUrl}/${repo.full_name || `${repo.owner.username || repo.owner.login}/${repo.name}`}`,
stars: repo.stars_count || repo.stargazers_count || 0,
forks: repo.forks_count || 0,
language: repo.language || '',
updatedAt: repo.updated_at,
owner: repo.owner.username || repo.owner.login,
avatarUrl: repo.owner.avatar_url,
platform: GitPlatform.GITEA
}));
return {
projects,
pagination: {
current: page,
total: totalPages,
hasNext: page < totalPages,
hasPrev: page > 1
}
};
} catch (error) {
return {
projects: [],
pagination: {
current: page,
total: 1,
hasNext: false,
hasPrev: page > 1
}
};
}
}
async function fetchGiteeProjects(username: string, organization: string, page: number, config: any) {
try {
const perPage = config.perPage || 10;
const giteeUsername = username || config.username;
if (!giteeUsername) {
throw new Error('Gitee 用户名未配置');
}
let apiUrl;
if (organization) {
apiUrl = `https://gitee.com/api/v5/orgs/${organization}/repos?page=${page}&per_page=${perPage}&sort=updated&direction=desc`;
} else {
apiUrl = `https://gitee.com/api/v5/users/${giteeUsername}/repos?page=${page}&per_page=${perPage}&sort=updated&direction=desc`;
}
if (config.token) {
apiUrl += `&access_token=${config.token}`;
}
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`Gitee API 请求失败: ${response.statusText}`);
}
const data = await response.json() as any[];
const projects: GitProject[] = data.map(repo => ({
name: repo.name || '',
description: repo.description || '',
url: repo.html_url || '',
stars: repo.stargazers_count || 0,
forks: repo.forks_count || 0,
language: repo.language || '',
updatedAt: repo.updated_at || '',
owner: repo.owner?.login || '',
avatarUrl: repo.owner?.avatar_url || '',
platform: GitPlatform.GITEE
}));
const totalCount = parseInt(response.headers.get('total_count') || '0');
const totalPages = Math.ceil(totalCount / perPage) || 1;
return {
projects,
pagination: {
current: page,
total: totalPages,
hasNext: page < totalPages,
hasPrev: page > 1
}
};
} catch (error) {
return {
projects: [],
pagination: {
current: page,
total: 1,
hasNext: false,
hasPrev: page > 1
}
};
}
}