-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathgithub.ts
More file actions
115 lines (98 loc) · 3.53 KB
/
github.ts
File metadata and controls
115 lines (98 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { cache } from "react";
// GitHub 相关工具方法:集中维护仓库常量与文档路径生成逻辑
const GITHUB_OWNER = "InvolutionHell";
const GITHUB_REPO = "involutionhell.github.io";
const DEFAULT_BRANCH = "main";
const DOCS_BASE = "app/docs";
const REPO_BASE_URL = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}`;
// 拼接路径并清理多余斜杠,避免出现 // 或首尾斜杠
function joinPath(...segments: (string | undefined)[]) {
return segments
.filter((segment) => (segment ?? "").trim().length > 0)
.join("/")
.replace(/\/+/g, "/")
.replace(/^\/+/, "")
.replace(/\/+$/, "");
}
// 将路径逐段 URL 编码,处理中文等特殊字符
function encodeRepoPath(...segments: (string | undefined)[]) {
const joined = joinPath(...segments);
if (!joined) return "";
return joined
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/");
}
// 构建文档的 GitHub 编辑链接
export function buildDocsEditUrl(relativeDocPath: string) {
const encoded = encodeRepoPath(DOCS_BASE, relativeDocPath);
return `${REPO_BASE_URL}/edit/${DEFAULT_BRANCH}/${encoded}`;
}
// 构建在 GitHub 新建文档的链接,附带 frontmatter 参数
export function buildDocsNewUrl(relativeDir: string, params: URLSearchParams) {
const encodedDir = encodeRepoPath(DOCS_BASE, relativeDir);
const query = params.toString();
const suffix = query ? `?${query}` : "";
return `${REPO_BASE_URL}/new/${DEFAULT_BRANCH}/${encodedDir}${suffix}`;
}
// 帮助预览完整 docs 路径(未编码)
export function normalizeDocsPath(relative: string) {
return joinPath(DOCS_BASE, relative);
}
// 暴露常量给其他场景复用
export const githubConstants = {
owner: GITHUB_OWNER,
repo: GITHUB_REPO,
defaultBranch: DEFAULT_BRANCH,
docsBase: DOCS_BASE,
repoBaseUrl: REPO_BASE_URL,
};
// Define contributor data structure
interface Contributor {
login: string;
avatar_url: string;
html_url: string;
}
// Use React's cache function for request caching and deduplication
export const getContributors = cache(
async (filePath: string): Promise<Contributor[]> => {
try {
// Use GITHUB_TOKEN environment variable for authorization to increase API rate limit
const headers: HeadersInit = {};
if (process.env.GITHUB_TOKEN) {
headers.Authorization = `token ${process.env.GITHUB_TOKEN}`;
}
const response = await fetch(
`https://api.github.com/repos/InvolutionHell/involutionhell.github.io/commits?path=${filePath}`,
{
headers,
// Use next.revalidate to configure cache strategy (e.g., revalidate every hour)
next: { revalidate: 3600 },
},
);
if (!response.ok) {
// If request fails, return empty array
console.error(
`Failed to fetch contributors for ${filePath}: ${response.statusText}`,
);
return [];
}
const commits = await response.json();
// Use Map to deduplicate contributors
const uniqueContributors = new Map<string, Contributor>();
commits.forEach((commit: { author?: Contributor }) => {
if (commit.author) {
uniqueContributors.set(commit.author.login, {
login: commit.author.login,
avatar_url: commit.author.avatar_url,
html_url: commit.author.html_url,
});
}
});
return Array.from(uniqueContributors.values());
} catch (error) {
console.error(`Error fetching contributors for ${filePath}:`, error);
return [];
}
},
);