1
0
Fork 0

feat(shared): Add dynamic OG images

This commit is contained in:
joostdecock 2023-11-03 15:36:09 +01:00
parent d36643d4a8
commit 2472ab1824
22 changed files with 141 additions and 169 deletions

View file

@ -28,16 +28,16 @@
</g> </g>
<g> <g>
<text class="gray-300 tiny italic center"> <text class="gray-300 tiny italic center">
<tspan x="100" y="179">intro_1</tspan> <tspan x="100" y="179">__intro_1__</tspan>
<tspan x="100" y="190">intro_2</tspan> <tspan x="100" y="190">__intro_2__</tspan>
</text> </text>
<text class="gray-200 big bold center"> <text class="gray-200 big bold center">
<tspan x="100" y="95">1title_1</tspan> <tspan x="100" y="95">__1title_1__</tspan>
<tspan x="100" y="90">2title_1</tspan> <tspan x="100" y="90">__2title_1__</tspan>
<tspan x="100" dy="24">2title_2</tspan> <tspan x="100" dy="24">__2title_2__</tspan>
<tspan x="100" y="80">3title_1</tspan> <tspan x="100" y="80">__3title_1__</tspan>
<tspan x="100" dy="24">3title_2</tspan> <tspan x="100" dy="24">__3title_2__</tspan>
<tspan x="100" dy="24">3title_3</tspan> <tspan x="100" dy="24">__3title_3__</tspan>
</text> </text>
<text class="bold" x="6" y="15"> <text class="bold" x="6" y="15">
<tspan dx="0" fill="#f87171">F</tspan> <tspan dx="0" fill="#f87171">F</tspan>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

View file

@ -1,5 +1,5 @@
--- ---
title: Motivation title: My reasoning behind FreeSewing's Revenue Pledge
--- ---
<Note> <Note>
@ -9,7 +9,7 @@ his motivations for [the FreeSewing revenue pledge](/docs/about/pledge/)
</Note> </Note>
You probably assume that we ask for money to keep the servers running. But that's not exactly true. You probably assume that I ask for money to keep the servers running. But that's not exactly true.
I don't know if you're familiar with the phrase **noblesse oblige** but it essentially means that privilege entails responsibility. I don't know if you're familiar with the phrase **noblesse oblige** but it essentially means that privilege entails responsibility.
@ -49,7 +49,7 @@ because FreeSewing is a force for good.
Here's the tricky part: People give less once they know the money goes to charity. Here's the tricky part: People give less once they know the money goes to charity.
I wish it wasn't the case, but it is. I wish it wasn't the case, but it is.
So we're presenting [our subscription options](/community/join) like you would see on a for-profit site. So I'm presenting [the subscription options](/patrons/join) like you would see on a for-profit site.
It seems more intuitive this way, and also just works better. It seems more intuitive this way, and also just works better.
Yes, everything is free, and the money doesn't actually go to paying the server bills Yes, everything is free, and the money doesn't actually go to paying the server bills

View file

@ -28,7 +28,7 @@ ${banner}
entryPoints: ['src/index.mjs'], entryPoints: ['src/index.mjs'],
format: 'esm', format: 'esm',
outfile: 'dist/index.mjs', outfile: 'dist/index.mjs',
external: ['./local-config.mjs'], external: ['./local-config.mjs', 'sharp'],
metafile: process.env.VERBOSE ? true : false, metafile: process.env.VERBOSE ? true : false,
minify: process.env.NO_MINIFY ? false : true, minify: process.env.NO_MINIFY ? false : true,
sourcemap: true, sourcemap: true,

View file

