blob: 40af035c3242b688e831aef0d48ada38acea9c13 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const fs = require("fs");
const https = require("https");
const teamSrc = "src/pages/team/data/team.json";
const avatarFile = "src/pages/team/data/github-avatar.json";
const avatarSize = 100;
const authorsFile = "blog/authors.json";
/**
* Generates a random delay between min and max milliseconds
* @param {number} min - Minimum delay in milliseconds
* @param {number} max - Maximum delay in milliseconds
* @returns {number} Random delay in milliseconds
*/
function getRandomDelay(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Fetches avatar image from GitHub and converts it to base64
* @param {string} githubId - GitHub ID
* @returns {Promise<string>} Base64 encoded avatar image
*/
function fetchAvatarAsBase64(githubId) {
return new Promise((resolve, reject) => {
const avatarUrl = `https://avatars.githubusercontent.com/u/${githubId}?v=4&s=${avatarSize}`;
https
.get(avatarUrl, (response) => {
// Check if request was successful
if (response.statusCode !== 200) {
reject(new Error(`Failed to fetch avatar from ${avatarUrl}: ${response.statusCode}`));
return;
}
const chunks = [];
// Collect data chunks
response.on("data", (chunk) => {
chunks.push(chunk);
});
// Convert to base64 when complete
response.on("end", () => {
const buffer = Buffer.concat(chunks);
const base64 = buffer.toString("base64");
resolve(base64);
});
})
.setTimeout(10000, () => {
reject(new Error(`Failed to fetch avatar ${avatarUrl}: timed out`));
})
.on("error", (error) => {
reject(error);
});
});
}
/**
* Get GitHub name from URL
* @param {string} url - GitHub URL
* @returns {string} GitHub name
*/
function getGitName(url) {
return url.replace('https://github.com/', '');
}
/**
* Processes a list of githubIds and adds avatar_base64 property
* @param {Array} ids - Array of id
* @returns {Promise<Array>} Array of avatar_base64
*/
async function processAvatars(ids) {
const processedArray = [];
for (let i = 0; i < ids.length; i++) {
const _id = ids[i];
try {
console.log(`-- Fetching avatar for ${_id} ... [${i + 1}/${ids.length}]`);
// Fetch avatar and convert to base64
const avatarBase64 = await fetchAvatarAsBase64(_id);
processedArray.push({
id: _id,
avatar_base64: avatarBase64
});
console.log(`āœ“ Successfully processed ${_id}`);
} catch (error) {
console.error(`āœ— Error processing ${_id}: ${error.message}`);
}
// Add random delay between 100-2000 millisecond before next request (except for the last member)
if (i < ids.length - 1) {
await new Promise((resolve) => setTimeout(resolve, getRandomDelay(100, 2000)));
}
}
return processedArray;
}
/**
* Processes blog authors data and adds avatar_base64 property
* @param {Object} teamData - Team data
* @param {Array} avatars - Array of avatars
* @returns {Promise<Object>} Blog authors data
*/
async function processBlogAuthors(teamData, avatars) {
const blogAuthorsMapPath = {};
(teamData.pmc.concat(teamData.committer) || []).forEach((m) => {
const gitName = getGitName(m.gitUrl);
const avatarObj = avatars.find((item) => item.id === m.githubId);
blogAuthorsMapPath[gitName] = {
"name": m.name,
"url": m.gitUrl,
"image_url": "data:image/png;base64," + avatarObj.avatar_base64,
"socials": {
"github": gitName
}
}
});
return blogAuthorsMapPath;
}
/**
* Main function
*/
async function main() {
try {
const uniqueGithubIdsSet = new Set();
// 1. Read and parse team
console.log(`==> Reading ${teamSrc} file`);
const teamSrcData = JSON.parse(fs.readFileSync(teamSrc, "utf8"));
// PMC && Committer
(teamSrcData.pmc.concat(teamSrcData.committer) || []).forEach((d) => {
if (d.githubId) {
uniqueGithubIdsSet.add(d.githubId);
}
});
const uniqueGithubArray = Array.from(uniqueGithubIdsSet);
console.log("\n==> Processing avatars");
const avatarsArray = await processAvatars(uniqueGithubArray);
// 2. Write files
console.log(`\n==> Write to ${avatarFile}`);
fs.writeFileSync(avatarFile, JSON.stringify(avatarsArray, null, 2));
// 3. Blog authors
const blogAuthorsMapPaths = await processBlogAuthors(teamSrcData, avatarsArray);
console.log(`\n==> Write to ${authorsFile}`);
fs.writeFileSync(authorsFile, JSON.stringify(blogAuthorsMapPaths, null, 2));
console.log("\nāœ“ Done!");
} catch (error) {
console.error("Error:", error.message);
process.exit(1);
}
}
main();