feat: cms integration
This commit is contained in:
parent
ff77bdaf36
commit
d23243b177
1050 changed files with 1032 additions and 27229 deletions
|
@ -1,51 +0,0 @@
|
|||
import fs from 'fs'
|
||||
import fetch from 'node-fetch'
|
||||
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: new URL('../../.env.local', import.meta.url).pathname });
|
||||
|
||||
const s3Client = new S3Client({
|
||||
credentials: {
|
||||
accessKeyId: process.env.B2_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.B2_SECRET_ACCESS_KEY
|
||||
},
|
||||
endpoint: {
|
||||
url: 'https://s3.us-west-001.backblazeb2.com',
|
||||
},
|
||||
region: 'us-west-1',
|
||||
});
|
||||
const booksData = fs.readFileSync('../../src/_data/json/read.json', 'utf8');
|
||||
const books = JSON.parse(booksData);
|
||||
|
||||
async function processBooks() {
|
||||
for (const book of books) {
|
||||
if (book.thumbnail.startsWith('https://coryd.dev/media/books/')) continue
|
||||
const cleanedThumbnailURL = book.thumbnail.replace('&edge=curl', '');
|
||||
const fileName = `${book.isbn}-${book.title.replace(/\s+/g, '-').replace(/[^\w\-]+/g, '').replace(/\-{2,}/g, '-').replace(/^-+|-+$/g, '') .toLowerCase().replace(/\s+/g, '-')}.jpg`;
|
||||
const fileKey = `books/${fileName}`
|
||||
|
||||
try {
|
||||
const response = await fetch(cleanedThumbnailURL);
|
||||
const buffer = await response.buffer();
|
||||
const putObjectParams = {
|
||||
Bucket: process.env.B2_BUCKET_NAME,
|
||||
Key: fileKey,
|
||||
Body: buffer,
|
||||
ContentType: 'image/jpeg'
|
||||
};
|
||||
|
||||
await s3Client.send(new PutObjectCommand(putObjectParams));
|
||||
|
||||
book.thumbnail = `https://coryd.dev/media/books/${fileName}`;
|
||||
|
||||
console.log(`Uploaded and updated ${book.title}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to process ${book.title}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('../../src/_data/json/read.json', JSON.stringify(books, null, 2));
|
||||
}
|
||||
|
||||
processBooks();
|
|
@ -1,73 +0,0 @@
|
|||
import { promises as fs } from 'fs'
|
||||
import path from 'path'
|
||||
import sharp from 'sharp'
|
||||
import matter from 'gray-matter'
|
||||
import slugify from 'slugify'
|
||||
import { Liquid } from 'liquidjs'
|
||||
import { DateTime } from 'luxon'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const FILE_NAME = fileURLToPath(import.meta.url)
|
||||
const DIR_NAME = path.dirname(FILE_NAME)
|
||||
|
||||
const baseDir = path.join(DIR_NAME, '../../src/posts')
|
||||
const outputDir = path.join(DIR_NAME, '../../src/assets/img/ogi/')
|
||||
const engine = new Liquid({ extname: '.liquid' })
|
||||
|
||||
engine.registerFilter('date', (isoDateString, formatString = 'MMMM d, yyyy') => {
|
||||
const date = DateTime.fromISO(isoDateString)
|
||||
return date.isValid ? date.toFormat(formatString) : isoDateString
|
||||
})
|
||||
|
||||
engine.registerFilter('splitLines', (input, maxCharLength) => {
|
||||
return input.split(' ').reduce((acc, cur) => {
|
||||
if (!acc.length || acc[acc.length - 1].length + cur.length + 1 > maxCharLength) {
|
||||
acc.push(cur)
|
||||
} else {
|
||||
acc[acc.length - 1] += ' ' + cur
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
})
|
||||
|
||||
engine.registerFilter('slugify', (input) => slugify(input, { lower: true, strict: true, remove: /[*+~.()'"!:@]/g }))
|
||||
|
||||
const svgToPNG = async (filePath) => {
|
||||
try {
|
||||
const fileContent = await fs.readFile(filePath, 'utf8')
|
||||
const { data } = matter(fileContent)
|
||||
const svgTemplatePath = path.resolve(DIR_NAME, 'index.liquid')
|
||||
const templateContent = await fs.readFile(svgTemplatePath, 'utf8')
|
||||
const svgContent = await engine.parseAndRender(templateContent, { preview: { data: data, date: data.date }})
|
||||
const outputFile = path.join(outputDir, `${engine.filters.slugify(data.title)}-preview.png`)
|
||||
|
||||
await fs.mkdir(outputDir, { recursive: true })
|
||||
await sharp(Buffer.from(svgContent)).png().toFile(outputFile)
|
||||
} catch (error) {
|
||||
console.error('Error processing file:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const processPostDirectories = async (baseDir) => {
|
||||
try {
|
||||
const yearDirs = await fs.readdir(baseDir, { withFileTypes: true })
|
||||
for (const dirent of yearDirs) {
|
||||
if (dirent.isDirectory()) {
|
||||
const yearPath = path.join(baseDir, dirent.name)
|
||||
const markdownFiles = await fs.readdir(yearPath, { withFileTypes: true })
|
||||
for (const file of markdownFiles) {
|
||||
if (file.isFile() && file.name.endsWith('.md')) {
|
||||
const filePath = path.join(yearPath, file.name)
|
||||
await svgToPNG(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to process directories:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const generateOgImages = async () => await processPostDirectories(baseDir)
|
||||
|
||||
generateOgImages()
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
{% assign titleInLines = preview.data.title | splitLines: 40 %}
|
||||
{% assign numberOfLines = titleInLines.length %}
|
||||
{% if numberOfLines == 1 %}
|
||||
{% assign verticalStartingPoint = 340 %}
|
||||
{% elsif numberOfLines == 2 %}
|
||||
{% assign verticalStartingPoint = 290 %}
|
||||
{% elsif numberOfLines == 3 %}
|
||||
{% assign verticalStartingPoint = 250 %}
|
||||
{% elsif numberOfLines == 4 %}
|
||||
{% assign verticalStartingPoint = 210 %}
|
||||
{% elsif numberOfLines == 5 %}
|
||||
{% assign verticalStartingPoint = 170 %}
|
||||
{% endif %}
|
||||
|
||||
<svg id="visual" viewBox="0 0 1200 630" width="1200" height="630" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><rect x="0" y="0" width="1200" height="630" fill="#000"></rect></svg>
|
||||
|
||||
<!-- date -->
|
||||
<text
|
||||
font-family="MonoLisa, Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, ui-monospace, monospace"
|
||||
font-size="24"
|
||||
font-weight="800"
|
||||
fill="#fff"
|
||||
>
|
||||
<tspan x="80" y="{{ verticalStartingPoint | minus: 120 }}">
|
||||
{{ preview.data.date | date: "MMMM d, yyyy" }}
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
<!-- title -->
|
||||
<text
|
||||
id="text"
|
||||
font-family="MonoLisa, Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, ui-monospace, monospace"
|
||||
font-size="40"
|
||||
font-weight="800"
|
||||
fill="#fff"
|
||||
>
|
||||
{% for line in titleInLines %}
|
||||
{% capture spacing %}{{ forloop.index0 | times: 50 }}{% endcapture %}
|
||||
<tspan x="80" y="{{ verticalStartingPoint | plus: spacing }}">
|
||||
{{ line }}
|
||||
</tspan>
|
||||
{% endfor %}
|
||||
</text>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.7 KiB |
Reference in a new issue