feat: implement Projects and Folders API (v2 architecture)
- Add projectService.js with full CRUD + tree operations - Add folderService.js with hierarchical folder support - Add projects.js routes: GET/POST/PUT/DELETE /projects, /projects/:id/tree, /projects/:id/documents - Add folders.js routes: GET/POST/PUT/DELETE /folders, /folders/:id/tree, /folders/:id/documents - Update documentService.js to support projectId/folderId with backwards compat (libraryId) - Update routes/index.js to mount new routers - Maintain backwards compatibility with legacy libraryId - All endpoints tested and working
This commit is contained in:
138
src/routes/folders.js
Normal file
138
src/routes/folders.js
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* SimpleNote Web - Folders Routes
|
||||
* CRUD + tree for folders
|
||||
*/
|
||||
|
||||
import { Router } from 'express';
|
||||
import { authMiddleware } from '../middleware/auth.js';
|
||||
import { getFolderService } from '../services/folderService.js';
|
||||
import { NotFoundError, ValidationError } from '../utils/errors.js';
|
||||
|
||||
const router = Router();
|
||||
router.use(authMiddleware);
|
||||
|
||||
// GET /folders?projectId=X&parentId=Y - List folders
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const { projectId, parentId } = req.query;
|
||||
if (!projectId) {
|
||||
throw new ValidationError('projectId query parameter is required');
|
||||
}
|
||||
const folderService = getFolderService();
|
||||
const folders = await folderService.getFolders(projectId, parentId || null);
|
||||
res.json({ folders });
|
||||
} catch (err) {
|
||||
if (err instanceof ValidationError) {
|
||||
return res.status(400).json({ error: err.message, code: err.code });
|
||||
}
|
||||
if (err instanceof NotFoundError) {
|
||||
return res.status(404).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error listing folders:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /folders - Create folder
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { name, projectId, parentId } = req.body;
|
||||
if (!name) {
|
||||
throw new ValidationError('name is required');
|
||||
}
|
||||
if (!projectId) {
|
||||
throw new ValidationError('projectId is required');
|
||||
}
|
||||
const folderService = getFolderService();
|
||||
const folder = await folderService.createFolder({ name, projectId, parentId: parentId || null });
|
||||
res.status(201).json(folder);
|
||||
} catch (err) {
|
||||
if (err instanceof ValidationError || err instanceof NotFoundError) {
|
||||
const status = err instanceof ValidationError ? 400 : 404;
|
||||
return res.status(status).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error creating folder:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /folders/:id - Get folder contents
|
||||
router.get('/:id', async (req, res) => {
|
||||
try {
|
||||
const folderService = getFolderService();
|
||||
const folder = await folderService.getFolder(req.params.id);
|
||||
res.json(folder);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return res.status(404).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error getting folder:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /folders/:id - Update folder
|
||||
router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
const { name } = req.body;
|
||||
const folderService = getFolderService();
|
||||
const folder = await folderService.updateFolder(req.params.id, { name });
|
||||
res.json(folder);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return res.status(404).json({ error: err.message, code: err.code });
|
||||
}
|
||||
if (err instanceof ValidationError) {
|
||||
return res.status(400).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error updating folder:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /folders/:id - Delete folder
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
const folderService = getFolderService();
|
||||
const result = await folderService.deleteFolder(req.params.id);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return res.status(404).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error deleting folder:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /folders/:id/documents - List documents in folder
|
||||
router.get('/:id/documents', async (req, res) => {
|
||||
try {
|
||||
const folderService = getFolderService();
|
||||
const result = await folderService.getFolderDocuments(req.params.id);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return res.status(404).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error listing folder documents:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /folders/:id/tree - Get full folder tree
|
||||
router.get('/:id/tree', async (req, res) => {
|
||||
try {
|
||||
const folderService = getFolderService();
|
||||
const tree = await folderService.getFolderTree(req.params.id);
|
||||
res.json(tree);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return res.status(404).json({ error: err.message, code: err.code });
|
||||
}
|
||||
console.error('Error getting folder tree:', err);
|
||||
res.status(500).json({ error: 'Internal server error', code: 'INTERNAL_ERROR' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user