很久以前寫的。不想白寫,這邊記錄一下,配合GPT簡要回憶一下代碼思路。
後臺系統
demo_initBlog.js
首先是博客的後臺系統,通過node demo_initBlog.js進行初始化。
const fs = require("fs").promises
const path = require('path')
const jsonfile = require('jsonfile')
async function initBlog() {
try {
// 1 創建文件夾,已經創建就不創建
console.log('=> 創建posts文件夾')
let createDir = await fs.mkdir('./source/_posts/', { recursive: true })
console.log(`=> created ${createDir}`);
// 無需創建 => created undefined
// 完全新建 => created D:\2022_8_3_mycnblogs\my_cnblogs\manuscript\source
// 僅_posts不存在 => created D:\2022_8_3_mycnblogs\my_cnblogs\manuscript\source\_posts
// 2 創建./source/post_infos.json用來保存和管理文章信息
let file_name = 'post_infos.json'
let file_path = path.join(__dirname, `source/${file_name}`)
try {
console.log('=> 嘗試讀取post_infos.json')
let content = await jsonfile.readFile(file_path)
console.log('=> 讀取post_infos.json成功!內容如下')
console.log(content)
} catch (err) {
// 沒創建才創建,已創建不創建
console.log(err)
console.log('=> post_infos.json未創建!即將創建')
let obj = {
categories: [],
posts: [],
}
jsonfile.writeFile(file_path, obj, { spaces: 2 })
.then(() => {
console.log('=> 回調: Write complete')
})
.catch(err => {
console.log('=> 創建post_infos.json異常')
console.log(err)
})
}
console.log('=> initBlog.js程序已到末尾, 如有需要刪除重新創建')
} catch (err) {
console.error(err.message);
}
}
initBlog()
親愛的朋友,這段代碼是用來初始化一個博客的。它做了以下幾件事情:
- 首先,它會創建一個名爲
posts
的文件夾,用於存放博客文章。如果文件夾已經存在,它就不會再創建,太聰明瞭吧! - 然後,它嘗試讀取一個名爲
post_infos.json
的文件。這個文件用於存儲博客文章的信息,比如分類和文章列表。 - 如果發現
post_infos.json
文件還不存在,它會馬上創建一個,並初始化它的內容,使其包含空的分類和文章列表。
簡單來說,這段代碼會確保你的博客文件夾結構和必要文件都已經創建好,以便於你開始寫博客。希望這個解釋對你有幫助!
demo_server.js
下面這個應該是一個後臺接口+後臺的業務邏輯代碼。
const express = require('express');
const app = express();
const fs = require("fs").promises
const path = require('path')
const bodyParser = require('body-parser')
const { customAlphabet } = require('nanoid')
// 字母表去掉一個 `-`,這樣纔可以在網址裏面用
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const nanoid = customAlphabet(alphabet, 8)
const cors = require('cors');
const jsonfile = require('jsonfile')
// express本身不能處理Post來的表單數據,通過下面的設置,
// 就可以在req.body中拿到數據
app.use(bodyParser.urlencoded({extended : false}))
app.use(bodyParser.json())
// 解決跨域問題
app.use(cors())
// PayloadTooLargeError: request entity too large
app.use(express.json({ limit: '10000kb'}))
app.use(express.urlencoded({ limit: '10000kb'}))
function utf8readFile(path) {
let opions = {
encoding: 'utf-8',
}
return fs.readFile(path, opions)
}
function cmd_log(str) {
console.log(`[${new Date().toLocaleString()}] ${str}`)
}
const SOURCE_PATH = path.join(__dirname, 'source')
const JSON_NAME = 'post_infos.json'
const JSON_PATH = path.join(SOURCE_PATH, JSON_NAME)
function postIdDirPath(postId) {
return path.join(SOURCE_PATH, '_posts', postId)
}
function htmlFilePath(postId) {
return path.join(postIdDirPath(postId), 'index.html')
}
function readHtmlFile(postId) {
// 讀取對應HTML文件內容
return utf8readFile(htmlFilePath(postId))
}
function writeHtmlFile(postId, content) {
// 將內容直接寫到對應的HTML文件中
return fs.writeFile(htmlFilePath(postId), content)
}
async function updateJSONFile_posts(dealWithObj) {
// 更新.json文件,傳入一個修改json文件中對象的函數
// 1 讀
let obj = await jsonfile.readFile(JSON_PATH)
// 2 改
obj = dealWithObj(obj)
// 3 寫
await jsonfile.writeFile(JSON_PATH, obj, { spaces: 2 })
return 1 // 隨便什麼值無所謂
}
async function updateJSONFile_categories(dealWithObj) {
// 更新.json文件,傳入一個修改json文件中對象的函數
const category_path = path.join(SOURCE_PATH, 'category_infos.json')
// 1 讀出
let obj = await jsonfile.readFile(category_path)
// 2 修改
obj = dealWithObj(obj)
// 3 寫回
await jsonfile.writeFile(category_path, obj, { spaces: 2 })
return 1 // 隨便什麼值無所謂
}
// 隨筆列表
app.get('/posts', async function (req, res) {
let { posts } = await jsonfile.readFile(JSON_PATH)
res.send(posts)
})
// 某個隨筆信息(info + html)
app.get('/posts/:postId', async function(req, res) {
const POST_ID = req.params.postId
try {
// 1 從.json裏讀取對應的POST信息
let { posts } = await jsonfile.readFile(JSON_PATH)
let post = posts.find(x => x.id === POST_ID)
// 2 讀POST對應的HTML文件
let content = await readHtmlFile(POST_ID)
post['content'] = content
res.send(post)
}catch(err) {
console.log(err)
res.status(500).end()
}
})
// 新隨筆
app.post('/posts', async function (req, res) {
let { title, content, category } = req.body
try {
title = title.trim()
if (title === '') throw new Error('標題不能爲空')
// POST模板
let post = {
id: nanoid(8),
title: title,
createTime: new Date(),
category: category,
state: '未發佈',
pubDate: new Date(),
}
// 1 將POST信息寫入.json
await updateJSONFile_posts((obj) => {
obj.posts.unshift(post)
return obj
})
cmd_log("成功寫入JSON_PATH: " + title)
// 2 創建對應文件夾和HTML文件
let createDir = await fs.mkdir(postIdDirPath(post.id), { recursive: true })
cmd_log(`created ${createDir} (文件夾)`);
await writeHtmlFile(post.id, content)
cmd_log(`.html文件已經創建`);
res.send(post)
}catch(err) {
console.log(err)
res.status(500).send('創建異常')
}
})
// 更新隨筆
app.post('/posts/:postId', async function (req, res) {
const POST_ID = req.params.postId
let { title, content, category, state, pubDate } = req.body
try {
// 將內容覆蓋到對應的HTML文件
await writeHtmlFile(POST_ID, content)
// 更新.json中的相關信息
let post
await updateJSONFile_posts((obj) => {
let index = obj.posts.findIndex(x => x.id === POST_ID)
obj.posts[index].title = title
obj.posts[index].category = category
obj.posts[index].state = state
obj.posts[index].pubDate = pubDate
post = obj.posts[index]
return obj
})
res.send(post)
}catch(err) {
console.log(err)
res.status(500).end()
}
})
// 刪除隨筆
app.delete('/posts/:postId', async function(req, res) {
const POST_ID = req.params.postId
try {
// 1 從.json裏刪除
let obj = await jsonfile.readFile(JSON_PATH)
obj.posts = obj.posts.filter(post => post.id !== POST_ID)
await jsonfile.writeFile(JSON_PATH, obj, { spaces: 2 })
cmd_log(`修改${JSON_NAME},已移除POST: ` + POST_ID)
// 2 刪除文件
await fs.rm(postIdDirPath(POST_ID), { recursive: true, force: true })
cmd_log('相關文件刪除成功')
res.end()
}catch(err) {
console.log(err)
res.status(500).send('刪除異常')
}
})
// 新類別
app.post('/categories', async function (req, res) {
let { name } = req.body
try {
name = name.trim()
if (name === '') throw new Error('類別名不能爲空')
// category
let category = {
id: nanoid(8),
name: name,
visible: true
}
cmd_log(`增加類別${category}`)
// 1 將category信息更新到.json
await updateJSONFile_categories(obj => {
obj.push(category)
return obj
})
res.send(category)
}catch(err) {
console.log(err)
res.status(500).send('創建異常')
}
})
// 類別列表
app.get('/categories', async function (req, res) {
const category_path = path.join(SOURCE_PATH, 'category_infos.json')
res.send(await jsonfile.readFile(category_path))
})
// 刪除類別
app.delete('/categories/:id', async function (req, res) {
try {
const id = req.params.id
await updateJSONFile_categories(obj => {
obj = obj.filter(x => x.id !== id)
return obj
})
res.end()
}catch(err) {
console.log(err)
res.status(500).send('刪除異常')
}
})
const PORT = 8081
let server = app.listen(PORT, function () {
console.log(`Example app listening on port ${PORT}`)
})
這個代碼主要實現了一個簡易的博客文章管理系統,具體功能如下:
-
導入必要的庫:使用了 Express.js 作爲 Web 框架,處理 HTTP 請求,同時還導入了其他輔助庫,如 fs (文件系統)、path (處理文件路徑)、body-parser (解析請求體數據)、nanoid (生成隨機 ID)、cors (處理跨域請求) 以及 jsonfile (讀寫 JSON 文件)。
-
初始化 Express.js 應用:設置 body-parser 解析請求體數據,並使用 CORS 解決跨域問題。同時設置了 JSON 和 URL 編碼的請求體大小限制。
-
定義一些實用函數:
- utf8readFile: 以 utf-8 編碼讀取指定路徑的文件。
- cmd_log: 在控制檯輸出帶有時間戳的日誌信息。
- postIdDirPath: 根據文章 ID 生成對應的文章文件夾路徑。
- htmlFilePath: 根據文章 ID 生成對應的 HTML 文件路徑。
- readHtmlFile: 讀取指定文章 ID 的 HTML 文件內容。
- writeHtmlFile: 將內容寫入指定文章 ID 的 HTML 文件。
- updateJSONFile_posts: 更新包含文章信息的 JSON 文件,傳入一個處理對象的函數。
- updateJSONFile_categories: 更新包含類別信息的 JSON 文件,傳入一個處理對象的函數。
-
實現了以下幾個路由處理函數:
- 隨筆列表:處理 GET 請求,返回所有文章信息。
- 某個隨筆信息:處理 GET 請求,根據文章 ID 獲取對應文章的信息和 HTML 內容。
- 新隨筆:處理 POST 請求,創建新文章,將文章信息添加到 JSON 文件,並創建對應的 HTML 文件。
- 更新隨筆:處理 POST 請求,根據文章 ID 更新文章信息,包括更新 JSON 文件和 HTML 文件。
- 刪除隨筆:處理 DELETE 請求,根據文章 ID 刪除文章,包括移除 JSON 文件中的信息和刪除 HTML 文件。
- 新類別:處理 POST 請求,創建新的文章類別,將類別信息添加到 JSON 文件。
- 類別列表:處理 GET 請求,返回所有類別信息。
- 刪除類別:處理 DELETE 請求,根據類別 ID 刪除類別,更新 JSON 文件。
-
啓動 Express.js 應用並監聽特定端口,等待客戶端發起請求。
這個博客文章管理系統的代碼包括基礎配置、工具函數和路由處理邏輯。用戶可以通過不同的 HTTP 請求來創建、更新、刪除博客文章和類別。