From 88d02e0315887509a6d4f3b3905ccf51d479baee Mon Sep 17 00:00:00 2001 From: Tuan-Dat Tran Date: Fri, 20 Feb 2026 17:15:59 +0100 Subject: [PATCH] refactor(api): update cv routes for knex with openapi docs --- backend/routes/cv.js | 161 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 backend/routes/cv.js diff --git a/backend/routes/cv.js b/backend/routes/cv.js new file mode 100644 index 0000000..7342735 --- /dev/null +++ b/backend/routes/cv.js @@ -0,0 +1,161 @@ +import { Router } from 'express'; +import { getDB } from '../db/init.js'; +import { authMiddleware } from '../middleware/auth.js'; + +const router = Router(); + +/** + * @openapi + * /cv: + * get: + * summary: Get CV data + * tags: [CV] + * responses: + * 200: + * description: CV data + * content: + * application/json: + * schema: + * type: object + * 404: + * description: CV data not found + */ +router.get('/', async (req, res) => { + try { + const db = await getDB(); + const row = await db('cv_data').where({ id: 1 }).first(); + + if (!row) { + return res.status(404).json({ error: 'CV data not found' }); + } + + res.json(JSON.parse(row.data)); + } catch (error) { + console.error('Error fetching CV:', error); + res.status(500).json({ error: 'Failed to fetch CV data' }); + } +}); + +/** + * @openapi + * /cv: + * put: + * summary: Update CV data + * tags: [CV] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * responses: + * 200: + * description: CV data updated + * 401: + * description: Unauthorized + */ +router.put('/', authMiddleware, async (req, res) => { + try { + const data = req.body; + + if (!data || typeof data !== 'object') { + return res.status(400).json({ error: 'Invalid CV data' }); + } + + if (!data.personal?.name) { + return res.status(400).json({ error: 'personal.name is required' }); + } + + const db = await getDB(); + await db('cv_data').where({ id: 1 }).update({ + data: JSON.stringify(data), + updated_at: db.fn.now() + }); + + res.json({ success: true, message: 'CV data updated' }); + } catch (error) { + console.error('Error updating CV:', error); + res.status(500).json({ error: 'Failed to update CV data' }); + } +}); + +/** + * @openapi + * /cv/export: + * get: + * summary: Export CV data as JSON file + * tags: [CV] + * responses: + * 200: + * description: CV JSON file + * content: + * application/json: + * schema: + * type: object + */ +router.get('/export', async (req, res) => { + try { + const db = await getDB(); + const row = await db('cv_data').where({ id: 1 }).first(); + + if (!row) { + return res.status(404).json({ error: 'CV data not found' }); + } + + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Content-Disposition', 'attachment; filename="cv.json"'); + res.send(row.data); + } catch (error) { + console.error('Error exporting CV:', error); + res.status(500).json({ error: 'Failed to export CV data' }); + } +}); + +/** + * @openapi + * /cv/import: + * post: + * summary: Import CV data from JSON + * tags: [CV] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * responses: + * 200: + * description: CV data imported + * 401: + * description: Unauthorized + */ +router.post('/import', authMiddleware, async (req, res) => { + try { + const data = req.body; + + if (!data || typeof data !== 'object') { + return res.status(400).json({ error: 'Invalid CV data' }); + } + + if (!data.personal?.name) { + return res.status(400).json({ error: 'personal.name is required' }); + } + + const db = await getDB(); + await db('cv_data').where({ id: 1 }).update({ + data: JSON.stringify(data), + updated_at: db.fn.now() + }); + + res.json({ success: true, message: 'CV data imported' }); + } catch (error) { + console.error('Error importing CV:', error); + res.status(500).json({ error: 'Failed to import CV data' }); + } +}); + +export default router;