import * as plugins from './plugins.js'; import * as paths from './paths.js'; // Function to generate nav links from a folder export async function generateNavLinks(projectName: string) { const folderPath = plugins.path.join(paths.docsDir, projectName); try { let files = await plugins.fs.readdir(folderPath); files = files.filter((file) => file.endsWith('.md')); // Only include Markdown files const navLinks: { text: string; link: string; }[] = []; for (const file of files) { const name = plugins.path.basename(file, '.md'); const fileContent = await plugins.fs.readFile(plugins.path.join(folderPath, file), 'utf8'); const smartfmInstance = new plugins.smartfm.Smartfm({ fmType: 'yaml' }); const frontmatter = smartfmInstance.parse(fileContent); let title = name; if (frontmatter.data.title) { title = frontmatter.data.title; } navLinks.push({ text: title, link: `/${projectName}/${name}`, }); } return navLinks; } catch (error) { console.error(`Error reading folder ${folderPath}:`, error); return []; } } /** * Downloads README files from all repositories within a specified Gitea organization. * * @param domain - The domain of the Gitea instance (e.g., "https://gitea.example.com"). * @param org - The name of the organization whose repositories will be processed. * @param outputDir - The directory where README files will be saved. Defaults to "./readmes". * @param authToken - (Optional) Personal access token for authenticated requests. */ export async function downloadReadmes( domain: string, org: string, outputDir: string = './readmes', authToken?: string ): Promise { // Ensure the output directory exists await plugins.fs.mkdir(outputDir, { recursive: true }); /** * Reads all existing .md files in the specified directory and deletes any * whose frontmatter has `source === "gitea"`. */ async function removeGiteaSourceFiles(dirPath: string): Promise { try { const existingFiles = await plugins.fs.readdir(dirPath); for (const file of existingFiles) { if (file.endsWith('.md')) { const filePath = plugins.path.join(dirPath, file); const fileContent = await plugins.fs.readFile(filePath, 'utf8'); // Parse frontmatter const smartfmInstance = new plugins.smartfm.Smartfm({ fmType: 'yaml' }); const frontmatter = smartfmInstance.parse(fileContent); // If `source` is "gitea", delete this file if (frontmatter.data && frontmatter.data.source === 'gitea') { await plugins.fs.rm(filePath); console.log(`Deleted file with frontmatter source === "gitea": ${filePath}`); } } } } catch (error) { console.error('Error removing files with gitea source frontmatter:', error); } } // Call the function to remove existing .md files with frontmatter source === "gitea" await removeGiteaSourceFiles(outputDir); // Helper function to construct headers const getHeaders = (additionalHeaders: Record = {}) => { const headers: Record = { 'Content-Type': 'application/json', ...additionalHeaders, }; if (authToken) { headers['Authorization'] = `token ${authToken}`; } return headers; }; try { let page = 1; const perPage = 50; // Adjust as needed let hasMore = true; const repos: any[] = []; console.log(`Fetching repositories for organization: ${org}`); // Fetch all repositories with pagination while (hasMore) { const reposUrl = new URL(`/api/v1/orgs/${encodeURIComponent(org)}/repos`, domain); reposUrl.searchParams.append('page', page.toString()); reposUrl.searchParams.append('limit', perPage.toString()); const reposResponse = await fetch(reposUrl.toString(), { method: 'GET', headers: getHeaders(), }); if (!reposResponse.ok) { throw new Error( `Failed to fetch repositories: ${reposResponse.status} ${reposResponse.statusText}` ); } const fetchedRepos = await reposResponse.json(); repos.push(...fetchedRepos); if (fetchedRepos.length < perPage) { hasMore = false; } else { page += 1; } } console.log(`Total repositories found: ${repos.length}`); for (const repo of repos) { const repoName: string = repo.name; if(repoName === 'smarthbs') { continue; } console.log(`Processing repository: ${repoName}`); try { const readmeUrl = new URL( `/api/v1/repos/${encodeURIComponent(org)}/${encodeURIComponent( repoName )}/contents/readme.md`, domain ); console.log(readmeUrl.toString()); const readmeResponse = await fetch(readmeUrl.toString(), { method: 'GET', headers: getHeaders({ Accept: 'application/vnd.gitea.raw', }), }); if (readmeResponse.status === 404) { console.warn(`README not found for repository: ${repoName}`); continue; } if (!readmeResponse.ok) { throw new Error( `Failed to fetch README: ${readmeResponse.status} ${readmeResponse.statusText}` ); } const readmeContentResponseObject: any = JSON.parse(await readmeResponse.text()); let readmeContent = atob(readmeContentResponseObject.content); readmeContent = `--- title: "@${org}/${repo.name}" source: "gitea" --- ${readmeContent}`; const sanitizedRepoName = repoName.replace(/[^a-z0-9_\-]/gi, '_'); // Sanitize filename const readmePath = plugins.path.join(outputDir, `${sanitizedRepoName}_readme.md`); await plugins.fs.writeFile(readmePath, readmeContent, 'utf8'); console.log(`Saved README for ${repoName} to ${readmePath}`); } catch (readmeError: any) { console.error(`Error fetching README for repository: ${repoName}`, readmeError.message); } } console.log('All READMEs have been processed.'); } catch (error: any) { console.error('An error occurred while fetching repositories:', error.message); } }