From 2d91e17c3e3c2466508d123c57c7bc5a6bdf66d3 Mon Sep 17 00:00:00 2001 From: Hiro Date: Sat, 28 Mar 2026 17:36:58 +0000 Subject: [PATCH] fix: resolve API inconsistencies found by Mokoto - Bug 1: No fix needed - frontend already uses PUT for document updates - Bug 2: Changed folders API to use 'project' param (matches documents) - Bug 3: GET /folders now works without project filter (lists all folders) Changes: - folders.js: Accept 'project' instead of 'projectId', make it optional - folderService.js: Support listing all folders when projectId is null - api.js: Updated getFolders() to use 'project' param consistently --- public/js/api.js | 8 ++++--- src/routes/folders.js | 9 +++---- src/services/folderService.js | 45 +++++++++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/public/js/api.js b/public/js/api.js index 75ebe8f..227043b 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -171,10 +171,12 @@ class ApiClient { } // ===== Folders ===== - getFolders(projectId, parentId = null) { - const params = new URLSearchParams({ projectId }); + getFolders(project = null, parentId = null) { + const params = new URLSearchParams(); + if (project) params.append('project', project); if (parentId) params.append('parentId', parentId); - return this.get(`/folders?${params.toString()}`); + const query = params.toString(); + return this.get(`/folders${query ? '?' + query : ''}`); } getFolder(id) { diff --git a/src/routes/folders.js b/src/routes/folders.js index 4676577..d34441e 100644 --- a/src/routes/folders.js +++ b/src/routes/folders.js @@ -11,15 +11,12 @@ import { NotFoundError, ValidationError } from '../utils/errors.js'; const router = Router(); router.use(authMiddleware); -// GET /folders?projectId=X&parentId=Y - List folders +// GET /folders?project=X&parentId=Y - List folders (project is optional, matches documents API) router.get('/', async (req, res) => { try { - const { projectId, parentId } = req.query; - if (!projectId) { - throw new ValidationError('projectId query parameter is required'); - } + const { project, parentId } = req.query; const folderService = getFolderService(); - const folders = await folderService.getFolders(projectId, parentId || null); + const folders = await folderService.getFolders(project || null, parentId || null); res.json({ folders }); } catch (err) { if (err instanceof ValidationError) { diff --git a/src/services/folderService.js b/src/services/folderService.js index 5140e91..3e351f9 100644 --- a/src/services/folderService.js +++ b/src/services/folderService.js @@ -157,13 +157,15 @@ export class FolderService { } } - async getFolders(projectId, parentId = null) { - // Verify project exists - const projectService = getProjectService(this.dataRoot); - await projectService.getProject(projectId); - + async getFolders(projectId = null, parentId = null) { const folders = []; + // If projectId is provided, verify it exists + if (projectId) { + const projectService = getProjectService(this.dataRoot); + await projectService.getProject(projectId); + } + if (parentId) { // Get folders within a specific parent const parentMeta = this._resolveFolderMeta(parentId, null, projectId); @@ -187,8 +189,8 @@ export class FolderService { folderCount: this._countSubFolders(join(entryPath, 'sub-folders')), }); } - } else { - // Get root folders of the project + } else if (projectId) { + // Get root folders of a specific project const rootFoldersPath = this._projectFoldersPath(projectId); if (!pathExists(rootFoldersPath)) { return folders; @@ -208,6 +210,35 @@ export class FolderService { folderCount: this._countSubFolders(join(entryPath, 'sub-folders')), }); } + } else { + // No projectId provided - list folders from all projects + if (!pathExists(this.projectsPath)) { + return folders; + } + + const projectEntries = listDir(this.projectsPath); + for (const projectEntry of projectEntries) { + const projectPath = join(this.projectsPath, projectEntry); + if (!isDirectory(projectPath)) continue; + + const rootFoldersPath = join(projectPath, FOLDERS_DIR); + if (!pathExists(rootFoldersPath)) continue; + + const entries = listDir(rootFoldersPath); + for (const entry of entries) { + const entryPath = join(rootFoldersPath, entry); + if (!isDirectory(entryPath)) continue; + + const meta = readJSON(join(entryPath, FOLDER_META_FILE)); + if (!meta) continue; + + folders.push({ + ...meta, + documentCount: this._countDocuments(join(entryPath, 'documents')), + folderCount: this._countSubFolders(join(entryPath, 'sub-folders')), + }); + } + } } return folders;