123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- import type { NextApiRequest, NextApiResponse } from 'next'
-
- const AV_SIZE = 32
- const PADDING = 4
- const COLS = 16
-
- type SponsorResult = { avatarUrl: string; login: string }
-
- type QueryResult = {
- node: { sponsorEntity: { avatarUrl: string; login: string } }
- }
-
- function getXY(i: number) {
- return [(i % COLS) * (AV_SIZE + PADDING), Math.floor(i / COLS) * (AV_SIZE + PADDING)]
- }
-
- export default async function GetSponsors(_req: NextApiRequest, res: NextApiResponse) {
- const sponsorInfo = await fetch('https://api.github.com/graphql', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'bearer ' + process.env.GITHUB_API_SECRET,
- },
- body: JSON.stringify({
- query: `{
- viewer {
- sponsors(first: 0) {
- totalCount
- }
- sponsorshipsAsMaintainer(first: 100, orderBy: {
- field:CREATED_AT,
- direction:DESC
- }) {
- edges {
- node {
- sponsorEntity {
- ...on User {
- avatarUrl
- login
- }
- }
- }
- }
- }
- }
- }`,
- }),
- }).then((res) => res.json())
-
- // Get the total count of sponsors
- const totalCount: number = sponsorInfo.data.viewer.sponsors.totalCount
-
- // Map out the login and avatarUrl for each sponsor
- const sponsors = (
- sponsorInfo.data.viewer.sponsorshipsAsMaintainer.edges as QueryResult[]
- ).map<SponsorResult>((edge) => ({
- login: edge.node.sponsorEntity.login,
- avatarUrl: edge.node.sponsorEntity.avatarUrl?.replace(/&/g, '&') ?? '',
- }))
-
- // If we're going to create a more link (see below), then make room for it if necessary
-
- if (totalCount > 100 && sponsors.length % COLS <= 2) {
- sponsors.pop()
- sponsors.pop()
- sponsors.pop()
- }
-
- // Generate images for each of the first 100 sponsors.
-
- const avatars = sponsors
- .map(({ avatarUrl, login }, i) => {
- const [x, y] = getXY(i)
- return `<image alt="${login}" href="${avatarUrl}" x="${x}" y="${y}" width="${AV_SIZE}" height="${AV_SIZE}"/>`
- })
- .join('')
-
- // If there are more than 100 sponsors, generate some text to list how many more.
-
- let more = ''
-
- if (totalCount > sponsors.length) {
- // More text
- const [x, y] = getXY(sponsors.length)
- const width = (AV_SIZE + PADDING) * 3
- more = `<g transform="translate(${x},${y})"><text text-lenth="${width}" font-family="Arial" font-size="12px" font-weight="bold" text-anchor="middle" text-align="center" x="${
- width / 2
- }" y="${AV_SIZE / 2 + 3}">...and ${totalCount - sponsors.length} more!</text></g>`
- }
-
- const svgImage = `
- <svg xmlns="http://www.w3.org/2000/svg"><a href="https://github.com/sponsors/steveruizok">${avatars}${more}</a></svg>`
-
- res
- .status(200)
- .setHeader('Cache-Control', 'max-age=604800')
- .setHeader('Content-Type', 'image/svg+xml')
- .send(svgImage)
- }
|