diff --git a/website/src/app/layout/site-header.html b/website/src/app/layout/site-header.html index 2d6c93892..655308166 100644 --- a/website/src/app/layout/site-header.html +++ b/website/src/app/layout/site-header.html @@ -40,7 +40,7 @@ {{ card.ti } + + @if (contributors().length > 0) { +
+ Contributors: + + @for (contributor of contributors(); track contributor.login) { + + + + } + +
+ } +
(() => + protected readonly sponsorsResource = httpResource<{ sponsors: GithubUser[] }>(() => this.isBrowser ? '/api/sponsors' : undefined, ); @@ -48,6 +48,14 @@ export class Landing { () => this.sponsorsResource.value()?.sponsors ?? [], ); + protected readonly contributorsResource = httpResource<{ contributors: GithubUser[] }>(() => + this.isBrowser ? '/api/contributors' : undefined, + ); + + protected readonly contributors = computed( + () => this.contributorsResource.value()?.contributors ?? [], + ); + protected readonly cards = [ { icon: 'zap', diff --git a/website/src/server/github-api.ts b/website/src/server/github-api.ts index e9db180d5..e9aa8830e 100644 --- a/website/src/server/github-api.ts +++ b/website/src/server/github-api.ts @@ -371,6 +371,20 @@ githubApi.get('/leaderboard/:board', async (req, res) => { res.json({ entries }); }); +/** All repository contributors for the landing page. */ +githubApi.get('/contributors', async (_req, res) => { + const { status, data } = await github(`/repos/${REPO}/contributors?per_page=100`, 3600); + if (status !== 200) { + res.status(503).json({ error: 'github request failed' }); + return; + } + const contributors = (data as any[]) + .filter((u) => !EXCLUDED_USERS.has(u.login) && u.type === 'User') + .map((u) => ({ login: u.login, avatar: u.avatar_url })); + res.set('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=86400'); + res.json({ contributors }); +}); + /** Repository stats for the landing page. */ githubApi.get('/stats', async (_req, res) => { const { status, data } = await github(`/repos/${REPO}`, 900);