Skip to content

Commit

Permalink
feat: add redis db to persist images; bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
transitive-bullshit committed Mar 23, 2022
1 parent 5417bb9 commit 40972ec
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 76 deletions.
11 changes: 9 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,12 @@
# Optional (for fathom analytics)
#NEXT_PUBLIC_FATHOM_ID=

# Optional (for rendering tweets efficiently)
TWITTER_ACCESS_TOKEN=
# Optional (for rendering tweets more efficiently)
#TWITTER_ACCESS_TOKEN=

# Optional (for persisting preview images to redis)
# NOTE: if you want to enable redis, only REDIS_HOST and REDIS_PASSWORD are required
#REDIS_HOST=
#REDIS_PASSWORD=
#REDIS_USER='default'
#REDIS_NAMESPACE='preview-images'
6 changes: 3 additions & 3 deletions components/NotionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { NotionRenderer, Code, Collection, CollectionRow } from 'react-notion-x'
// utils
import { getBlockTitle } from 'notion-utils'
import { mapPageUrl, getCanonicalPageUrl } from 'lib/map-page-url'
import { mapNotionImageUrl } from 'lib/map-image-url'
import { mapImageUrl } from 'lib/map-image-url'
import { getPageDescription } from 'lib/get-page-description'
import { getPageTweet } from 'lib/get-page-tweet'
import { searchNotion } from 'lib/search-notion'
Expand Down Expand Up @@ -129,7 +129,7 @@ export const NotionPage: React.FC<types.PageProps> = ({
const showTableOfContents = !!isBlogPost
const minTableOfContentsItems = 3

const socialImage = mapNotionImageUrl(
const socialImage = mapImageUrl(
(block as PageBlock).format?.page_cover || config.defaultPageCover,
block
)
Expand Down Expand Up @@ -282,7 +282,7 @@ export const NotionPage: React.FC<types.PageProps> = ({
defaultPageCover={config.defaultPageCover}
defaultPageCoverPosition={config.defaultPageCoverPosition}
mapPageUrl={siteMapPageUrl}
mapImageUrl={mapNotionImageUrl}
mapImageUrl={mapImageUrl}
searchNotion={searchNotion}
pageAside={pageAside}
footer={
Expand Down
20 changes: 17 additions & 3 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ export const defaultPageCoverPosition: number = getSiteConfig(
0.5
)

// Optional image CDN host to proxy all image requests through
export const imageCDNHost: string | null = getSiteConfig('imageCDNHost', null)

// Optional whether or not to enable support for LQIP preview images
export const isPreviewImageSupportEnabled: boolean = getSiteConfig(
'isPreviewImageSupportEnabled',
Expand All @@ -96,6 +93,23 @@ export const includeNotionIdInUrls: boolean = getSiteConfig(

// ----------------------------------------------------------------------------

// Optional redis instance for persisting preview images
// (if you want to enable redis, only REDIS_HOST and REDIS_PASSWORD are required)
// we recommend that you store these in a local `.env` file
export const redisHost: string | null = getEnv('REDIS_HOST', null)
export const redisPassword: string | null = getEnv('REDIS_PASSWORD', null)
export const redisUser: string = getEnv('REDIS_USER', 'default')
export const redisUrl = getEnv(
'REDIS_URL',
`redis://${redisUser}:${redisPassword}@${redisHost}`
)
export const redisNamespace: string | null = getEnv(
'REDIS_NAMESPACE',
'preview-images'
)

// ----------------------------------------------------------------------------

export const isServer = typeof window === 'undefined'

export const port = getEnv('PORT', '3000')
Expand Down
14 changes: 14 additions & 0 deletions lib/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Keyv from 'keyv'

import {
isPreviewImageSupportEnabled,
redisUrl,
redisNamespace
} from './config'

let db: Keyv
if (isPreviewImageSupportEnabled) {
db = new Keyv(redisUrl, { namespace: redisNamespace || undefined })
}

export { db }
56 changes: 2 additions & 54 deletions lib/map-image-url.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,3 @@
import { Block } from 'notion-types'
import { imageCDNHost } from './config'
import { defaultMapImageUrl } from 'react-notion-x'

export const mapNotionImageUrl = (url: string, block: Block) => {
if (!url) {
return null
}

if (url.startsWith('data:')) {
return url
}

if (imageCDNHost && url.startsWith(imageCDNHost)) {
return url
}

// const origUrl = url

if (url.startsWith('/images')) {
url = `https://www.notion.so${url}`
}

// more recent versions of notion don't proxy unsplash images
if (!url.startsWith('https://images.unsplash.com')) {
url = `https://www.notion.so${
url.startsWith('/image') ? url : `/image/${encodeURIComponent(url)}`
}`

const notionImageUrlV2 = new URL(url)
let table = block.parent_table === 'space' ? 'block' : block.parent_table
if (table === 'collection') {
table = 'block'
}
notionImageUrlV2.searchParams.set('table', table)
notionImageUrlV2.searchParams.set('id', block.id)
notionImageUrlV2.searchParams.set('cache', 'v2')

url = notionImageUrlV2.toString()
}

// console.log({ url, origUrl })
return mapImageUrl(url)
}

export const mapImageUrl = (imageUrl: string) => {
if (imageUrl.startsWith('data:')) {
return imageUrl
}

if (imageCDNHost) {
return `${imageCDNHost}/${encodeURIComponent(imageUrl)}`
} else {
return imageUrl
}
}
export const mapImageUrl = defaultMapImageUrl
19 changes: 15 additions & 4 deletions lib/preview-images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import got from 'got'
import lqip from 'lqip-modern'
import pMap from 'p-map'
import pMemoize from 'p-memoize'

import { ExtendedRecordMap, PreviewImage, PreviewImageMap } from 'notion-types'
import { mapNotionImageUrl } from './map-image-url'

import { db } from './db'
import { mapImageUrl } from './map-image-url'

// NOTE: this is just an example of how to pre-compute preview images.
// Depending on how many images you're working with, this can potentially be
Expand Down Expand Up @@ -46,7 +47,7 @@ export async function getPreviewImageMap(
return null
})
.filter(Boolean)
.map(({ block, url }) => mapNotionImageUrl(url, block))
.map(({ block, url }) => mapImageUrl(url, block))
.filter(Boolean)

const urls = Array.from(new Set(imageUrls))
Expand All @@ -60,16 +61,26 @@ export async function getPreviewImageMap(
}

async function createPreviewImage(url: string): Promise<PreviewImage | null> {
const cacheKey = url

try {
const cachedPreviewImage = await db.get(cacheKey)
if (cachedPreviewImage) {
return cachedPreviewImage
}

const { body } = await got(url, { responseType: 'buffer' })
const result = await lqip(body)
console.log('lqip', result.metadata)

return {
const previewImage = {
originalWidth: result.metadata.originalWidth,
originalHeight: result.metadata.originalHeight,
dataURIBase64: result.metadata.dataURIBase64
}

await db.set(cacheKey, previewImage)
return previewImage
} catch (err) {
console.warn('error creating preview image', url, err)
return null
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
"test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
},
"dependencies": {
"@keyv/redis": "^2.2.3",
"classnames": "^2.3.1",
"date-fns": "^2.25.0",
"fathom-client": "^3.0.0",
"got": "^11.8.2",
"isomorphic-unfetch": "^3.1.0",
"keyv": "^4.1.1",
"lqip-modern": "^1.2.0",
"next": "^12.1.0",
"node-fetch": "^2.6.1",
Expand All @@ -42,7 +44,7 @@
"react-body-classname": "^1.3.1",
"react-dom": "^17.0.2",
"react-icons": "^4.3.1",
"react-notion-x": "^4.19.2",
"react-notion-x": "^4.19.6",
"react-static-tweets": "^0.7.1",
"react-use": "^17.3.2",
"static-tweets": "^0.7.1",
Expand Down
5 changes: 5 additions & 0 deletions site.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ module.exports = {
defaultPageCover: null,
defaultPageCoverPosition: 0.5,

// whether or not to enable support for LQIP preview images (optional)
// NOTE: this requires you to set up an external key-value store and add the
// environment variables specified in .env.example
isPreviewImageSupportEnabled: true,

// map of notion page IDs to URL paths (optional)
// any pages defined here will override their default URL paths
// example:
Expand Down
14 changes: 10 additions & 4 deletions styles/notion.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@

.notion-collection-card-cover {
border-radius: 16px;
overflow: visible;
box-shadow: 2px 2px 8px 4px rgba(15, 15, 15, 0.1);
}

Expand Down Expand Up @@ -179,14 +178,21 @@
padding: 0;
}

.notion-page-cover {
max-width: 1200px;
.notion-page-cover-wrapper,
.notion-page-cover-wrapper span,
.notion-page-cover-wrapper img {
max-width: 1200px !important;
border-radius: 24px;
}

.notion-page-cover-wrapper {
box-shadow: 2px 2px 8px 4px rgba(15, 15, 15, 0.1);
}

@media only screen and (max-width: 1200px) {
.notion-page-cover {
.notion-page-cover-wrapper,
.notion-page-cover-wrapper span,
.notion-page-cover-wrapper img {
border-radius: 0;
}
}
Expand Down
Loading

0 comments on commit 40972ec

Please sign in to comment.