voxblog/apps/api/src/drafts.ts

83 lines
2.5 KiB
TypeScript

import express from 'express';
import path from 'path';
import { promises as fs } from 'fs';
import crypto from 'crypto';
import { db } from './db';
import { posts } from './db/schema';
import { eq } from 'drizzle-orm';
const router = express.Router();
const draftsDir = path.resolve(__dirname, '../../../data/drafts');
async function ensureDir() {
await fs.mkdir(draftsDir, { recursive: true });
}
router.get('/', async (_req, res) => {
try {
await ensureDir();
const files = await fs.readdir(draftsDir);
const items = files.filter(f => f.endsWith('.json')).map(f => f.replace(/\.json$/, ''));
res.json({ items });
} catch (err) {
console.error('List drafts error:', err);
res.status(500).json({ error: 'Failed to list drafts' });
}
});
router.get('/:id', async (req, res) => {
try {
await ensureDir();
const id = req.params.id;
const file = path.join(draftsDir, `${id}.json`);
const content = await fs.readFile(file, 'utf8');
res.json(JSON.parse(content));
} catch (err) {
console.error('Read draft error:', err);
res.status(404).json({ error: 'Draft not found' });
}
});
router.post('/', async (req, res) => {
try {
await ensureDir();
const { id, content } = req.body as { id?: string; content?: string };
if (typeof content !== 'string') {
return res.status(400).json({ error: 'content is required' });
}
const draftId = id || crypto.randomUUID();
const file = path.join(draftsDir, `${draftId}.json`);
const payload = { id: draftId, content, updatedAt: new Date().toISOString() };
await fs.writeFile(file, JSON.stringify(payload, null, 2), 'utf8');
try {
const now = new Date();
// Upsert: try insert, fall back to update
try {
await db.insert(posts).values({
id: draftId,
title: null as any,
contentHtml: content,
tagsText: null as any,
featureImage: null as any,
canonicalUrl: null as any,
status: 'editing' as any,
version: 1,
createdAt: now,
updatedAt: now,
});
} catch {
await db.update(posts).set({ contentHtml: content, updatedAt: now }).where(eq(posts.id, draftId));
}
} catch (e) {
// ignore DB errors to keep file-based save working
}
res.json({ success: true, id: draftId });
} catch (err) {
console.error('Save draft error:', err);
res.status(500).json({ error: 'Failed to save draft' });
}
});
export default router;