diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index a03006c..fe4fee6 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -65,8 +65,43 @@ router.get('/new', requireAuth, async (req, res) => { }); }); +// Helper to detect API bearer requests +function isApiRequest(req) { + const accept = (req.get('accept') || '').toLowerCase(); + return accept.includes('application/json') || accept.includes('application/vnd.api+json'); +} + // GET /sites/:siteId/containers - List all containers for the logged-in user in this site -router.get('/', requireAuth, async (req, res) => { +router.get('/', async (req, res) => { + // If called by API clients using Bearer token, return JSON instead of HTML + if (isApiRequest(req)) { + try { + const siteId = parseInt(req.params.siteId, 10); + const site = await Site.findByPk(siteId); + if (!site) return res.status(404).json([]); + + // Limit search to nodes within this site + const nodes = await Node.findAll({ where: { siteId }, attributes: ['id'] }); + const nodeIds = nodes.map(n => n.id); + + const { hostname } = req.query; + const where = {}; + if (hostname) where.hostname = hostname; + where.nodeId = nodeIds; + + const containers = await Container.findAll({ where, include: [{ association: 'node', attributes: ['id', 'name'] }] }); + const out = containers.map(c => ({ id: c.id, hostname: c.hostname, ipv4Address: c.ipv4Address, macAddress: c.macAddress, node: c.node ? { id: c.node.id, name: c.node.name } : null, createdAt: c.createdAt })); + return res.json(out); + } catch (err) { + console.error('API GET /sites/:siteId/containers error:', err); + return res.status(500).json({ error: 'Internal server error' }); + } + } + + // Browser path: require authentication and render HTML + await new Promise(resolve => requireAuth(req, res, resolve)); + if (res.headersSent) return; // requireAuth already handled redirect + const siteId = parseInt(req.params.siteId, 10); const site = await Site.findByPk(siteId); @@ -203,12 +238,41 @@ router.post('/', async (req, res) => { // Validate site exists const site = await Site.findByPk(siteId); if (!site) { + if (isApiRequest(req)) return res.status(404).json({ error: 'Site not found' }); req.flash('error', 'Site not found'); return res.redirect('/sites'); } // TODO: build the container async in a Job try { + // If API client (Bearer token), perform a lightweight create and return JSON + if (isApiRequest(req)) { + try { + const { hostname, ipv4Address, macAddress, nodeName, containerId } = req.body; + if (!hostname) return res.status(400).json({ error: 'hostname required' }); + // attempt to associate node if provided + let node = null; + if (nodeName) node = await Node.findOne({ where: { name: nodeName, siteId } }); + + let existing = await Container.findOne({ where: { hostname } }); + if (existing) return res.status(200).json({ containerId: existing.id, message: 'Already exists' }); + + const created = await Container.create({ + hostname, + username: req.body.username || 'api', + nodeId: node ? node.id : null, + containerId: containerId || null, + macAddress: macAddress || null, + ipv4Address: ipv4Address || null + }); + + return res.status(201).json({ containerId: created.id, message: 'Created' }); + } catch (err) { + console.error('API POST /sites/:siteId/containers error:', err); + return res.status(500).json({ error: 'Internal server error' }); + } + } + const { hostname, template, services } = req.body; const [ nodeName, templateVmid ] = template.split(','); const node = await Node.findOne({ where: { name: nodeName, siteId } }); @@ -378,9 +442,25 @@ router.post('/', async (req, res) => { }); // PUT /sites/:siteId/containers/:id - Update container services -router.put('/:id', requireAuth, async (req, res) => { +router.put('/:id', async (req, res) => { const siteId = parseInt(req.params.siteId, 10); const containerId = parseInt(req.params.id, 10); + // API clients may update container metadata via Bearer token + if (isApiRequest(req)) { + try { + const container = await Container.findByPk(containerId); + if (!container) return res.status(404).json({ error: 'Not found' }); + await container.update({ + ipv4Address: req.body.ipv4Address ?? container.ipv4Address, + macAddress: req.body.macAddress ?? container.macAddress, + osRelease: req.body.osRelease ?? container.osRelease + }); + return res.status(200).json({ message: 'Updated' }); + } catch (err) { + console.error('API PUT /sites/:siteId/containers/:id error:', err); + return res.status(500).json({ error: 'Internal server error' }); + } + } const site = await Site.findByPk(siteId); if (!site) { @@ -513,9 +593,21 @@ router.put('/:id', requireAuth, async (req, res) => { }); // DELETE /sites/:siteId/containers/:id - Delete a container -router.delete('/:id', requireAuth, async (req, res) => { +router.delete('/:id', async (req, res) => { const siteId = parseInt(req.params.siteId, 10); const containerId = parseInt(req.params.id, 10); + // If API request, perform lightweight delete and return JSON/204 + if (isApiRequest(req)) { + try { + const container = await Container.findByPk(containerId); + if (!container) return res.status(404).json({ error: 'Not found' }); + await container.destroy(); + return res.status(204).send(); + } catch (err) { + console.error('API DELETE /sites/:siteId/containers/:id error:', err); + return res.status(500).json({ error: 'Internal server error' }); + } + } // Validate site exists const site = await Site.findByPk(siteId); diff --git a/create-a-container/server.js b/create-a-container/server.js index 31a40ad..b66cd33 100644 --- a/create-a-container/server.js +++ b/create-a-container/server.js @@ -103,6 +103,8 @@ async function main() { const groupsRouter = require('./routers/groups'); const sitesRouter = require('./routers/sites'); // Includes nested nodes and containers routers const jobsRouter = require('./routers/jobs'); + // expose API endpoints before HTML routes so they respond at top-level + app.use('/', apiContainersRouter); app.use('/jobs', jobsRouter); app.use('/login', loginRouter); app.use('/register', registerRouter);