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
This commit is contained in:
Hiro
2026-03-28 17:36:58 +00:00
parent 007c51a98f
commit 2d91e17c3e
3 changed files with 46 additions and 16 deletions

View File

@@ -171,10 +171,12 @@ class ApiClient {
} }
// ===== Folders ===== // ===== Folders =====
getFolders(projectId, parentId = null) { getFolders(project = null, parentId = null) {
const params = new URLSearchParams({ projectId }); const params = new URLSearchParams();
if (project) params.append('project', project);
if (parentId) params.append('parentId', parentId); if (parentId) params.append('parentId', parentId);
return this.get(`/folders?${params.toString()}`); const query = params.toString();
return this.get(`/folders${query ? '?' + query : ''}`);
} }
getFolder(id) { getFolder(id) {

View File

@@ -11,15 +11,12 @@ import { NotFoundError, ValidationError } from '../utils/errors.js';
const router = Router(); const router = Router();
router.use(authMiddleware); 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) => { router.get('/', async (req, res) => {
try { try {
const { projectId, parentId } = req.query; const { project, parentId } = req.query;
if (!projectId) {
throw new ValidationError('projectId query parameter is required');
}
const folderService = getFolderService(); const folderService = getFolderService();
const folders = await folderService.getFolders(projectId, parentId || null); const folders = await folderService.getFolders(project || null, parentId || null);
res.json({ folders }); res.json({ folders });
} catch (err) { } catch (err) {
if (err instanceof ValidationError) { if (err instanceof ValidationError) {

View File

@@ -157,12 +157,14 @@ export class FolderService {
} }
} }
async getFolders(projectId, parentId = null) { async getFolders(projectId = null, parentId = null) {
// Verify project exists const folders = [];
// If projectId is provided, verify it exists
if (projectId) {
const projectService = getProjectService(this.dataRoot); const projectService = getProjectService(this.dataRoot);
await projectService.getProject(projectId); await projectService.getProject(projectId);
}
const folders = [];
if (parentId) { if (parentId) {
// Get folders within a specific parent // Get folders within a specific parent
@@ -187,8 +189,8 @@ export class FolderService {
folderCount: this._countSubFolders(join(entryPath, 'sub-folders')), folderCount: this._countSubFolders(join(entryPath, 'sub-folders')),
}); });
} }
} else { } else if (projectId) {
// Get root folders of the project // Get root folders of a specific project
const rootFoldersPath = this._projectFoldersPath(projectId); const rootFoldersPath = this._projectFoldersPath(projectId);
if (!pathExists(rootFoldersPath)) { if (!pathExists(rootFoldersPath)) {
return folders; return folders;
@@ -208,6 +210,35 @@ export class FolderService {
folderCount: this._countSubFolders(join(entryPath, 'sub-folders')), 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; return folders;