initiation librodrome
This commit is contained in:
30
server/api/admin/chapters/[slug].get.ts
Normal file
30
server/api/admin/chapters/[slug].get.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const slug = getRouterParam(event, 'slug')
|
||||
|
||||
if (!slug || !/^[a-z0-9-]+$/.test(slug)) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Invalid slug' })
|
||||
}
|
||||
|
||||
const filePath = join(process.cwd(), 'content', 'book', `${slug}.md`)
|
||||
|
||||
try {
|
||||
const raw = await readFile(filePath, 'utf-8')
|
||||
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n?/)
|
||||
|
||||
let frontmatter = ''
|
||||
let body = raw
|
||||
|
||||
if (fmMatch) {
|
||||
frontmatter = fmMatch[1]
|
||||
body = raw.slice(fmMatch[0].length)
|
||||
}
|
||||
|
||||
return { slug, frontmatter, body }
|
||||
}
|
||||
catch {
|
||||
throw createError({ statusCode: 404, statusMessage: `Chapter "${slug}" not found` })
|
||||
}
|
||||
})
|
||||
23
server/api/admin/chapters/[slug].put.ts
Normal file
23
server/api/admin/chapters/[slug].put.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const slug = getRouterParam(event, 'slug')
|
||||
|
||||
if (!slug || !/^[a-z0-9-]+$/.test(slug)) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Invalid slug' })
|
||||
}
|
||||
|
||||
const body = await readBody<{ frontmatter: string; body: string }>(event)
|
||||
|
||||
if (!body?.frontmatter && !body?.body) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Missing content' })
|
||||
}
|
||||
|
||||
const filePath = join(process.cwd(), 'content', 'book', `${slug}.md`)
|
||||
const content = `---\n${body.frontmatter.trim()}\n---\n${body.body}`
|
||||
|
||||
await writeFile(filePath, content, 'utf-8')
|
||||
|
||||
return { ok: true }
|
||||
})
|
||||
47
server/api/admin/chapters/index.get.ts
Normal file
47
server/api/admin/chapters/index.get.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { readdir, readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
const bookDir = join(process.cwd(), 'content', 'book')
|
||||
const files = await readdir(bookDir)
|
||||
const mdFiles = files.filter(f => f.endsWith('.md')).sort()
|
||||
|
||||
const chapters = await Promise.all(
|
||||
mdFiles.map(async (file) => {
|
||||
const raw = await readFile(join(bookDir, file), 'utf-8')
|
||||
const slug = file.replace(/\.md$/, '')
|
||||
const frontmatter = parseFrontmatter(raw)
|
||||
return { slug, ...frontmatter }
|
||||
}),
|
||||
)
|
||||
|
||||
return chapters.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
||||
})
|
||||
|
||||
function parseFrontmatter(content: string): Record<string, unknown> {
|
||||
const match = content.match(/^---\n([\s\S]*?)\n---/)
|
||||
if (!match) return {}
|
||||
|
||||
const lines = match[1].split('\n')
|
||||
const result: Record<string, unknown> = {}
|
||||
|
||||
for (const line of lines) {
|
||||
const colonIdx = line.indexOf(':')
|
||||
if (colonIdx === -1) continue
|
||||
const key = line.slice(0, colonIdx).trim()
|
||||
let value: string | number = line.slice(colonIdx + 1).trim()
|
||||
// Remove quotes
|
||||
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
||||
value = value.slice(1, -1)
|
||||
}
|
||||
// Parse numbers
|
||||
if (/^\d+$/.test(value)) {
|
||||
result[key] = parseInt(value, 10)
|
||||
}
|
||||
else {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user