Skip to content

Commit 75bbe17

Browse files
committed
Migrate new utilities, fix security issues
Removed isApiRequest from this file and imported it from ../utils/http-utils Applied requireAuth to the GET / route. Refactored GET / to unify the logic. It now runs the same database query for both API and HTML clients, calculating the service ports and statuses, and then switches the output format at the very end. Consolidated the 404 Site Not Found logic in GET /.
1 parent 8fce3ec commit 75bbe17

2 files changed

Lines changed: 48 additions & 40 deletions

File tree

create-a-container/routers/containers.js

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ const { Container, Service, HTTPService, TransportService, DnsService, Node, Sit
66
const { requireAuth } = require('../middlewares');
77
const ProxmoxApi = require('../utils/proxmox-api');
88
const serviceMap = require('../data/services.json');
9+
// Imported from utils as requested
10+
const { isApiRequest } = require('../utils/http-utils');
911

1012
/**
1113
* Normalize a Docker image reference to full format: host/org/image:tag
1214
*/
1315
function normalizeDockerRef(ref) {
1416
// If this looks like a git URL (starts with http/https/git), return as is
15-
// (Though with the fix below, this shouldn't be called for git repos anymore)
1617
if (ref.startsWith('http://') || ref.startsWith('https://') || ref.startsWith('git@')) {
1718
return ref;
1819
}
@@ -54,16 +55,9 @@ function normalizeDockerRef(ref) {
5455
return `${host}/${org}/${image}:${tag}`;
5556
}
5657

57-
// Helper to detect API bearer requests
58-
function isApiRequest(req) {
59-
const accept = (req.get('accept') || '').toLowerCase();
60-
return accept.includes('application/json') || accept.includes('application/vnd.api+json');
61-
}
62-
6358
// GET /sites/:siteId/containers/new - List available templates via API or HTML form
6459
router.get('/new', requireAuth, async (req, res) => {
6560
const siteId = parseInt(req.params.siteId, 10);
66-
// Check if this is an API request (requires the helper function defined earlier)
6761
const isApi = isApiRequest(req);
6862

6963
// verify site exists
@@ -131,48 +125,39 @@ router.get('/new', requireAuth, async (req, res) => {
131125
});
132126

133127
// GET /sites/:siteId/containers
134-
router.get('/', async (req, res) => {
135-
if (isApiRequest(req)) {
136-
try {
137-
const siteId = parseInt(req.params.siteId, 10);
138-
const site = await Site.findByPk(siteId);
139-
if (!site) return res.status(404).json([]);
140-
141-
const nodes = await Node.findAll({ where: { siteId }, attributes: ['id'] });
142-
const nodeIds = nodes.map(n => n.id);
143-
144-
const { hostname } = req.query;
145-
const where = {};
146-
if (hostname) where.hostname = hostname;
147-
where.nodeId = nodeIds;
148-
149-
const containers = await Container.findAll({ where, include: [{ association: 'node', attributes: ['id', 'name'] }] });
150-
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 }));
151-
return res.json(out);
152-
} catch (err) {
153-
console.error('API GET /sites/:siteId/containers error:', err);
154-
return res.status(500).json({ error: 'Internal server error' });
155-
}
156-
}
157-
158-
await new Promise(resolve => requireAuth(req, res, resolve));
159-
if (res.headersSent) return;
160-
128+
// Added requireAuth to ensure API keys and Sessions are validated
129+
router.get('/', requireAuth, async (req, res) => {
161130
const siteId = parseInt(req.params.siteId, 10);
162131
const site = await Site.findByPk(siteId);
132+
133+
// Unified Error Handling for Site 404
163134
if (!site) {
135+
if (isApiRequest(req)) {
136+
return res.status(404).json({ error: 'Site not found' });
137+
}
164138
await req.flash('error', 'Site not found');
165139
return res.redirect('/sites');
166140
}
167141

168142
const nodes = await Node.findAll({ where: { siteId }, attributes: ['id'] });
169143
const nodeIds = nodes.map(n => n.id);
170144

145+
// Determine current user (support Session or API Token via requireAuth)
146+
const currentUser = req.session?.user || req.user?.username;
147+
148+
// Build query
149+
const whereClause = {
150+
username: currentUser,
151+
nodeId: nodeIds
152+
};
153+
154+
// Support hostname filtering for API requests if provided
155+
if (req.query.hostname) {
156+
whereClause.hostname = req.query.hostname;
157+
}
158+
171159
const containers = await Container.findAll({
172-
where: {
173-
username: req.session.user,
174-
nodeId: nodeIds
175-
},
160+
where: whereClause,
176161
include: [
177162
{
178163
association: 'services',
@@ -191,19 +176,28 @@ router.get('/', async (req, res) => {
191176
const sshPort = ssh?.transportService?.externalPort || null;
192177
const http = services.find(s => s.type === 'http');
193178
const httpPort = http ? http.internalPort : null;
179+
180+
// Common object structure for both API and View
194181
return {
195182
id: c.id,
196183
hostname: c.hostname,
197184
ipv4Address: c.ipv4Address,
185+
// API might want raw MacAddress, View might not need it, but including it doesn't hurt
186+
macAddress: c.macAddress,
198187
status: c.status,
199188
template: c.template,
200189
creationJobId: c.creationJobId,
201190
sshPort,
202191
httpPort,
203-
nodeName: c.node ? c.node.name : '-'
192+
nodeName: c.node ? c.node.name : '-',
193+
createdAt: c.createdAt
204194
};
205195
});
206196

197+
if (isApiRequest(req)) {
198+
return res.json({ containers: rows });
199+
}
200+
207201
return res.render('containers/index', { rows, site, req });
208202
});
209203

create-a-container/utils/http.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Helper to detect if a request is an API request based on headers.
3+
* Checks if the client accepts JSON.
4+
* * @param {import('express').Request} req
5+
* @returns {boolean}
6+
*/
7+
function isApiRequest(req) {
8+
const accept = (req.get('accept') || '').toLowerCase();
9+
return accept.includes('application/json') || accept.includes('application/vnd.api+json');
10+
}
11+
12+
module.exports = {
13+
isApiRequest
14+
};

0 commit comments

Comments
 (0)