diff --git a/packages/chat-client/vite.config.js b/packages/chat-client/vite.config.js index c7cd74e..68f098a 100644 --- a/packages/chat-client/vite.config.js +++ b/packages/chat-client/vite.config.js @@ -12,6 +12,7 @@ export default defineConfig({ '/chat': 'http://192.168.0.205:4000', '/sessions': 'http://192.168.0.205:4000', '/models': 'http://192.168.0.205:4000', + '/projects': 'http://192.168.0.205:4000', }, }, }); \ No newline at end of file diff --git a/packages/memory-service/src/db/projects.js b/packages/memory-service/src/db/projects.js new file mode 100644 index 0000000..548c104 --- /dev/null +++ b/packages/memory-service/src/db/projects.js @@ -0,0 +1,37 @@ +const { getDB } = require('./index'); +const { parseRow } = require('@nexusai/shared'); + +function createProject({ name, description, colour, icon }) { + const db = getDB(); + const result = db.prepare(` + INSERT INTO projects (name, description, colour, icon) + VALUES (?, ?, ?, ?) + `).run(name, description ?? null, colour ?? null, icon ?? null); + return getProject(result.lastInsertRowid); +} + +function getProjects() { + const db = getDB(); + return db.prepare(`SELECT * FROM projects ORDER BY created_at DESC`).all().map(parseRow); +} + +function getProject(id) { + const db = getDB(); + return parseRow(db.prepare(`SELECT * FROM projects WHERE id = ?`).get(id)); +} + +function updateProject(id, { name, description, colour, icon }) { + const db = getDB(); + db.prepare(` + UPDATE projects SET name = ?, description = ?, colour = ?, icon = ? + WHERE id = ? + `).run(name, description ?? null, colour ?? null, icon ?? null, id); + return getProject(id); +} + +function deleteProject(id) { + const db = getDB(); + db.prepare(`DELETE FROM projects WHERE id = ?`).run(id); +} + +module.exports = { createProject, getProjects, getProject, updateProject, deleteProject }; \ No newline at end of file diff --git a/packages/orchestration-service/src/index.js b/packages/orchestration-service/src/index.js index 5e4b80b..6eae722 100644 --- a/packages/orchestration-service/src/index.js +++ b/packages/orchestration-service/src/index.js @@ -3,7 +3,8 @@ const express = require('express'); const {getEnv, PORTS, SERVICES, ORCHESTRATION} = require('@nexusai/shared'); const chatRouter = require('./routes/chat'); const sessionsRouter = require('./routes/sessions'); -const modelsRouter = require('./routes/models') +const modelsRouter = require('./routes/models'); +const projectsRouter = require('./routes/projects'); const cors = require('cors'); const app = express(); @@ -37,6 +38,7 @@ app.get('/health', (req, res) => { app.use('/chat', chatRouter); app.use('/sessions', sessionsRouter); app.use('/models', modelsRouter); +app.use('/projects', projectsRouter); /******* Start the server ************/ app.listen(PORT, () => { diff --git a/packages/orchestration-service/src/routes/projects.js b/packages/orchestration-service/src/routes/projects.js new file mode 100644 index 0000000..7886d0b --- /dev/null +++ b/packages/orchestration-service/src/routes/projects.js @@ -0,0 +1,41 @@ +const { Router } = require('express'); +const memory = require('../services/memory'); + +const router = Router(); + +router.get('/', async (req, res) => { + try { + res.json(await memory.getProjects()); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post('/', async (req, res) => { + const { name, description, colour, icon } = req.body; + if (!name?.trim()) return res.status(400).json({ error: 'name is required' }); + try { + res.status(201).json(await memory.createProject({ name: name.trim(), description, colour, icon })); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.patch('/:id', async (req, res) => { + try { + res.json(await memory.updateProject(req.params.id, req.body)); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.delete('/:id', async (req, res) => { + try { + await memory.deleteProject(req.params.id); + res.status(204).send(); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/packages/orchestration-service/src/services/memory.js b/packages/orchestration-service/src/services/memory.js index a9531a8..b4d99e4 100644 --- a/packages/orchestration-service/src/services/memory.js +++ b/packages/orchestration-service/src/services/memory.js @@ -80,6 +80,38 @@ async function deleteSession(externalId) { if (!res.ok) throw new Error(`Failed to delete session: ${res.status}`); } +/******** PROJECTS ********* */ +async function createProject({ name, description, colour, icon }) { + const res = await fetch(`${BASE_URL}/projects`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, description, colour, icon }) + }); + if (!res.ok) throw new Error(`Failed to create project: ${res.status}`); + return res.json(); +} + +async function getProjects() { + const res = await fetch(`${BASE_URL}/projects`); + if (!res.ok) throw new Error(`Failed to fetch projects: ${res.status}`); + return res.json(); +} + +async function updateProject(id, { name, description, colour, icon }) { + const res = await fetch(`${BASE_URL}/projects/${id}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, description, colour, icon }) + }); + if (!res.ok) throw new Error(`Failed to update project: ${res.status}`); + return res.json(); +} + +async function deleteProject(id) { + const res = await fetch(`${BASE_URL}/projects/${id}`, { method: 'DELETE' }); + if (!res.ok) throw new Error(`Failed to delete project: ${res.status}`); +} + module.exports = { getSessionByExternalId, createSession, @@ -90,4 +122,8 @@ module.exports = { getSessions, updateSession, deleteSession, + createProject, + getProjects, + updateProject, + deleteProject, } \ No newline at end of file diff --git a/packages/project.zip b/packages/project.zip new file mode 100644 index 0000000..a81e32a Binary files /dev/null and b/packages/project.zip differ