switch to puppeteer
This commit is contained in:
parent
92b6846bbb
commit
6e3bad10f8
6 changed files with 611 additions and 667 deletions
143
assets/DMSans/style.css
Normal file
143
assets/DMSans/style.css
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-SemiBoldItalic.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Thin.ttf') format('truetype');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Light.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-ThinItalic.ttf') format('truetype');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Medium.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Regular.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-LightItalic.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-MediumItalic.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-SemiBold.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-ExtraLightItalic.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-BlackItalic.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Italic.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-ExtraBold.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-ExtraBoldItalic.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Black.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-Bold.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-BoldItalic.ttf') format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DM Sans';
|
||||||
|
src: url('DMSans-ExtraLight.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
|
@ -1,3 +1,8 @@
|
||||||
|
@import url('/assets/DMSans/style.css');
|
||||||
|
body {
|
||||||
|
font-family: 'DM Sans', sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
div {
|
div {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
@ -76,6 +81,7 @@ p {
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
}
|
}
|
||||||
.is-debug .description {
|
.is-debug .description {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -1,35 +1,72 @@
|
||||||
import html2png, { renderHTML as inline } from '@besties/html2png'
|
import puppeteer from 'puppeteer'
|
||||||
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')
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
defaultViewport: {
|
||||||
|
width: 1200,
|
||||||
|
height: 600
|
||||||
|
},
|
||||||
|
headless: 'new',
|
||||||
|
// slowMo: 250,
|
||||||
|
args: [
|
||||||
|
// TODO: fix this
|
||||||
|
'--disable-gpu',
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
'--disable-setuid-sandbox',
|
||||||
|
'--no-sandbox'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
async function wait() {
|
||||||
|
const selectors = [...document.querySelectorAll('img')]
|
||||||
|
await Promise.all([
|
||||||
|
document.fonts.ready,
|
||||||
|
...selectors.map(img => {
|
||||||
|
// Image has already finished loading, let’s see if it worked
|
||||||
|
if (img.complete) {
|
||||||
|
// Image loaded and has presence
|
||||||
|
if (img.naturalHeight !== 0) return
|
||||||
|
// Image failed, so it has no height
|
||||||
|
throw new Error('Image failed to load')
|
||||||
|
}
|
||||||
|
// Image hasn’t loaded yet, added an event listener to know when it does
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
img.addEventListener('load', resolve)
|
||||||
|
img.addEventListener('error', reject)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function renderPage(html) {
|
||||||
|
const page = await browser.newPage()
|
||||||
|
try {
|
||||||
|
await page.setContent(html, {
|
||||||
|
waitUntil: 'domcontentloaded'
|
||||||
|
})
|
||||||
|
await page.evaluate(wait)
|
||||||
|
const buffer = await page.screenshot({
|
||||||
|
type: 'png',
|
||||||
|
encoding: 'binary'
|
||||||
|
})
|
||||||
|
await page.close()
|
||||||
|
return buffer
|
||||||
|
} catch {
|
||||||
|
await page.close()
|
||||||
|
throw new Error('Failed to render page')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default async function (html, options = {}) {
|
export default async function (html, options = {}) {
|
||||||
return html2png(`<style>${style}</style>${html}`, options)
|
if (options.format == 'html') {
|
||||||
|
return await renderHtml(html)
|
||||||
|
}
|
||||||
|
return renderPage(`<style>${style}</style>${html}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
return `<style>${styleNow}</style>${html}`
|
||||||
`<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
height: 100%;
|
|
||||||
font-family: 'DM Sans', Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
* {
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
background-color: black;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title>OpenGraph Debug Preview</title>
|
|
||||||
</head>` + (await inline(`<style>${styleNow}</style>${html}`))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
"author": "besties",
|
"author": "besties",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@besties/html2png": "^1.1.0",
|
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"eta": "^3.1.0",
|
"eta": "^3.1.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"puppeteer": "^21.6.1",
|
||||||
"undici": "^5.24.0"
|
"undici": "^5.24.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
982
pnpm-lock.yaml
generated
982
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
58
server.js
58
server.js
|
@ -1,13 +1,10 @@
|
||||||
import 'dotenv/config'
|
import 'dotenv/config'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { Eta } from 'eta'
|
import { Eta } from 'eta'
|
||||||
import render, { renderHtml } from './lib/render.js'
|
import render from './lib/render.js'
|
||||||
import languageColors from './lib/languagecolors.js'
|
import languageColors from './lib/languagecolors.js'
|
||||||
import { fetch } from 'undici'
|
import { fetch } from 'undici'
|
||||||
import hash from './lib/hash.js'
|
import hash from './lib/hash.js'
|
||||||
import { DMSans, ComicSans } from './lib/fonts.js'
|
|
||||||
// satori is using the native fetch api, causing a warning, so make node-fetch the default
|
|
||||||
globalThis.fetch = fetch
|
|
||||||
|
|
||||||
const forgejoBaseUrl = process.env.FORGEJO_BASE_URL
|
const forgejoBaseUrl = process.env.FORGEJO_BASE_URL
|
||||||
if (!forgejoBaseUrl) throw new Error('FORGEJO_BASE_URL unspecified')
|
if (!forgejoBaseUrl) throw new Error('FORGEJO_BASE_URL unspecified')
|
||||||
|
@ -21,7 +18,7 @@ const eta = new Eta({
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
app.get('/', function (req, res) {
|
app.get('/', function (_, res) {
|
||||||
res.redirect('https://git.gay/gitgay/og.git')
|
res.redirect('https://git.gay/gitgay/og.git')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -50,19 +47,19 @@ async function getLanguages(owner, repo) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const comicSansNames = new Set(['trixie'])
|
app.use('/assets', express.static('assets'))
|
||||||
const allowedFormats = new Set(['png', 'svg', 'json'])
|
|
||||||
|
const defaultFormat = debug ? 'html' : 'png'
|
||||||
|
|
||||||
app.use('/', function (req, res, next) {
|
app.use('/', function (req, res, next) {
|
||||||
if (req.query.format && !allowedFormats.has(req.query.format)) {
|
app.locals.options = req.query.debug
|
||||||
res.status(400)
|
? {
|
||||||
res.end()
|
format: 'html'
|
||||||
return
|
}
|
||||||
}
|
: {
|
||||||
app.locals.options = {
|
format: defaultFormat
|
||||||
fonts: comicSansNames.has(req.params.owner) ? ComicSans : DMSans,
|
}
|
||||||
format: req.query.format ?? 'png'
|
res.type(app.locals.options.format)
|
||||||
}
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -87,15 +84,14 @@ app.get('/:user', async function (req, res) {
|
||||||
await reposResp.json()
|
await reposResp.json()
|
||||||
])
|
])
|
||||||
user.repo_count = repos.length
|
user.repo_count = repos.length
|
||||||
|
user.description = user.description
|
||||||
|
?.split('---\r\n')
|
||||||
|
.slice(0, -1)
|
||||||
|
.join('---\r\n')
|
||||||
const html = await eta.renderAsync('user', {
|
const html = await eta.renderAsync('user', {
|
||||||
user,
|
user,
|
||||||
debug
|
debug
|
||||||
})
|
})
|
||||||
if (debug) {
|
|
||||||
res.send(await renderHtml(html))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.type(app.locals.options.format)
|
|
||||||
res.set('Content-Disposition', 'inline')
|
res.set('Content-Disposition', 'inline')
|
||||||
res.send(await render(html, app.locals.options))
|
res.send(await render(html, app.locals.options))
|
||||||
})
|
})
|
||||||
|
@ -128,11 +124,6 @@ app.get('/:owner/:repo', async function (req, res) {
|
||||||
languageColors,
|
languageColors,
|
||||||
debug
|
debug
|
||||||
})
|
})
|
||||||
if (debug) {
|
|
||||||
res.send(await renderHtml(html))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.type(app.locals.options.format)
|
|
||||||
res.set('Content-Disposition', 'inline')
|
res.set('Content-Disposition', 'inline')
|
||||||
res.send(await render(html, app.locals.options))
|
res.send(await render(html, app.locals.options))
|
||||||
})
|
})
|
||||||
|
@ -174,11 +165,6 @@ app.get('/:owner/:repo/commit/:hash', async function (req, res) {
|
||||||
languageColors,
|
languageColors,
|
||||||
debug
|
debug
|
||||||
})
|
})
|
||||||
if (debug) {
|
|
||||||
res.send(await renderHtml(html))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.type(app.locals.options.format)
|
|
||||||
res.set('Content-Disposition', 'inline')
|
res.set('Content-Disposition', 'inline')
|
||||||
res.send(await render(html, app.locals.options))
|
res.send(await render(html, app.locals.options))
|
||||||
})
|
})
|
||||||
|
@ -219,11 +205,6 @@ app.get('/:owner/:repo/issues/:num', async function (req, res) {
|
||||||
languageColors,
|
languageColors,
|
||||||
debug
|
debug
|
||||||
})
|
})
|
||||||
if (debug) {
|
|
||||||
res.send(await renderHtml(html))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.type(app.locals.options.format)
|
|
||||||
res.set('Content-Disposition', 'inline')
|
res.set('Content-Disposition', 'inline')
|
||||||
res.send(await render(html, app.locals.options))
|
res.send(await render(html, app.locals.options))
|
||||||
})
|
})
|
||||||
|
@ -270,11 +251,6 @@ app.get('/:owner/:repo/pulls/:num', async function (req, res) {
|
||||||
languageColors,
|
languageColors,
|
||||||
debug
|
debug
|
||||||
})
|
})
|
||||||
if (debug) {
|
|
||||||
res.send(await renderHtml(html))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.type(app.locals.options.format)
|
|
||||||
res.set('Content-Disposition', 'inline')
|
res.set('Content-Disposition', 'inline')
|
||||||
res.send(await render(html, app.locals.options))
|
res.send(await render(html, app.locals.options))
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue