feat: live Docker data — containers, services, logs, metrics via dockerode
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- Backend: rewrote containers/services/logs/metrics routes to use dockerode - docker-compose: mount /var/run/docker.sock, run backend as root - .drone.yml: sync docker-compose.yml from Gitea on deploy - Frontend: Containers page shows real data with wired start/stop/restart - Frontend: Services page shows Docker Compose stacks with health status - Frontend: Metrics page adds disk (docker df) and containers cards + chart legend - Frontend: Logs page replaces text input with container dropdown + auto-refresh
This commit is contained in:
@@ -1,55 +1,66 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import Docker from 'dockerode';
|
||||
|
||||
const router = Router();
|
||||
const docker = new Docker({ socketPath: '/var/run/docker.sock' });
|
||||
|
||||
// GET /api/services - List all services
|
||||
router.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// TODO: Implement service listing (systemd, docker compose, etc.)
|
||||
res.json({ services: [] });
|
||||
const list = await docker.listContainers({ all: true });
|
||||
|
||||
const projects = new Map<string, any[]>();
|
||||
const standalone: any[] = [];
|
||||
|
||||
for (const c of list) {
|
||||
const project = c.Labels?.['com.docker.compose.project'];
|
||||
const service = c.Labels?.['com.docker.compose.service'];
|
||||
if (project && service) {
|
||||
if (!projects.has(project)) projects.set(project, []);
|
||||
projects.get(project)!.push({
|
||||
name: service,
|
||||
container: c.Names[0]?.replace(/^\//, ''),
|
||||
state: c.State,
|
||||
status: c.Status,
|
||||
image: c.Image,
|
||||
});
|
||||
} else {
|
||||
standalone.push({
|
||||
name: c.Names[0]?.replace(/^\//, '') ?? c.Id.slice(0, 12),
|
||||
container: c.Names[0]?.replace(/^\//, ''),
|
||||
state: c.State,
|
||||
status: c.Status,
|
||||
image: c.Image,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const stacks = [
|
||||
...Array.from(projects.entries()).map(([project, svcs]) => ({
|
||||
id: project,
|
||||
name: project,
|
||||
type: 'compose',
|
||||
services: svcs,
|
||||
running: svcs.filter(s => s.state === 'running').length,
|
||||
total: svcs.length,
|
||||
status: svcs.every(s => s.state === 'running') ? 'healthy'
|
||||
: svcs.some(s => s.state === 'running') ? 'degraded' : 'stopped',
|
||||
})),
|
||||
...(standalone.length > 0 ? [{
|
||||
id: 'standalone',
|
||||
name: 'standalone',
|
||||
type: 'standalone',
|
||||
services: standalone,
|
||||
running: standalone.filter(s => s.state === 'running').length,
|
||||
total: standalone.length,
|
||||
status: 'unknown',
|
||||
}] : []),
|
||||
];
|
||||
|
||||
res.json({ services: stacks });
|
||||
} catch (error) {
|
||||
console.error('Services error:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch services' });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/services/:name - Get service status
|
||||
router.get('/:name', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// TODO: Implement service status check
|
||||
res.json({ name: req.params.name, status: 'unknown' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch service status' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/services/:name/start - Start service
|
||||
router.post('/:name/start', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// TODO: Implement service start
|
||||
res.json({ status: 'started' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to start service' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/services/:name/stop - Stop service
|
||||
router.post('/:name/stop', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// TODO: Implement service stop
|
||||
res.json({ status: 'stopped' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to stop service' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/services/:name/restart - Restart service
|
||||
router.post('/:name/restart', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// TODO: Implement service restart
|
||||
res.json({ status: 'restarted' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to restart service' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user