更新内容
This commit is contained in:
+79
-40
@@ -4,6 +4,16 @@ import fs from 'fs/promises';
|
||||
import { existsSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { defineConfig, Plugin } from 'vite';
|
||||
import {
|
||||
deleteDocumentByFileName,
|
||||
getDocumentByFileName,
|
||||
getDocumentById,
|
||||
getVocabCards,
|
||||
initDatabase,
|
||||
listDocuments,
|
||||
migrateSavesToDatabase,
|
||||
saveDocument,
|
||||
} from './server/db.js';
|
||||
|
||||
const SAVES_DIR = path.resolve(__dirname, 'saves');
|
||||
|
||||
@@ -37,52 +47,38 @@ const sendJson = (res: any, statusCode: number, payload: unknown) => {
|
||||
const localSavesPlugin = (): Plugin => ({
|
||||
name: 'local-saves-api',
|
||||
configureServer(server) {
|
||||
const ready = (async () => {
|
||||
await initDatabase();
|
||||
await ensureSavesDir();
|
||||
await migrateSavesToDatabase(SAVES_DIR);
|
||||
})();
|
||||
|
||||
server.middlewares.use(async (req, res, next) => {
|
||||
if (!req.url?.startsWith('/api/saves')) {
|
||||
if (!req.url?.startsWith('/api/saves') && !req.url?.startsWith('/api/documents') && !req.url?.startsWith('/api/vocab')) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ready;
|
||||
await ensureSavesDir();
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
const fileNameFromPath = decodeURIComponent(url.pathname.replace(/^\/api\/saves\/?/, ''));
|
||||
|
||||
if (req.method === 'GET' && url.pathname === '/api/saves') {
|
||||
const entries = await fs.readdir(SAVES_DIR);
|
||||
const files = await Promise.all(
|
||||
entries
|
||||
.filter((entry) => entry.toLowerCase().endsWith('.json'))
|
||||
.map(async (entry) => {
|
||||
const filePath = path.join(SAVES_DIR, entry);
|
||||
const stat = await fs.stat(filePath);
|
||||
let lineCount: number | null = null;
|
||||
let title = entry.replace(/\.json$/i, '');
|
||||
try {
|
||||
const parsed = JSON.parse(await fs.readFile(filePath, 'utf-8'));
|
||||
lineCount = Array.isArray(parsed?.lines) ? parsed.lines.length : null;
|
||||
title = typeof parsed?.title === 'string' && parsed.title.trim() ? parsed.title : title;
|
||||
} catch {
|
||||
lineCount = null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: entry,
|
||||
title,
|
||||
savedAt: stat.mtime.toISOString(),
|
||||
size: stat.size,
|
||||
lineCount,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
files.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
|
||||
const files = await listDocuments();
|
||||
sendJson(res, 200, { files });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && fileNameFromPath) {
|
||||
if (req.method === 'GET' && url.pathname.startsWith('/api/saves/') && fileNameFromPath) {
|
||||
const fileName = sanitizeSaveName(fileNameFromPath);
|
||||
const document = await getDocumentByFileName(fileName);
|
||||
if (document) {
|
||||
sendJson(res, 200, document);
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join(SAVES_DIR, fileName);
|
||||
if (!existsSync(filePath)) {
|
||||
sendJson(res, 404, { error: 'Save file not found' });
|
||||
@@ -105,19 +101,12 @@ const localSavesPlugin = (): Plugin => ({
|
||||
|
||||
const filePath = path.join(SAVES_DIR, fileName);
|
||||
await fs.writeFile(filePath, JSON.stringify(document, null, 2), 'utf-8');
|
||||
const stat = await fs.stat(filePath);
|
||||
sendJson(res, 200, {
|
||||
file: {
|
||||
name: fileName,
|
||||
savedAt: stat.mtime.toISOString(),
|
||||
size: stat.size,
|
||||
lineCount: Array.isArray(document.lines) ? document.lines.length : null,
|
||||
},
|
||||
});
|
||||
const file = await saveDocument({ name: fileName, document });
|
||||
sendJson(res, 200, { file });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE' && fileNameFromPath) {
|
||||
if (req.method === 'DELETE' && url.pathname.startsWith('/api/saves/') && fileNameFromPath) {
|
||||
const fileName = sanitizeSaveName(fileNameFromPath);
|
||||
const filePath = path.join(SAVES_DIR, fileName);
|
||||
if (!existsSync(filePath)) {
|
||||
@@ -125,10 +114,59 @@ const localSavesPlugin = (): Plugin => ({
|
||||
return;
|
||||
}
|
||||
await fs.unlink(filePath);
|
||||
await deleteDocumentByFileName(fileName);
|
||||
sendJson(res, 200, { ok: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && url.pathname === '/api/documents') {
|
||||
const files = await listDocuments();
|
||||
sendJson(res, 200, { files });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && url.pathname.startsWith('/api/documents/')) {
|
||||
const id = decodeURIComponent(url.pathname.replace(/^\/api\/documents\/?/, ''));
|
||||
const document = await getDocumentById(id);
|
||||
if (!document) {
|
||||
sendJson(res, 404, { error: 'Document not found' });
|
||||
return;
|
||||
}
|
||||
sendJson(res, 200, document);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'POST' && url.pathname === '/api/documents') {
|
||||
const body = await readJsonBody(req);
|
||||
const document = body.document;
|
||||
if (!document || typeof document !== 'object') {
|
||||
sendJson(res, 400, { error: 'Missing document payload' });
|
||||
return;
|
||||
}
|
||||
const file = await saveDocument({ name: String(body.name || document.title || 'current'), document });
|
||||
await fs.writeFile(path.join(SAVES_DIR, file.name), JSON.stringify(document, null, 2), 'utf-8');
|
||||
sendJson(res, 200, { file });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && url.pathname === '/api/vocab') {
|
||||
const cards = await getVocabCards();
|
||||
sendJson(res, 200, { cards });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && url.pathname.startsWith('/api/vocab/')) {
|
||||
const term = decodeURIComponent(url.pathname.replace(/^\/api\/vocab\/?/, ''));
|
||||
const cards = await getVocabCards();
|
||||
const card = cards.find((item: any) => item.normalizedText === term || item.text === term);
|
||||
if (!card) {
|
||||
sendJson(res, 404, { error: 'Vocab card not found' });
|
||||
return;
|
||||
}
|
||||
sendJson(res, 200, { card });
|
||||
return;
|
||||
}
|
||||
|
||||
sendJson(res, 405, { error: 'Unsupported saves API request' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -140,6 +178,7 @@ const localSavesPlugin = (): Plugin => ({
|
||||
|
||||
export default defineConfig(() => {
|
||||
return {
|
||||
base: './',
|
||||
plugins: [react(), tailwindcss(), localSavesPlugin()],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Reference in New Issue
Block a user