add user profile templating

This commit is contained in:
aria blue 2023-12-16 22:36:07 -05:00
parent f0fc3df881
commit 1dafd75723
4 changed files with 115 additions and 6 deletions

View file

@ -1,4 +1,4 @@
import html2png, { renderHtml as htmlParser } from '@besties/html2png' import html2png from '@besties/html2png'
import fs from 'node:fs' import fs from 'node:fs'
const style = fs.readFileSync('./assets/style.css', 'utf8') const style = fs.readFileSync('./assets/style.css', 'utf8')
@ -9,5 +9,5 @@ export default async function (html, options = {}) {
export async function renderHtml(html) { export async function renderHtml(html) {
const styleNow = await fs.promises.readFile('./assets/style.css', 'utf8') const styleNow = await fs.promises.readFile('./assets/style.css', 'utf8')
return await htmlParser(`<style>${styleNow}</style>${html}`) return `<style>${styleNow}</style>${html}`
} }

View file

@ -6,8 +6,8 @@ settings:
dependencies: dependencies:
'@besties/html2png': '@besties/html2png':
specifier: ^1.0.0 specifier: ^1.0.1
version: 1.0.0 version: 1.0.1
dotenv: dotenv:
specifier: ^16.3.1 specifier: ^16.3.1
version: 16.3.1 version: 16.3.1
@ -63,8 +63,8 @@ packages:
js-tokens: 4.0.0 js-tokens: 4.0.0
dev: true dev: true
/@besties/html2png@1.0.0: /@besties/html2png@1.0.1:
resolution: {integrity: sha512-34dB4CxXL5xA/rJtpqI4UVp62DeQf2DQY3t37xSwar1nUde+i8VCCrRb6NfvZK7pz3v7gHk1M4uA7JDZpeWqKw==} resolution: {integrity: sha512-qBkWy66WjvF8hrY2yEzPAIgbyCpTQZI1nt5+KcKg6kZtzvJb1w9GqoGp2MaRKmFTSENyjwK2WYY3CzkI7d49lw==}
dependencies: dependencies:
'@resvg/resvg-wasm': 2.4.1 '@resvg/resvg-wasm': 2.4.1
html-entities: 2.4.0 html-entities: 2.4.0

View file

@ -52,6 +52,44 @@ async function getLanguages(owner, repo) {
const comicSansNames = new Set(['trixie']) const comicSansNames = new Set(['trixie'])
app.get('/:user', async function (req, res) {
const [userResp, reposResp] = await Promise.all([
await fetch(
`${forgejoBaseUrl}/api/v1/users/${encodeURIComponent(req.params.user)}`
),
await fetch(
`${forgejoBaseUrl}/api/v1/users/${encodeURIComponent(
req.params.user
)}/repos`
)
])
if (!userResp.ok) {
res.status(userResp.status)
res.end()
return
}
const [user, repos] = await Promise.all([
await userResp.json(),
await reposResp.json()
])
user.repo_count = repos.length
const html = await eta.renderAsync('user', {
user,
debug
})
if (debug) {
res.send(await renderHtml(html))
return
}
const options = {
fonts: DMSans,
format: req.query.format == 'svg' ? 'svg' : 'png'
}
res.type(options.format)
res.set('Content-Disposition', 'inline')
res.send(await render(html, options))
})
app.get('/:owner/:repo', async function (req, res) { app.get('/:owner/:repo', async function (req, res) {
const [repoResp, commitsResp, languages] = await Promise.all([ const [repoResp, commitsResp, languages] = await Promise.all([
fetch( fetch(

71
views/user.eta Normal file
View file

@ -0,0 +1,71 @@
<% layout('layout') %>
<%
let description = it.user.description?.replace(/https?:\/\/([\S]+)/g, '$1')
function prettyNumber(number) {
if (number < 30) return number
if (number < 1000) return Math.floor(number/10) * 10 + '+'
return Math.floor(number / 1000)+'k'
}
%>
<div class="main user">
<div class="contents">
<div class="info">
<p class="title"><span><%= it.user.full_name || it.user.username %></span> <span class="username">@<%= it.user.username %></span></p>
<p class="description"><%= description %></p>
</div>
<div class="graphics">
<% if (it.user.avatar_url) { %>
<img width="120" height="120" src="<%= it.user.avatar_url %>"></img>
<% } %>
</div>
</div>
<div class="info-line">
<div class="fact">
<p class="fact-heading"><%= it.user.followers_count %></p>
<p class="fact-subtitle">Followers</p>
</div>
<div class="fact">
<p class="fact-heading"><%= it.user.following_count %></p>
<p class="fact-subtitle">Following</p>
</div>
<div class="fact">
<p class="fact-heading"><%= prettyNumber(it.user.repo_count) %></p>
<p class="fact-subtitle">Repos</p>
</div>
<div class="fact">
<p class="fact-heading"><%= it.user.starred_repos_count %></p>
<p class="fact-subtitle">Stars Given</p>
</div>
</div>
</div>
<style>
.user .title {
white-space: nowrap;
display: flex;
}
.user .title span:not(.username) {
display: flex;
align-items: center;
gap: 1rem;
font-size: 3rem;
font-weight: 600;
color: #aeb3d0;
}
.user .title .username {
font-size: 1.75rem;
gap: 0.5rem;
font-weight: 400;
color: #6d728f;
}
.fact {
display: flex;
margin-right: 0.5em;
font-size: 1.5rem;
flex-direction: column;
}
.fact-heading {
color: #8c91af;
font-size: 2rem;
font-weight: 600;
}
</style>