import './setup.ts'; import * as Sentry from '@sentry/node'; import Pageres from 'pageres'; import type { Screenshot } from 'pageres'; import { ulid } from 'ulid'; import dbConnection from './db.ts'; import type { Database } from './db.ts'; import { getLocaleUrls } from '#shared/domain.ts'; import type { LocaleCode } from '~~/locale/locales.ts'; import { s3, s3Config, s3BucketParams } from '~~/server/cloudServices.ts'; import jwt from '~~/server/jwt.ts'; import isHighLoadTime from '~~/server/overload.js'; const urlBases: Record = {}; for (const [locale, url] of Object.entries(getLocaleUrls(process.env.NUXT_PUBLIC_DOMAIN_BASE))) { urlBases[locale] = `${url}/card/@`; } const sleep = (ms: number): Promise => new Promise((res) => setTimeout(res, ms)); const modes = ['light', 'dark'] as const; const shoot = async (db: Database, mode: 'light' | 'dark'): Promise => { const profiles = (await db.all<{ id: string; locale: LocaleCode; username: string }>(` SELECT profiles.id, profiles.locale, users.username FROM profiles LEFT JOIN users on profiles.userId = users.id WHERE profiles.${mode === 'dark' ? 'cardDark' : 'card'} = '' ORDER BY RANDOM() LIMIT 6 `)).filter(({ locale }) => !isHighLoadTime(locale)); if (profiles.length === 0) { console.log('No profiles in the queue'); return; } const results: Record = {}; try { const pr = new Pageres({ darkMode: mode === 'dark', delay: 3, scale: 1.5, launchOptions: { headless: 'new', }, }); for (const { locale, username } of profiles) { const token = await jwt.sign( locale, { username: 'example', email: 'example@pronouns.page', roles: '', avatarSource: '', bannedReason: null, mfa: false, authenticated: true, }, '15m', process.env.NUXT_PUBLIC_DOMAIN_BASE ?? '', ); pr.source(`${urlBases[locale] + username}?token=${encodeURIComponent(token)}`, ['1024x300'], { filename: `${username}-${locale}`, }); } for (const buffer of await pr.run()) { const match = buffer.filename.match(/(.+)-(\w+)\.png/); if (!match) { console.error('invalid filename', buffer.filename); continue; } const [, username, locale] = match; results[`${locale}/${username.replace(/[^A-Za-z0-9.-]/g, '_')}`] = buffer; } } catch (error) { Sentry.captureException(error); return; } for (const { id, locale, username } of profiles) { const cardId = ulid(); let key = `card/${locale}/${encodeURIComponent(username).replace(/'/g, '_')}-${cardId}.png`; if (mode === 'dark') { key = mode === 'dark' ? key.replace('.png', '-dark.png') : key; } console.log(`Uploading @${username} (${locale}, ${mode}) – ${cardId}`); const buffer = results[`${locale}/${username.replace(/[^A-Za-z0-9.-]/g, '_')}`]; if (buffer === undefined) { console.error('Cannot find the proper buffer!'); continue; } await s3.putObject({ Key: key, Body: buffer, ContentType: 'image/png', ACL: 'public-read', ...s3BucketParams, }); await db.get(` UPDATE profiles SET ${mode === 'dark' ? 'cardDark' : 'card'}='${s3Config.publicAccess}/${key}' WHERE id='${id}'`); } }; (async (): Promise => { const db = await dbConnection(); while (true) { for (const mode of modes) { await sleep(3000); console.log(`Starting mode: ${mode}`); await shoot(db, mode); } } })();