import { Hono } from 'hono'; import { HonoEnv } from '../lib/types'; export const entriesRoutes = new Hono(); entriesRoutes.post('/', async (c) => { const userId = c.get('userId'); const prisma = c.get('prisma'); const mediaDir = c.env.MEDIA_DIR || './data/media'; const body = await c.req.json(); const { date, type, content, metadata } = body; if (!date || !type || !content) { return c.json({ data: null, error: { code: 'VALIDATION_ERROR', message: 'date, type, and content are required' } }, 400); } const validTypes = ['text', 'voice', 'photo', 'health', 'location']; if (!validTypes.includes(type)) { return c.json({ data: null, error: { code: 'VALIDATION_ERROR', message: `type must be one of: ${validTypes.join(', ')}` } }, 400); } const entry = await prisma.entry.create({ data: { userId, date, type, content, metadata: metadata ? JSON.stringify(metadata) : null, }, }); return c.json({ data: entry, error: null }, 201); }); entriesRoutes.get('/:id', async (c) => { const userId = c.get('userId'); const { id } = c.req.param(); const prisma = c.get('prisma'); const entry = await prisma.entry.findFirst({ where: { id, userId }, }); if (!entry) { return c.json({ data: null, error: { code: 'NOT_FOUND', message: 'Entry not found' } }, 404); } return c.json({ data: entry, error: null }); }); entriesRoutes.put('/:id', async (c) => { const userId = c.get('userId'); const { id } = c.req.param(); const prisma = c.get('prisma'); const body = await c.req.json(); const { content, metadata } = body; const existing = await prisma.entry.findFirst({ where: { id, userId }, }); if (!existing) { return c.json({ data: null, error: { code: 'NOT_FOUND', message: 'Entry not found' } }, 404); } const journal = await prisma.journal.findFirst({ where: { userId, date: existing.date } }); if (journal) { return c.json({ data: null, error: { code: 'ENTRY_IMMUTABLE', message: 'Cannot edit entry: journal already generated. Delete the journal first.' } }, 400); } const entry = await prisma.entry.update({ where: { id }, data: { content: content ?? existing.content, metadata: metadata !== undefined ? JSON.stringify(metadata) : existing.metadata, }, }); return c.json({ data: entry, error: null }); }); entriesRoutes.delete('/:id', async (c) => { const userId = c.get('userId'); const { id } = c.req.param(); const prisma = c.get('prisma'); const existing = await prisma.entry.findFirst({ where: { id, userId }, }); if (!existing) { return c.json({ data: null, error: { code: 'NOT_FOUND', message: 'Entry not found' } }, 404); } const journal = await prisma.journal.findFirst({ where: { userId, date: existing.date } }); if (journal) { return c.json({ data: null, error: { code: 'ENTRY_IMMUTABLE', message: 'Cannot delete entry: journal already generated. Delete the journal first.' } }, 400); } await prisma.entry.delete({ where: { id } }); return c.json({ data: { deleted: true }, error: null }); }); entriesRoutes.post('/:id/photo', async (c) => { const userId = c.get('userId'); const { id } = c.req.param(); const prisma = c.get('prisma'); const mediaDir = c.env.MEDIA_DIR || './data/media'; const entry = await prisma.entry.findFirst({ where: { id, userId }, }); if (!entry) { return c.json({ data: null, error: { code: 'NOT_FOUND', message: 'Entry not found' } }, 404); } const body = await c.req.parseBody(); const file = body.file; if (!file || !(file instanceof File)) { return c.json({ data: null, error: { code: 'VALIDATION_ERROR', message: 'No file provided' } }, 400); } const ext = file.name.split('.').pop() || 'jpg'; const fileName = `${id}.${ext}`; const userMediaDir = `${mediaDir}/${userId}/${entry.date}`; const filePath = `${userMediaDir}/${fileName}`; await Bun.write(filePath, file); await prisma.entry.update({ where: { id }, data: { mediaPath: filePath }, }); return c.json({ data: { mediaPath: filePath }, error: null }, 201); }); entriesRoutes.post('/:id/voice', async (c) => { const userId = c.get('userId'); const { id } = c.req.param(); const prisma = c.get('prisma'); const mediaDir = c.env.MEDIA_DIR || './data/media'; const entry = await prisma.entry.findFirst({ where: { id, userId }, }); if (!entry) { return c.json({ data: null, error: { code: 'NOT_FOUND', message: 'Entry not found' } }, 404); } const body = await c.req.parseBody(); const file = body.file; if (!file || !(file instanceof File)) { return c.json({ data: null, error: { code: 'VALIDATION_ERROR', message: 'No audio file provided' } }, 400); } const fileName = `${id}.webm`; const userMediaDir = `${mediaDir}/${userId}/${entry.date}`; const filePath = `${userMediaDir}/${fileName}`; await Bun.write(filePath, file); await prisma.entry.update({ where: { id }, data: { mediaPath: filePath }, }); return c.json({ data: { mediaPath: filePath }, error: null }, 201); });