@ -59,7 +59,11 @@ const introAsLines = (intro, type) => {
// Does it fit on one line? // Does it fit on one line?
if (intro.length <= imgConfig.templates.chars[type].intro) return [intro] if (intro.length <= imgConfig.templates.chars[type].intro) return [intro]
// Two lines it is // Two lines it is
return splitLine(intro, imgConfig.templates.chars[type].intro) const lines = splitLine(intro, imgConfig.templates.chars[type].intro)
if (lines[1].length > imgConfig.templates.chars[type].intro)
lines[1] = lines[1].slice(0, imgConfig.templates.chars[type].intro) + '…'
return lines
} }
/* Hide unused placeholders */ /* Hide unused placeholders */
@ -67,11 +71,11 @@ const hidePlaceholders = (list, type) => {
let svg = templates[type] let svg = templates[type]
for (const i of list) { for (const i of list) {
svg = svg svg = svg
.replace(`${i}title_1`, '') .replace(`__${i}title_1__`, '')
.replace(`${i}title_2`, '') .replace(`__${i}title_2__`, '')
.replace(`${i}title_3`, '') .replace(`__${i}title_3__`, '')
.replace(`${i}title_4`, '') .replace(`__${i}title_4__`, '')
.replace(`${i}title_5`, '') .replace(`__${i}title_5__`, '')
} }
return svg return svg
@ -82,61 +86,70 @@ const decorateSvg = (data) => {
let svg let svg
// Single title line // Single title line
if (data.title.length === 1) { if (data.title.length === 1) {
svg = hidePlaceholders([2, 3, 4, 5], data.type).replace(`1title_1`, data.title[0]) svg = hidePlaceholders([2, 3, 4, 5], data.type).replace(`__1title_1__`, data.title[0])
} }
// Double title line // Double title line
else if (data.title.length === 2) { else if (data.title.length === 2) {
svg = hidePlaceholders([1, 3, 4, 5], data.type) svg = hidePlaceholders([1, 3, 4, 5], data.type)
.replace(`2title_1`, data.title[0]) .replace(`__2title_1__`, data.title[0])
.replace(`2title_2`, data.title[1]) .replace(`__2title_2__`, data.title[1])
} }
// Triple title line // Triple title line
else if (data.title.length === 3) { else if (data.title.length === 3) {
svg = hidePlaceholders([1, 2, 4, 5], data.type) svg = hidePlaceholders([1, 2, 4, 5], data.type)
.replace(`3title_1`, data.title[0]) .replace(`__3title_1__`, data.title[0])
.replace(`3title_2`, data.title[1]) .replace(`__3title_2__`, data.title[1])
.replace(`3title_3`, data.title[2]) .replace(`__3title_3__`, data.title[2])
} }
// Quadruple title line // Quadruple title line
else if (data.title.length === 4) { else if (data.title.length === 4) {
svg = hidePlaceholders([1, 2, 3, 5], data.type) svg = hidePlaceholders([1, 2, 3, 5], data.type)
.replace(`4title_1`, data.title[0]) .replace(`__4title_1__`, data.title[0])
.replace(`4title_2`, data.title[1]) .replace(`__4title_2__`, data.title[1])
.replace(`4title_3`, data.title[2]) .replace(`__4title_3__`, data.title[2])
.replace(`4title_4`, data.title[3]) .replace(`__4title_4__`, data.title[3])
} }
// Quintuple title line // Quintuple title line
else if (data.title.length === 5) { else if (data.title.length === 5) {
svg = hidePlaceholders([1, 2, 3, 4], data.type) svg = hidePlaceholders([1, 2, 3, 4], data.type)
.replace(`5title_1`, data.title[0]) .replace(`__5title_1__`, data.title[0])
.replace(`5title_2`, data.title[1]) .replace(`__5title_2__`, data.title[1])
.replace(`5title_3`, data.title[2]) .replace(`__5title_3__`, data.title[2])
.replace(`5title_4`, data.title[3]) .replace(`__5title_4__`, data.title[3])
.replace(`5title_5`, data.title[4]) .replace(`__5title_5__`, data.title[4])
} }
return svg return svg
.replace(`intro_1`, data.intro[0] || '') .replace(`__intro_1__`, data.intro[0] || '')
.replace(`intro_2`, data.intro[1] || '') .replace(`__intro_2__`, data.intro[1] || '')
.replace('site', data.site || '') .replace('__site__', data.site || '')
} }
export function ImgController() {} export function ImgController() {}
const parseInput = (req) => {
let input = {}
if (req.params.data) {
try {
input = JSON.parse(decodeURIComponent(req.params.data))
} catch (err) {
console.log(err)
}
} else input = req.body
return input
}
/* /*
* Generate an Open Graph image * Generate an Open Graph image
* See: https://freesewing.dev/reference/backend/api * See: https://freesewing.dev/reference/backend/api
*/ */
ImgController.prototype.generate = async (req, res) => { ImgController.prototype.generate = async (req, res) => {
const input = parseInput(req)
/* /*
* Extract body parameters * Extract body parameters
*/ */
const { const { site = false, title = '', intro = '', type = 'wide' } = input
site = false,
title = 'Please provide a title',
intro = 'Please provide an intro',
type = 'wide',
} = req.body
if (site && imgConfig.sites.indexOf(site) === -1) if (site && imgConfig.sites.indexOf(site) === -1)
return res.status(400).send({ error: 'invalidSite' }) return res.status(400).send({ error: 'invalidSite' })

View file

@ -7,4 +7,7 @@ export function imgRoutes(tools) {
// Generate an image // Generate an image
app.post('/img', (req, res) => Img.generate(req, res, tools)) app.post('/img', (req, res) => Img.generate(req, res, tools))
// Generate an image
app.get('/img/:data', (req, res) => Img.generate(req, res, tools))
} }

View file

@ -1,7 +1,6 @@
// Dependencies // Dependencies
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
// Components // Components
import Head from 'next/head'
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
import { Robot } from 'shared/components/robot/index.mjs' import { Robot } from 'shared/components/robot/index.mjs'
import { Popout } from 'shared/components/popout/index.mjs' import { Popout } from 'shared/components/popout/index.mjs'
@ -12,23 +11,7 @@ import { NavLinks, MainSections } from 'shared/components/navigation/sitenav.mjs
const namespaces = [...pageNs] const namespaces = [...pageNs]
const Page404 = () => ( const Page404 = () => (
<PageWrapper title="404: Page not found"> <PageWrapper title="404: Page not found" intro="We could not find what you are looking for">
<Head>
<meta property="og:type" content="article" key="type" />
<meta
property="og:description"
content="There's nothing here. If you followed a link to get here, that link is broken"
key="description"
/>
<meta property="og:article:author" content="Joost De Cock" key="author" />
<meta property="og:image" content={`https://freesewing.dev/og/404/og.png`} key="image" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content={`https://freesewing.dev/`} key="url" />
<meta property="og:locale" content="en_US" key="locale" />
<meta property="og:site_name" content="freesewing.dev" key="site" />
</Head>
<BaseLayout> <BaseLayout>
<BaseLayoutLeft> <BaseLayoutLeft>
<MainSections /> <MainSections />

View file

@ -3,8 +3,8 @@ import { pages } from 'site/prebuild/docs.en.mjs'
// Dependencies // Dependencies
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { loadMdxAsStaticProps } from 'shared/mdx/load.mjs' import { loadMdxAsStaticProps } from 'shared/mdx/load.mjs'
import { freeSewingConfig as config } from 'shared/config/freesewing.config.mjs'
// Components // Components
import Head from 'next/head'
import { PageWrapper, ns } from 'shared/components/wrappers/page.mjs' import { PageWrapper, ns } from 'shared/components/wrappers/page.mjs'
//import { components } from 'shared/components/mdx/index.mjs' //import { components } from 'shared/components/mdx/index.mjs'
import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs' import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs'
@ -27,24 +27,6 @@ import {
*/ */
const DocsPage = ({ page, slug, frontmatter, mdx, mdxSlug }) => ( const DocsPage = ({ page, slug, frontmatter, mdx, mdxSlug }) => (
<PageWrapper {...page} title={frontmatter.title} intro={frontmatter.intro}> <PageWrapper {...page} title={frontmatter.title} intro={frontmatter.intro}>
<Head>
<meta property="og:title" content={frontmatter.title} key="title" />
<meta property="og:type" content="article" key="type" />
<meta property="og:description" content={frontmatter.intro} key="type" />
<meta property="og:article:author" content="Joost De Cock" key="author" />
<meta
property="og:image"
content={`https://canary.backend.freesewing.org/og-img/en/org/${slug}}`}
key="image"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content={`https://freesewing.dev/${slug}`} key="url" />
<meta property="og:locale" content="en" key="locale" />
<meta property="og:site_name" content="freesewing.dev" key="site" />
<title>{frontmatter.title}</title>
</Head>
<BaseLayout> <BaseLayout>
<BaseLayoutLeft> <BaseLayoutLeft>
<MainSections /> <MainSections />

View file

@ -1,7 +1,6 @@
// Dependencies // Dependencies
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
// Components // Components
import Head from 'next/head'
import { PageWrapper } from 'shared/components/wrappers/page.mjs' import { PageWrapper } from 'shared/components/wrappers/page.mjs'
import { PageLink } from 'shared/components/link.mjs' import { PageLink } from 'shared/components/link.mjs'
import { Highlight } from 'shared/components/mdx/highlight.mjs' import { Highlight } from 'shared/components/mdx/highlight.mjs'
@ -28,25 +27,11 @@ const Card = ({ bg = 'bg-base-200', textColor = 'text-base-content', title, chil
* or set them manually. * or set them manually.
*/ */
const HomePage = ({ page }) => ( const HomePage = ({ page }) => (
<PageWrapper {...page}> <PageWrapper
<Head> {...page}
<meta property="og:type" content="article" key="type" /> title="FreeSewing.dev"
<meta intro="Documentation and tutorials for FreeSewing developers and contributors"
property="og:description" >
content="Documentation and tutorials for FreeSewing developers and contributors"
key="description"
/>
<meta property="og:article:author" content="Joost De Cock" key="author" />
<meta property="og:image" content="https://freesewing.dev/og/og.png" key="image" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://freesewing.dev/" key="url" />
<meta property="og:locale" content="en_US" key="locale" />
<meta property="og:site_name" content="freesewing.dev" key="site" />
<title>{title}</title>
</Head>
<div className="max-w-7xl m-auto px-0 mt-24 px-4"> <div className="max-w-7xl m-auto px-0 mt-24 px-4">
<FreeSewingIcon className="h-36 w-36 m-auto" /> <FreeSewingIcon className="h-36 w-36 m-auto" />
<h1 className="text-center font-heavy drop-shadow-md px-4"> <h1 className="text-center font-heavy drop-shadow-md px-4">

View file

@ -25,7 +25,7 @@ const SearchPage = ({ page }) => {
) )
return ( return (
<PageWrapper {...page}> <PageWrapper {...page} title="Search" intro="Use the FreeSewing.dev site search">
<BaseLayout> <BaseLayout>
<BaseLayoutLeft> <BaseLayoutLeft>
<MainSections /> <MainSections />

View file

@ -75,7 +75,7 @@ prebuildRunner({
* Only prebuild the Open Graph (og) images in production * Only prebuild the Open Graph (og) images in production
* Will be skipped in development mode to save time * Will be skipped in development mode to save time
*/ */
ogImages: true, //'productionOnly', ogImages: false, // We currently don't prebuild these images
/* /*
* Only prebuild the patron info in production * Only prebuild the patron info in production

View file

@ -3,7 +3,6 @@ import { nsMerge } from 'shared/utils.mjs'
// Hooks // Hooks
import { useContext } from 'react' import { useContext } from 'react'
// Components // Components
import Head from 'next/head'
import { import {
BaseLayout, BaseLayout,
BaseLayoutLeft, BaseLayoutLeft,
@ -22,37 +21,11 @@ import { PrevNext } from 'shared/components/prev-next.mjs'
export const ns = nsMerge(navNs, 'docs', metaNs) export const ns = nsMerge(navNs, 'docs', metaNs)
export const FrontmatterHead = ({ frontmatter, slug, locale }) => (
<Head>
<meta property="og:title" content={frontmatter.title} key="title" />
<meta property="og:type" content="article" key="type" />
<meta property="og:description" content={frontmatter.intro || frontmatter.title} key="type" />
<meta
property="og:article:author"
content={frontmatter.author || frontmatter.maker || 'Joost De Cock'}
key="author"
/>
<meta
property="og:image"
content={`https://canary.backend.freesewing.org/og-img/en/org/${slug}}`}
key="image"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content={`https://freesewing.org/${slug}`} key="url" />
<meta property="og:locale" content={locale || 'en'} key="locale" />
<meta property="og:site_name" content="freesewing.org" key="site" />
<title>{frontmatter.title + '- FreeSewing.org'}</title>
</Head>
)
export const DocsLayout = ({ children = [], frontmatter }) => { export const DocsLayout = ({ children = [], frontmatter }) => {
const { slug, locale } = useContext(NavigationContext) const { slug, locale } = useContext(NavigationContext)
return ( return (
<> <>
<FrontmatterHead {...{ frontmatter, slug, locale }} />
<BaseLayout> <BaseLayout>
<BaseLayoutLeft> <BaseLayoutLeft>
<MainSections /> <MainSections />

View file

@ -5,7 +5,6 @@ import { Lightbox } from 'shared/components/lightbox.mjs'
import { ImageWrapper } from 'shared/components/wrappers/img.mjs' import { ImageWrapper } from 'shared/components/wrappers/img.mjs'
import { TimeAgo, ns as timeagoNs } from 'shared/components/timeago/index.mjs' import { TimeAgo, ns as timeagoNs } from 'shared/components/timeago/index.mjs'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { FrontmatterHead } from './docs.mjs'
import { import {
BaseLayout, BaseLayout,
BaseLayoutLeft, BaseLayoutLeft,
@ -81,7 +80,6 @@ export const PostLayout = ({ mdx, slug, frontmatter, locale, type, dir }) => {
return ( return (
<BaseLayout> <BaseLayout>
<FrontmatterHead {...{ frontmatter, slug, locale }} />
<BaseLayoutLeft> <BaseLayoutLeft>
<MainSections /> <MainSections />
<NavLinks /> <NavLinks />

View file

@ -11,6 +11,7 @@ const BlogPage = ({ dir, page, mdx, frontmatter }) => {
return ( return (
<PageWrapper <PageWrapper
{...page} {...page}
intro={frontmatter.intro}
title={frontmatter.title} title={frontmatter.title}
layout={(props) => ( layout={(props) => (
<PostLayout <PostLayout

View file

@ -58,12 +58,13 @@ const HomePage = ({ page }) => {
}, [account.username]) }, [account.username])
return ( return (
<PageWrapper {...page} layout={BareLayout}> <PageWrapper
{...page}
layout={BareLayout}
title="FreeSewing.org"
intro={t('homepage:freePatterns')}
>
<ForceAccountCheck /> <ForceAccountCheck />
<Head>
<title>FreeSewing.org</title>
</Head>
<div className="text-center w-full m-auto"> <div className="text-center w-full m-auto">
<FreeSewingIcon className="w-36 h-36 mt-0 lg:mt-8 lg:w-56 lg:h-=56 mt-4 m-auto pr-6" /> <FreeSewingIcon className="w-36 h-36 mt-0 lg:mt-8 lg:w-56 lg:h-=56 mt-4 m-auto pr-6" />
<h1 className="font-bold -mt-8 lg:-mt-4" style={{ letterSpacing: '-0.1rem' }}> <h1 className="font-bold -mt-8 lg:-mt-4" style={{ letterSpacing: '-0.1rem' }}>

View file

@ -12,6 +12,7 @@ const ShowcasePage = ({ dir, page, mdx, frontmatter }) => {
<PageWrapper <PageWrapper
{...page} {...page}
title={frontmatter.title} title={frontmatter.title}
intro={frontmatter.intro}
layout={(props) => ( layout={(props) => (
<PostLayout <PostLayout
{...props} {...props}

View file

@ -36,7 +36,7 @@ const ShowcaseIndexPage = ({ page }) => {
: Object.keys(posts[page.locale]) : Object.keys(posts[page.locale])
return ( return (
<PageWrapper {...page} layout={BareLayout}> <PageWrapper {...page} title={t('sections:showcase')} layout={BareLayout}>
<div className="p-4 m-auto"> <div className="p-4 m-auto">
<h1 className="text-center">FreeSewing - {t('sections:showcase')}</h1> <h1 className="text-center">FreeSewing - {t('sections:showcase')}</h1>
<div className="max-w-7xl m-auto"> <div className="max-w-7xl m-auto">

View file

@ -85,7 +85,7 @@ prebuildRunner({
* Only prebuild the Open Graph (og) images in production * Only prebuild the Open Graph (og) images in production
* Will be skipped in development mode to save time * Will be skipped in development mode to save time
*/ */
ogImages: 'productionOnly', ogImages: false, // We do not currently prebuild these
/* /*
* Only prebuild the patron info in production * Only prebuild the patron info in production

View file

@ -1,7 +1,8 @@
// __SDEFILE__ - This file is a dependency for the stand-alone environment // __SDEFILE__ - This file is a dependency for the stand-alone environment
// Dependencies // Dependencies
import React, { useState, useEffect, useContext } from 'react' import React, { useState, useEffect, useContext } from 'react'
import { nsMerge } from 'shared/utils.mjs' import { ogUrl, nsMerge } from 'shared/utils.mjs'
import { siteConfig } from 'site/site.config.mjs'
// Context // Context
import { LoadingStatusContext } from 'shared/context/loading-status-context.mjs' import { LoadingStatusContext } from 'shared/context/loading-status-context.mjs'
// Hooks // Hooks
@ -21,7 +22,14 @@ export const PageWrapper = (props) => {
/* /*
* Deconstruct props * Deconstruct props
*/ */
const { layout = DefaultLayout, footer = true, header = true, children = [], path = [] } = props const {
layout = DefaultLayout,
footer = true,
header = true,
locale = 'en',
children = [],
path = [],
} = props
// Title is typically set in props.t but check props.title too // Title is typically set in props.t but check props.title too
const pageTitle = props.t ? props.t : props.title ? props.title : null const pageTitle = props.t ? props.t : props.title ? props.title : null
@ -60,6 +68,25 @@ export const PageWrapper = (props) => {
{props.intro && ( {props.intro && (
<meta property="og:description" content={props.intro} key="description" /> <meta property="og:description" content={props.intro} key="description" />
)} )}
<meta property="og:title" content={pageTitle} key="title" />
<meta property="og:type" content="article" key="type" />
<meta property="og:article:author" content="Joost De Cock" key="author" />
<meta
property="og:image"
content={`${ogUrl({ site: siteConfig.tld, title: pageTitle, intro: props.intro })}`}
key="image"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="2400" />
<meta property="og:image:height" content="1260" />
<meta
property="og:url"
content={`https://FreeSewing.${siteConfig.tld}/${path.join('/')}`}
key="url"
/>
<meta property="og:locale" content={locale} key="locale" />
<meta property="og:site_name" content={`FreeSewing.${siteConfig.tld}`} key="site" />
<title>{pageTitle}</title>
</Head> </Head>
)} )}
<div <div

View file

@ -1,11 +1,10 @@
import axios from 'axios' import axios from 'axios'
import { freeSewingConfig as config } from '../config/freesewing.config.mjs' import { freeSewingConfig as config } from '../config/freesewing.config.mjs'
import { slugToOgImg } from '../utils.mjs'
import get from 'lodash.get' import get from 'lodash.get'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
const slugToImg = (slug, language) => `${language}_${slug.split('/').join('_')}.png`
export const generateImage = async ({ title, intro, site, slug, language }) => { export const generateImage = async ({ title, intro, site, slug, language }) => {
let result let result
try { try {
@ -14,7 +13,7 @@ export const generateImage = async ({ title, intro, site, slug, language }) => {
{ title, intro, site, type: 'wide' }, { title, intro, site, type: 'wide' },
{ responseType: 'arraybuffer' } { responseType: 'arraybuffer' }
) )
const file = path.resolve('..', site, 'public', 'img', 'og', slugToImg(slug, language)) const file = path.resolve('..', site, 'public', 'img', 'og', slugToOgImg(slug, language))
await fs.promises.writeFile(file, result.data) await fs.promises.writeFile(file, result.data)
} catch (err) { } catch (err) {
console.log(err) console.log(err)

View file

@ -7,6 +7,7 @@ import orderBy from 'lodash.orderby'
import unset from 'lodash.unset' import unset from 'lodash.unset'
import { cloudflareConfig } from './config/cloudflare.mjs' import { cloudflareConfig } from './config/cloudflare.mjs'
import { mergeOptions } from '@freesewing/core' import { mergeOptions } from '@freesewing/core'
import { freeSewingConfig as config } from './config/freesewing.config.mjs'
const slugifyConfig = { const slugifyConfig = {
replacement: '-', // replace spaces with replacement character, defaults to `-` replacement: '-', // replace spaces with replacement character, defaults to `-`
@ -491,3 +492,8 @@ export const workbenchHash = ({ settings = {}, view = 'draft' }) =>
export const getSearchParam = (name = 'id') => export const getSearchParam = (name = 'id') =>
typeof window === 'undefined' ? undefined : new URLSearchParams(window.location.search).get(name) typeof window === 'undefined' ? undefined : new URLSearchParams(window.location.search).get(name)
export const slugToOgImg = (slug, language) => `${language}_${slug.split('/').join('_')}.png`
export const ogUrl = ({ site = false, title, intro }) =>
`${config.backend}/img/${encodeURIComponent(JSON.stringify({ site, title, intro }))}`