diff --git a/server/index.js b/server/index.js index fa6f142f..7dd1e314 100755 --- a/server/index.js +++ b/server/index.js @@ -19,7 +19,6 @@ import { getConnectableHost } from '../shared/networkHosts.js'; import { findAppRoot, getModuleDir } from './utils/runtime-paths.js'; import { - renameProjectById, deleteSessionById, deleteProjectById, getProjectPathById, @@ -302,17 +301,6 @@ app.post('/api/system/update', authenticateToken, async (req, res) => { } }); -// Rename project endpoint; stores the custom name on the DB row for `projectId`. -app.put('/api/projects/:projectId/rename', authenticateToken, async (req, res) => { - try { - const { displayName } = req.body; - await renameProjectById(req.params.projectId, displayName); - res.json({ success: true }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - // Delete session endpoint; resolves `projectId` to path before touching disk. app.delete('/api/projects/:projectId/sessions/:sessionId', authenticateToken, async (req, res) => { try { diff --git a/server/modules/projects/projects.routes.ts b/server/modules/projects/projects.routes.ts index 6ee65104..b593f60c 100644 --- a/server/modules/projects/projects.routes.ts +++ b/server/modules/projects/projects.routes.ts @@ -1,6 +1,6 @@ import express from 'express'; -import { createProject } from '@/modules/projects/services/project-management.service.js'; +import { createProject, updateProjectDisplayName } from '@/modules/projects/services/project-management.service.js'; import { startCloneProject } from '@/modules/projects/services/project-clone.service.js'; import { getProjectTaskMaster } from '@/modules/projects/services/projects-has-taskmaster.service.js'; import { AppError, asyncHandler } from '@/shared/utils.js'; @@ -166,4 +166,15 @@ router.get( }), ); +router.put('/:projectId/rename', (req, res) => { + try { + const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : ''; + const { displayName } = req.body as { displayName?: unknown }; + updateProjectDisplayName(projectId, displayName); + res.json({ success: true }); + } catch (error) { + res.status(500).json({ error: error instanceof Error ? error.message : 'Failed to rename project' }); + } +}); + export default router; diff --git a/server/modules/projects/services/project-management.service.ts b/server/modules/projects/services/project-management.service.ts index e25e90b1..4362c487 100644 --- a/server/modules/projects/services/project-management.service.ts +++ b/server/modules/projects/services/project-management.service.ts @@ -140,3 +140,11 @@ export async function createProject( project: mapProjectRowToApiView(projectRow), }; } + +/** + * Sets `projects.custom_project_name` for the given `projectId` (or clears it when empty). + */ +export function updateProjectDisplayName(projectId: string, newDisplayName: unknown): void { + const trimmed = typeof newDisplayName === 'string' ? newDisplayName.trim() : ''; + projectsDb.updateCustomProjectNameById(projectId, trimmed.length > 0 ? trimmed : null); +} diff --git a/server/projects.js b/server/projects.js index eae9c375..332696b6 100755 --- a/server/projects.js +++ b/server/projects.js @@ -666,57 +666,6 @@ async function getSessionMessages(projectName, sessionId, limit = null, offset = } } -// Rename a project's display name -async function renameProject(projectName, newDisplayName) { - const config = await loadProjectConfig(); - - if (!newDisplayName || newDisplayName.trim() === '') { - // Remove custom name if empty, will fall back to auto-generated - if (config[projectName]) { - delete config[projectName].displayName; - } - } else { - // Set custom display name, preserving other properties (manuallyAdded, originalPath) - config[projectName] = { - ...config[projectName], - displayName: newDisplayName.trim() - }; - } - - await saveProjectConfig(config); - return true; -} - -/** - * ID-based wrapper around `renameProject`. - * - * Writes the new display name to the `projects.custom_project_name` column - * (the source of truth for the DB-driven getProjectsWithSessions() response) and also - * keeps the legacy project-config.json in sync for backwards compatibility - * with any code that still reads it. - */ -async function renameProjectById(projectId, newDisplayName) { - const projectPath = await getProjectPathById(projectId); - if (!projectPath) { - throw new Error(`Unknown projectId: ${projectId}`); - } - - const trimmed = typeof newDisplayName === 'string' ? newDisplayName.trim() : ''; - // Persist on the DB row so getProjectsWithSessions() immediately reflects the change. - projectsDb.updateCustomProjectNameById(projectId, trimmed.length > 0 ? trimmed : null); - - // Keep the legacy file-based project config in lockstep so historic readers - // that still consult project-config.json don't diverge. - const claudeFolderName = claudeFolderNameFromPath(projectPath); - try { - await renameProject(claudeFolderName, trimmed); - } catch (error) { - console.warn(`[projects] Legacy renameProject sync failed for ${projectId}:`, error.message); - } - - return true; -} - /** * ID-based wrapper around `deleteSession`. * @@ -2030,11 +1979,10 @@ async function getGeminiCliSessionMessages(sessionId) { } // Only functions with consumers outside this module are exported. Folder-name -// based helpers (`getSessions`, `renameProject`, `deleteSession`, etc.) are -// kept as internal implementation details of the id-based wrappers below. +// based helpers (`getSessions`, `deleteSession`, etc.) are kept as internal +// implementation details of the id-based wrappers below. export { getSessionMessages, - renameProjectById, deleteSessionById, deleteProjectById, addProjectManually,