const express = require('express'); const router = express.Router(); const fs = require('fs'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); const { logger } = require('../utils/logger'); // Import export module const exportRouter = require('./export'); function modifMd(id, modifications) { if (id === undefined) throw new Error('id required'); if (!Array.isArray(modifications) || modifications.length === 0) throw new Error('modifications required'); const dataDir = path.resolve(__dirname, '../data'); const mapPath = path.join(dataDir, 'uuid_map.json'); if (!fs.existsSync(mapPath)) throw new Error('uuid_map.json does not exist'); const map = JSON.parse(fs.readFileSync(mapPath, 'utf8')); const uuid = map[id]; if (!uuid) throw new Error(`No file for id ${id}`); const mdPath = path.join(dataDir, `${uuid}.md`); if (!fs.existsSync(mdPath)) throw new Error('Markdown file does not exist'); let lignes = fs.readFileSync(mdPath, 'utf8').split('\n'); modifications = modifications.slice().sort((a, b) => a.debut - b.debut); let offset = 0; for (let m of modifications) { let debut = m.debut + offset; let fin = m.fin + offset; const remplacement = Array.isArray(m.contenu) ? m.contenu : m.contenu.split('\n'); if (debut < 0 || fin >= lignes.length || debut > fin) throw new Error('Invalid range (start/end) for a modification'); const avant = lignes.slice(0, debut); const apres = lignes.slice(fin + 1); lignes = [...avant, ...remplacement, ...apres]; offset += remplacement.length - (fin - debut + 1); } const nouveau = lignes.join('\n'); fs.writeFileSync(mdPath, nouveau, { encoding: 'utf8', flag: 'w' }); return { id, uuid, path: mdPath, modifications }; } function createMd(markdownContent = "# Title\nContent...") { const uuid = uuidv4(); const dataDir = path.resolve(__dirname, '../data'); const mdPath = path.join(dataDir, `${uuid}.md`); const mapPath = path.join(dataDir, 'uuid_map.json'); // Guarantee storage directory exists (defensive in case server init didn't run) if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); logger.info('STORAGE', 'Created data directory on demand', { path: dataDir }); } fs.writeFileSync(mdPath, markdownContent, { encoding: 'utf8', flag: 'w' }); let map = {}; if (fs.existsSync(mapPath)) { map = JSON.parse(fs.readFileSync(mapPath, 'utf8')); } const existingIds = Object.keys(map) .map((key) => Number.parseInt(key, 10)) .filter((value) => Number.isInteger(value) && value >= 0); const nextIdNumber = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 0; const id = String(nextIdNumber); map[id] = uuid; fs.writeFileSync(mapPath, JSON.stringify(map, null, 2), 'utf8'); return { id, uuid, path: mdPath, markdownContent }; } function readMd(id = undefined) { const dataDir = path.resolve(__dirname, '../data'); const mapPath = path.join(dataDir, 'uuid_map.json'); let map = {}; if (fs.existsSync(mapPath)) { map = JSON.parse(fs.readFileSync(mapPath, 'utf8')); } if (id !== undefined) { const uuid = map[id]; if (!uuid) { throw new Error(`No file found for id ${id}`); } const mdPath = path.join(dataDir, `${uuid}.md`); if (!fs.existsSync(mdPath)) { throw new Error(`File ${mdPath} does not exist`); } const markdownContent = fs.readFileSync(mdPath, 'utf8'); return [{ id, uuid, path: mdPath, markdownContent }]; } else { const results = []; for (const [curId, uuid] of Object.entries(map)) { const mdPath = path.join(dataDir, `${uuid}.md`); if (fs.existsSync(mdPath)) { const markdownContent = fs.readFileSync(mdPath, 'utf8'); results.push({ id: curId, uuid, path: mdPath, markdownContent }); } } return results; } } function updateMd(id, newMarkdownContent) { if (id === undefined) throw new Error('id required'); const dataDir = path.resolve(__dirname, '../data'); const mapPath = path.join(dataDir, 'uuid_map.json'); if (!fs.existsSync(mapPath)) throw new Error('uuid_map.json does not exist'); let map = JSON.parse(fs.readFileSync(mapPath, 'utf8')); const uuid = map[id]; if (!uuid) throw new Error(`No file found for id ${id}`); const mdPath = path.join(dataDir, `${uuid}.md`); if (!fs.existsSync(mdPath)) throw new Error('Markdown file does not exist'); fs.writeFileSync(mdPath, newMarkdownContent, { encoding: 'utf8', flag: 'w' }); return { id, uuid, path: mdPath, newMarkdownContent }; } function deteMd(id) { if (id === undefined) throw new Error('id required'); const dataDir = path.resolve(__dirname, '../data'); const mapPath = path.join(dataDir, 'uuid_map.json'); if (!fs.existsSync(mapPath)) throw new Error('uuid_map.json does not exist'); let map = JSON.parse(fs.readFileSync(mapPath, 'utf8')); const uuid = map[id]; if (!uuid) throw new Error(`No file found for id ${id}`); const mdPath = path.join(dataDir, `${uuid}.md`); if (fs.existsSync(mdPath)) fs.unlinkSync(mdPath); delete map[id]; fs.writeFileSync(mapPath, JSON.stringify(map, null, 2), 'utf8'); return { id, deleted: true }; } // GET /api/journals - Get all journals router.get('/journals', (req, res) => { try { logger.storageRead('get-all-journals', 'all'); const data = readMd(); res.json({ success: true, data: data }); } catch (error) { logger.storageError('get-all-journals', error); res.status(500).json({ success: false, error: error.message }); } }); // POST /api/journals - Create a new journal router.post('/journals', (req, res) => { try { const { content } = req.body; const result = createMd(content); logger.storageWrite('create-journal', result.uuid); res.status(201).json({ success: true, data: result }); } catch (error) { logger.storageError('create-journal', error); res.status(500).json({ success: false, error: error.message }); } }); // GET /api/journals/:id - Get a specific journal router.get('/journals/:id', (req, res) => { try { const { id } = req.params; logger.storageRead('get-journal', id); const data = readMd(id); res.json({ success: true, data: data }); } catch (error) { logger.storageError('get-journal', error); res.status(500).json({ success: false, error: error.message }); } }); // PUT /api/journals/:id - Update a journal router.put('/journals/:id', (req, res) => { const { id } = req.params; const { content, modifications } = req.body; try { let result; if (content) { // Complete content update logger.storageWrite('update-journal-content', id); result = updateMd(id, content); } else if (modifications) { // Partial modifications (for future compatibility) logger.storageWrite('update-journal-modifications', id); result = modifMd(id, modifications); } else { logger.storageError('update-journal', new Error('Content or modifications required')); return res.status(400).json({ success: false, error: 'Content or modifications required' }); } res.json({ success: true, data: result }); } catch (error) { logger.storageError('update-journal', error); res.status(500).json({ success: false, error: error.message }); } }); // DELETE /api/journals/:id - Delete a journal router.delete('/journals/:id', (req, res) => { try { const { id } = req.params; logger.storageWrite('delete-journal', id); const data = deteMd(id); res.json({ success: true, data: data }); } catch (error) { logger.storageError('delete-journal', error); res.status(500).json({ success: false, error: error.message }); } }); // Integrate export routes router.use('/export', exportRouter); module.exports = router;