83 lines
2.5 KiB
TypeScript
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;
|