Infra API

This commit is contained in:
Bray 2025-05-30 13:56:32 -04:00
parent 75357d31a2
commit f70bcb53f8
12 changed files with 1632 additions and 7 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
.env
node_modules

5
infra-api/Dockerfile Normal file
View file

@ -0,0 +1,5 @@
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]

View file

@ -0,0 +1,18 @@
version: '3.8'
services:
infra-api:
image: node:18
container_name: infra-api
working_dir: /app
volumes:
- ./:/app
- /var/run/docker.sock:/var/run/docker.sock
command: >
sh -c "npm install && node server.js"
ports:
- "8686:8686"
restart: unless-stopped
environment:
- NODE_ENV=production
network_mode: ${USE_HOST_NETWORK}

View file

View file

1444
infra-api/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

6
infra-api/package.json Normal file
View file

@ -0,0 +1,6 @@
{
"dependencies": {
"dockerode": "^4.0.6",
"express": "^5.1.0"
}
}

View file

@ -0,0 +1,53 @@
const express = require('express');
const router = express.Router();
const { getContainerData } = require('../utils/dockerInspector');
const os = require('os');
router.get('/', async (req, res) => {
try {
const containers = await getContainerData();
const hostInterfaces = os.networkInterfaces();
const ip =
hostInterfaces.en0?.find(i => i.family === 'IPv4')?.address ||
hostInterfaces.eth0?.find(i => i.family === 'IPv4')?.address ||
'127.0.0.1';
const rows = containers
.map(c => {
const [host, container] = c.name.split('-', 2);
if (!host || !container) return null;
const safeContainer = container.replace(/_/g, '');
const safeHost = host.replace(/_/g, '');
const hasPorts = c.ports && c.ports.length > 0;
const url = hasPorts ? `${ip}:${c.ports[0]}` : null;
const domain = `${safeContainer}.${safeHost}.bray.io`;
if (!url) return null;
return {
name: safeContainer,
url,
domain,
};
})
.filter(Boolean);
// Create a pretty text table
const header = `NAME | URL | DOMAIN`;
const divider = `--------------------|------------------------|-----------------------------`;
const lines = rows.map(row =>
`${row.name.padEnd(20)}| ${row.url.padEnd(24)}| ${row.domain}`
);
const output = [header, divider, ...lines].join('\n');
res.setHeader('Content-Type', 'text/plain');
res.send(output);
} catch (err) {
console.error('Error building domain list:', err);
res.status(500).send('Failed to generate domain mappings');
}
});
module.exports = router;

27
infra-api/server.js Normal file
View file

@ -0,0 +1,27 @@
const express = require('express');
const os = require('os');
const { getContainerData } = require('./utils/dockerInspector');
const app = express();
const PORT = process.env.PORT || 8686;
const hostname = os.hostname().toLowerCase(); // used in domain mapping
// Endpoint to list all containers and their info
app.get('/containers', async (req, res) => {
try {
const containers = await getContainerData();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(containers, null, 2)); // Pretty print JSON
} catch (err) {
console.error('Error fetching containers:', err);
res.status(500).send('Internal Server Error');
}
});
const domainRoutes = require('./routes/domains');
app.use('/domains', domainRoutes);
// Start server
app.listen(8686, '0.0.0.0', () => {
console.log('Infra API listening at http://localhost:8686');
});

View file

@ -0,0 +1,69 @@
const Docker = require('dockerode');
const os = require('os');
const docker = new Docker();
// Helper to detect the host IP (e.g. 192.168.x.x)
function getHostIP() {
const interfaces = os.networkInterfaces();
for (const iface of Object.values(interfaces)) {
for (const net of iface) {
if (net.family === 'IPv4' && !net.internal && net.address.startsWith('192.')) {
return net.address;
}
}
}
return 'localhost'; // Fallback
}
async function getContainerData() {
const containers = await docker.listContainers({ all: true });
const hostname = os.hostname();
const hostIp = getHostIP();
const output = [];
for (const containerInfo of containers) {
const container = docker.getContainer(containerInfo.Id);
const inspect = await container.inspect();
const rawName = inspect.Name || containerInfo.Names?.[0] || '';
const name = rawName.replace(/^\//, '');
const networks = inspect.NetworkSettings.Networks || {};
let staticIP = null;
// Try to find a meaningful internal Docker-assigned static IP
for (const netName in networks) {
const ip = networks[netName].IPAddress;
if (ip && (ip.startsWith('192.') || ip.startsWith('10.'))) {
staticIP = ip;
break;
}
}
const rawPorts = Object.values(inspect.NetworkSettings.Ports || {})
.flat()
.filter(Boolean);
const ports = rawPorts.map(p => p.HostPort);
// Prefer static IP if available, fallback to host IP
let url = '(none)';
if (ports.length > 0) {
const accessIP = staticIP || hostIp;
url = `http://${accessIP}:${ports[0]}`;
}
output.push({
name,
ip: staticIP || 'N/A',
ports,
hostname,
url,
});
}
return output;
}
module.exports = { getContainerData };

View file

@ -1,9 +1,10 @@
positions:
/var/log/acroUpdaterTools.log: "16567"
/var/log/alf.log: "0"
/var/log/fsck_apfs.log: "23880"
/var/log/fsck_apfs_error.log: "676"
/var/log/fsck_hfs.log: "4044"
/var/log/install.log: "3800109"
/var/log/install.log: "3856377"
/var/log/shutdown_monitor.log: "1971"
/var/log/system.log: "6132"
/var/log/wifi.log: "1107090"
/var/log/system.log: "6579"
/var/log/wifi.log: "1164834"

View file

@ -1,9 +1,10 @@
positions:
/var/log/acroUpdaterTools.log: "16567"
/var/log/alf.log: "0"
/var/log/fsck_apfs.log: "23880"
/var/log/fsck_apfs_error.log: "676"
/var/log/fsck_hfs.log: "4044"
/var/log/install.log: "3800109"
/var/log/install.log: "3856377"
/var/log/shutdown_monitor.log: "1971"
/var/log/system.log: "6132"
/var/log/wifi.log: "1107090"
/var/log/system.log: "6579"
/var/log/wifi.log: "1164834"