-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// 1. get the repo's contributors list | ||
// 2. generate svg | ||
// 3. save to local | ||
// 4. 根据仓库地址,会保存头像在本地,如果通过actions 触发后,通过对比,本地存在头像则不会再更新 | ||
// 4.1 第4点中,bug:如果用户更新,则还是旧头像。出现用户头像,除非用头像生成 md5 存储作为指纹 | ||
// import { http } from 'node:http' | ||
|
||
import {writeFile} from 'node:fs/promises'; | ||
import {chunk} from 'lodash'; | ||
import {svgStart, svgEnd, asyncHandleUsersSVG, asyncHandlerUserDefsSVG} from './svg'; | ||
import {UserConfig, UserItem} from '../types'; | ||
import {getOwnerRepo} from '../utils'; | ||
|
||
import config from '../../config' | ||
import console from 'node:console'; | ||
|
||
// global constants | ||
const SVG_WIDTH = 800; | ||
const SVG_HEIGHT = 370; | ||
const FONT_SIZE = 0 | ||
const BASE_SIZE = 100 | ||
|
||
export const generateUserListSVG = async (userList: UserItem[], config: UserConfig) => { | ||
|
||
// split => two-dimensional array | ||
let splitList: UserItem[] | UserItem[][] = userList | ||
|
||
const svgWidth = config.width || SVG_WIDTH | ||
const svgHeight = config.height || SVG_HEIGHT | ||
const baseSize = config.size || BASE_SIZE | ||
const fontSize = config.fontSize || FONT_SIZE | ||
|
||
const isRadius = config.isRadius !== false // default need radius => 50%; | ||
const outSize = fontSize + baseSize | ||
const oneRowMax = Math.floor(svgWidth / outSize) | ||
|
||
// split new array | ||
if (userList.length > oneRowMax) { | ||
splitList = chunk(userList, oneRowMax) | ||
} | ||
|
||
const svgConfig = { | ||
baseSize, | ||
fontSize, | ||
oneRowMax, | ||
outSize, | ||
svgWidth, | ||
svgHeight, | ||
isRadius, | ||
} | ||
|
||
// radius | ||
if (config.isRadius === undefined || config.isRadius === true) { | ||
return `${svgStart(svgWidth, svgHeight)} | ||
<defs> | ||
${await asyncHandlerUserDefsSVG(splitList, svgConfig)} | ||
</defs> | ||
${await asyncHandleUsersSVG(splitList, svgConfig)} | ||
${svgEnd()} | ||
` | ||
} else if (!config.isRadius) { | ||
const userBlockData = await asyncHandleUsersSVG(splitList, svgConfig) | ||
return `${svgStart(svgWidth, svgHeight)} | ||
${userBlockData} | ||
${svgEnd()} | ||
` | ||
} | ||
return '' | ||
} | ||
|
||
export const saveSVG = async (ownerRepo: string, userConfig: UserConfig, repoUserList: UserItem[]) => { | ||
console.log('saveSVG=>', ownerRepo, repoUserList.length) | ||
const {owner, repo} = getOwnerRepo && getOwnerRepo(ownerRepo) || {} | ||
if (!owner || !repo) { | ||
console.error('Invalid repo address:', ownerRepo) | ||
return | ||
} | ||
try { | ||
const svgStr = await generateUserListSVG(repoUserList, userConfig) | ||
const filename = `./repos/${owner}/${repo}.svg` | ||
await writeFile(filename, svgStr, {encoding: 'utf-8'}) | ||
} catch (error) { | ||
console.error('err=>', error) | ||
} | ||
} | ||
|
||
// saveSVG('veaba/contributors',listTen) | ||
// saveSVG('vuejs-translations/docs-zh-cn',listTen) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import axios from "axios" | ||
|
||
import { createWriteStream } from 'node:fs' | ||
import { resolve } from 'path' | ||
import { UserConfig, UserItem } from "../types" | ||
import { sortBy } from 'lodash' | ||
import { data } from '../../tests/mock' | ||
import { getTotalList, getOwnerRepo } from "../utils" | ||
|
||
|
||
|
||
|
||
/** | ||
* get contributors avatar to public/avatars | ||
* | ||
* 1、TODO check local has been save ? | ||
* | ||
* 2、TODO 通过存在,则通过 md5 判断,一致则略过 | ||
* | ||
* 3、TODO 不一致则拉取 | ||
* | ||
* 4、TODO if not, get remote data and save to public/avatars | ||
* | ||
* 5、TODO 存储一份本地 avatars 映射的 md5 list | ||
* | ||
*/ | ||
export const getRepoData = async (repoKey: string, repoConfig: UserConfig): Promise<UserItem[]> => { | ||
const { owner, repo } = getOwnerRepo(repoKey) | ||
|
||
let repData = [] | ||
try { | ||
const resp = await axios.get(`http://api.github.com/repos/${repoKey}/stats/contributors`) | ||
if (resp?.data) { | ||
repData = resp.data || [] | ||
} | ||
} catch (err) { | ||
console.error('get repo stats contributors err=>') | ||
} | ||
repData = getTotalList(data, repoConfig) | ||
|
||
const sortList = sortBy(repData, (item) => -item.total) | ||
return sortList | ||
} | ||
|
||
|
||
export const downloadAvatar = async (userItem: UserItem) => { | ||
try { | ||
const resp = await axios.get(`https://avatars.githubusercontent.com/u/${userItem.id}?v=4`, { | ||
responseType: "stream" | ||
}) | ||
|
||
if (resp?.status === 200) { | ||
const writer = createWriteStream(resolve(__dirname, `../public/avatars/${userItem.id}.jpg`),) | ||
resp.data.pipe(writer) | ||
return new Promise((resolve, reject) => { | ||
writer.on('finish', resolve) | ||
writer.on('error', reject) | ||
}) | ||
} | ||
} catch (err) { | ||
console.error('err=>', err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { readFile } from 'node:fs/promises' | ||
export const imagePathToBase64 = async (imagePath: string) => { | ||
const imageBuffer = await readFile(imagePath) | ||
return imageBuffer.toString('base64') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// import config from '../../config' | ||
// import { resolve } from 'path' | ||
// import { saveSVG } from './app'; | ||
// import { writeFile, access } from "node:fs/promises" // access | ||
// import { downloadAvatar, getRepoData } from './github'; | ||
// import { MD5Item, UserItem } from '../types'; | ||
// // import { isHasFile, readMD5 } from "../utils" | ||
// | ||
// import md5JSON from '../../public/avatars/avatarsMD5.json' | ||
// | ||
// const md5s: any = md5JSON | ||
// | ||
// interface TypesContributors { | ||
// repo: string; | ||
// } | ||
// | ||
// const updateAvatars = async (sortList: UserItem[]) => { | ||
// await Promise.all(sortList.map(async userItem => { | ||
// | ||
// const isHas = await isHasFile(resolve(__dirname, `../public/avatars/${userItem.id}.jpg`)) | ||
// if (!isHas) { | ||
// return await downloadAvatar(userItem) | ||
// } | ||
// })) | ||
// } | ||
// | ||
// // export const serverStart = async () => { | ||
// // const now = Date.now() | ||
// // console.log('开始=>', new Date()) | ||
// // | ||
// // console.time('task=>') | ||
// // const repos = Object.keys(config) | ||
// // // const reposConfigs = Object.values(config) | ||
// // | ||
// // await Promise.all(repos.map(async repo => { | ||
// // const repoConfig = config[repo] | ||
// // const repoList = await getRepoData(repo, repoConfig) | ||
// // await updateAvatars(repoList) | ||
// // await saveSVG(repo, repoList) | ||
// // })) | ||
// // | ||
// // console.timeEnd('task=>') | ||
// // console.log('结束=>', new Date(), Date.now() - now) | ||
// // | ||
// // } | ||
// | ||
// // serverStart() |