174 lines
4.7 KiB
JavaScript
174 lines
4.7 KiB
JavaScript
/**
|
|
* Load all posts from my blog database and write them to Markdown file
|
|
*
|
|
* usage:
|
|
*
|
|
* node db_to_md.js db_username db_password
|
|
*/
|
|
|
|
import { dirname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import * as fs from 'node:fs'
|
|
import mysql from 'mysql2/promise'
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = dirname(__filename)
|
|
|
|
class BlogToMarkdown {
|
|
databaseName = 'godruoyi'
|
|
|
|
host = '127.0.0.1'
|
|
|
|
port = 13306
|
|
|
|
constructor() {
|
|
const { username, password } = this.parseProcessArgs()
|
|
|
|
this.username = username
|
|
this.password = password
|
|
}
|
|
|
|
async run() {
|
|
this.connection = await this.connectMySQL(this.username, this.password)
|
|
|
|
const categoryMap = await this.fetchCategories()
|
|
const posts = await this.fetchPosts()
|
|
|
|
const formatedPosts = this.formatPosts(categoryMap, posts)
|
|
|
|
for (const p of formatedPosts) {
|
|
// this.writePostToMarkdown(p)
|
|
await this.savePostBanner(p)
|
|
}
|
|
|
|
console.log('All done')
|
|
}
|
|
|
|
async savePostBanner(post) {
|
|
const filePath = this.postBannerFilePath(post)
|
|
console.log('savePostBanner', filePath)
|
|
|
|
const response = await fetch(post.banner)
|
|
|
|
if (!response.ok) {
|
|
throw new Error('download image failed')
|
|
}
|
|
|
|
const arrayBuffer = await response.arrayBuffer()
|
|
fs.writeFileSync(filePath, Buffer.from(arrayBuffer))
|
|
}
|
|
|
|
async fetchPosts() {
|
|
const query = 'SELECT * FROM posts ORDER BY id ASC'
|
|
|
|
const [rows] = await this.connection.execute(query)
|
|
|
|
return rows
|
|
}
|
|
|
|
async fetchCategories() {
|
|
const query = 'SELECT * FROM categories ORDER BY id ASC'
|
|
|
|
const [rows] = await this.connection.execute(query)
|
|
|
|
return rows.reduce((map, obj) => map.set(obj.id, obj), new Map())
|
|
}
|
|
|
|
writePostToMarkdown(post) {
|
|
console.log(`saving post to markdown: ${post.title}`)
|
|
|
|
const file = this.postToMarkdownFilePath(post)
|
|
const content = this.convertPostToMarkdown(post)
|
|
|
|
fs.writeFileSync(file, content, 'utf-8')
|
|
}
|
|
|
|
postToMarkdownFilePath(p) {
|
|
return join(__dirname, `./../src/content/posts/${p.slug}.md`)
|
|
}
|
|
|
|
postBannerFilePath(p) {
|
|
const name = new URL(p.banner).pathname.split('/').pop()
|
|
return join(__dirname, `./../src/images/posts/${name}`)
|
|
}
|
|
|
|
convertPostToMarkdown(post) {
|
|
return `---
|
|
title: "${post.title}"
|
|
description: "${post.description}"
|
|
pubDate: "${this.formatDate(post.pubDate)}"
|
|
category: "${post.category.slug}"
|
|
cardImage: "${post.banner}"
|
|
tags: ["${post.category.slug}"]
|
|
oldViewCount: ${post.viewCount}
|
|
oldKeywords: ["${post.keyword}"]
|
|
---
|
|
|
|
${post.content}
|
|
`
|
|
}
|
|
|
|
formatPosts(categoryMap, posts) {
|
|
return posts.map((p) => {
|
|
return {
|
|
title: p.title,
|
|
description: p.excerpt,
|
|
pubDate: p.created_at,
|
|
banner: p.banner,
|
|
category_id: p.category_id,
|
|
category: categoryMap.get(p.category_id),
|
|
slug: p.slug,
|
|
viewCount: p.view_count,
|
|
content: p.content,
|
|
id: p.id,
|
|
keyword: p.keyword,
|
|
}
|
|
})
|
|
}
|
|
|
|
async connectMySQL(username, password) {
|
|
return mysql.createConnection({
|
|
host: this.host,
|
|
port: this.port,
|
|
user: username,
|
|
password,
|
|
database: this.databaseName,
|
|
})
|
|
}
|
|
|
|
closeConnection() {
|
|
if (this.connection) {
|
|
this.connection.close()
|
|
}
|
|
}
|
|
|
|
parseProcessArgs() {
|
|
const username = process.argv[2]
|
|
const password = process.argv[3]
|
|
|
|
if (!username || !password) {
|
|
throw new Error('Missing username or password')
|
|
}
|
|
|
|
return { username, password }
|
|
}
|
|
|
|
formatDate(date) {
|
|
const newDate = new Date(date.getTime() + 8 * 60 * 60 * 1000) // Date object uses milliseconds
|
|
|
|
const yyyy = newDate.getUTCFullYear()
|
|
const mm = String(newDate.getUTCMonth() + 1).padStart(2, '0') // Months are zero based
|
|
const dd = String(newDate.getUTCDate()).padStart(2, '0')
|
|
|
|
const hh = String(newDate.getUTCHours()).padStart(2, '0')
|
|
const mi = String(newDate.getUTCMinutes()).padStart(2, '0')
|
|
const ss = String(newDate.getUTCSeconds()).padStart(2, '0')
|
|
|
|
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
|
|
}
|
|
}
|
|
|
|
const x = new BlogToMarkdown()
|
|
await x.run()
|
|
await process.exit(0)
|