89 lines
2.7 KiB
JavaScript
89 lines
2.7 KiB
JavaScript
/**
|
|
* GitHub API Service
|
|
* Fetches files from a public GitHub repository directly from the browser.
|
|
* No proxy or token required for public repos.
|
|
*/
|
|
|
|
const GITHUB_API = 'https://api.github.com';
|
|
const GITHUB_RAW = 'https://raw.githubusercontent.com';
|
|
|
|
/**
|
|
* List markdown/text files in a GitHub repository folder.
|
|
* @param {string} owner - e.g. "respellion"
|
|
* @param {string} repo - e.g. "employee-handbook"
|
|
* @param {string} folder - e.g. "docs" or "" for root
|
|
* @param {string} branch - e.g. "main"
|
|
* @returns {Promise<Array<{name, sha, path}>>}
|
|
*/
|
|
export async function getRepoFolder(owner, repo, folder = '', branch = 'main') {
|
|
// Use the GitHub Git Trees API to fetch all files recursively in one request
|
|
const url = `${GITHUB_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`;
|
|
|
|
const res = await fetch(url, {
|
|
headers: { Accept: 'application/vnd.github+json' },
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({}));
|
|
throw new Error(`GitHub API ${res.status}: ${err.message || 'Could not list folder'}`);
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
// If folder is 'docs', we only want paths starting with 'docs/'
|
|
const folderPrefix = folder ? folder.replace(/^\/|\/$/g, '') + '/' : '';
|
|
|
|
// Return only files (blobs) that match the folder prefix and file extensions
|
|
return data.tree
|
|
.filter(item =>
|
|
item.type === 'blob' &&
|
|
item.path.startsWith(folderPrefix) &&
|
|
(item.path.endsWith('.md') || item.path.endsWith('.txt'))
|
|
)
|
|
.map(item => ({
|
|
name: item.path.split('/').pop(),
|
|
path: item.path,
|
|
sha: item.sha
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Fetch the raw text content of a file.
|
|
* Uses raw.githubusercontent.com — no rate limits for public content.
|
|
* @param {string} owner
|
|
* @param {string} repo
|
|
* @param {string} path - e.g. "docs/ROLES.md"
|
|
* @param {string} branch
|
|
* @returns {Promise<string>}
|
|
*/
|
|
export async function getFileContent(owner, repo, path, branch = 'main') {
|
|
const url = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${path}`;
|
|
const res = await fetch(url);
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Could not fetch "${path}" from GitHub (${res.status})`);
|
|
}
|
|
|
|
return res.text();
|
|
}
|
|
|
|
/**
|
|
* Parse a GitHub tree URL into its parts.
|
|
* e.g. "https://github.com/respellion/employee-handbook/tree/main/docs"
|
|
* → { owner: 'respellion', repo: 'employee-handbook', branch: 'main', folder: 'docs' }
|
|
*/
|
|
export function parseGitHubUrl(url) {
|
|
try {
|
|
const match = url.match(/github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)(?:\/(.+))?)?/);
|
|
if (!match) return null;
|
|
return {
|
|
owner: match[1],
|
|
repo: match[2],
|
|
branch: match[3] || 'main',
|
|
folder: match[4] || '',
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|