-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement PubKey Profile Image generator
- Loading branch information
Showing
31 changed files
with
935 additions
and
1,041 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
128 changes: 128 additions & 0 deletions
128
api/src/lib/features/generate-pub-key-profile-image.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** @jsx JSX.createElement */ | ||
/** @jsxFrag JSX.Fragment */ | ||
import { Builder, JSX } from 'canvacord' | ||
|
||
interface Props { | ||
message: string | ||
username: string | ||
footer: string | ||
header: string | ||
avatarUrl: string | ||
logoUrl: string | ||
} | ||
|
||
export interface GenerateDynamicImageOptions { | ||
width: number | ||
height: number | ||
message?: string | ||
footer?: string | ||
header?: string | ||
username?: string | ||
avatarUrl?: string | ||
logoUrl?: string | ||
} | ||
export class GeneratePubKeyProfileImage extends Builder<Props> { | ||
constructor(props: GenerateDynamicImageOptions) { | ||
// set width and height | ||
super(props.width, props.height) | ||
const issuedAt = new Date().toISOString().split('T')[0] | ||
// 30 days from now | ||
const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] | ||
// initialize props | ||
this.bootstrap({ | ||
message: props.message ?? `256 Points`, | ||
avatarUrl: props.avatarUrl ?? 'https://avatars.githubusercontent.com/u/36491?v=4', | ||
logoUrl: | ||
props.logoUrl ?? 'https://raw.githubusercontent.com/pubkeyapp/pubkey-brand/main/logo/logo-white-txt400h.png', | ||
username: props.username ?? 'beeman', | ||
footer: props.footer ?? `Issued at: ${issuedAt} Expires at: ${expiresAt}`, | ||
header: props.header ?? `PubKey Profile`, | ||
}) | ||
} | ||
|
||
async render(): Promise<JSX.Element> { | ||
return ( | ||
<TemplateRender | ||
avatarUrl={this.options.get('avatarUrl')} | ||
logoUrl={this.options.get('logoUrl')} | ||
footer={this.options.get('footer')} | ||
header={this.options.get('header')} | ||
message={this.options.get('message')} | ||
username={this.options.get('username')} | ||
/> | ||
) | ||
} | ||
} | ||
|
||
function TemplateRender({ | ||
avatarUrl, | ||
logoUrl, | ||
footer, | ||
header, | ||
message, | ||
username, | ||
}: { | ||
avatarUrl: string | ||
logoUrl: string | ||
footer: string | ||
header: string | ||
message: string | ||
username: string | ||
}) { | ||
return ( | ||
<div | ||
className="h-full w-full flex flex-col justify-between bg-gray-900 rounded-[4] text-white text-2xl" | ||
style={{ fontFamily: 'BalooBhai2 Regular' }} | ||
> | ||
<TemplateHeader logoUrl={logoUrl} header={header} /> | ||
<TemplateMain avatarUrl={avatarUrl} message={message} username={username} /> | ||
|
||
<TemplateFooter footer={footer} /> | ||
</div> | ||
) | ||
} | ||
|
||
function TemplateMain({ avatarUrl, message, username }: { avatarUrl: string; message: string; username: string }) { | ||
const usernameSize = username.length > 12 ? 'text-[64px]' : 'text-[92px]' | ||
return ( | ||
<div className="flex flex-col flex-grow px-16"> | ||
<div className="flex flex-col justify-between flex-grow bg-gray-800 rounded-[4]"> | ||
<div className="flex flex-col items-center justify-center pt-24 "> | ||
<img src={avatarUrl} alt="" className="flex h-[50] w-[50] rounded-xl" /> | ||
<div className={`mt-8 flex ${usernameSize}`} style={{ fontFamily: 'BalooBhai2 ExtraBold' }}> | ||
{username} | ||
</div> | ||
</div> | ||
<div | ||
style={{ fontFamily: 'BalooBhai2 SemiBold' }} | ||
className="flex flex-grow text-[92px] w-full items-center justify-center text-gray-400 mb-4" | ||
> | ||
{message} | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
function TemplateHeader({ logoUrl, header }: { logoUrl: string; header: string }) { | ||
return ( | ||
<div className="px-16 text-4xl flex h-[32]" style={{ fontFamily: 'BalooBhai2 ExtraBold' }}> | ||
<div className="flex w-full items-center justify-between"> | ||
<div> | ||
<img src={logoUrl} alt="" className="flex h-[16]" /> | ||
</div> | ||
<div>{header}</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
function TemplateFooter({ footer }: { footer?: string }) { | ||
return ( | ||
<div className="px-16 flex text-2xl h-[32]" style={{ fontFamily: 'BalooBhai2 Medium' }}> | ||
<div className="flex w-full items-center justify-center"> | ||
<div className="flex text-gray-400">{footer}</div> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Font } from 'canvacord' | ||
import { Request, Response } from 'express' | ||
import { join } from 'node:path' | ||
import { GeneratePubKeyProfileImage } from './generate-pub-key-profile-image' | ||
|
||
export function pubkeyProfileRoute({ cwd }: { cwd: string }) { | ||
const fontRoot = join(cwd, 'assets/fonts') | ||
|
||
Font.loadDefault() | ||
Font.fromFileSync(join(fontRoot, 'BalooBhai2/BalooBhai2-ExtraBold.ttf'), 'BalooBhai2 ExtraBold') | ||
Font.fromFileSync(join(fontRoot, 'BalooBhai2/BalooBhai2-Bold.ttf'), 'BalooBhai2 Bold') | ||
Font.fromFileSync(join(fontRoot, 'BalooBhai2/BalooBhai2-Medium.ttf'), 'BalooBhai2 Medium') | ||
Font.fromFileSync(join(fontRoot, 'BalooBhai2/BalooBhai2-Regular.ttf'), 'BalooBhai2 Regular') | ||
Font.fromFileSync(join(fontRoot, 'BalooBhai2/BalooBhai2-SemiBold.ttf'), 'BalooBhai2 SemiBold') | ||
|
||
return async (req: Request, res: Response) => { | ||
const query = req.query as { | ||
avatarUrl?: string | ||
footer?: string | ||
header?: string | ||
logoUrl?: string | ||
message?: string | ||
username?: string | ||
} | ||
|
||
const imageBuilder = new GeneratePubKeyProfileImage({ | ||
width: 1024, | ||
height: 1024, | ||
message: query.message, | ||
avatarUrl: query.avatarUrl, | ||
footer: query.footer, | ||
header: query.header, | ||
logoUrl: query.logoUrl, | ||
username: query.username, | ||
}) | ||
|
||
const image = await imageBuilder.build({ format: 'png' }) | ||
|
||
// set headers | ||
res.setHeader('Content-Type', 'pubkey-profile/png') | ||
res.setHeader('Cache-Control', 'no-store no-cache must-revalidate private max-age=0 s-maxage=0 proxy-revalidate') | ||
|
||
// send pubkey-profile | ||
res.send(image) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.