Skip to content

Commit 56e5ba2

Browse files
authored
Merge pull request #17 from RustyRory/feat/16-update-frontend
feat(service): fixes #16 - maj frontend
2 parents 298a228 + d3d17c2 commit 56e5ba2

10 files changed

Lines changed: 111 additions & 48 deletions

File tree

vps-monitor-app/api/server.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
const express = require('express');
2-
const path = require('path');
3-
const { getContainers } = require('./services/docker');
4-
const { checkWebsites } = require('./services/http');
1+
import 'dotenv/config';
2+
import express from 'express';
3+
import { fileURLToPath } from 'url';
4+
import { dirname, join } from 'path';
5+
import { getContainers } from './services/docker.js';
6+
import { checkWebsites } from './services/http.js';
57

6-
const app = express();
8+
const __dirname = dirname(fileURLToPath(import.meta.url));
79

8-
const PORT = process.env.PORT;
9-
const URI = process.env.URI;
10+
const PORT = process.env.PORT || 3000;
1011
const BASE_URL = process.env.BASE_URL || `http://localhost:${PORT}`;
1112

12-
app.use(express.static(path.join(__dirname, '../public')));
13+
const app = express();
14+
15+
app.use(express.static(join(__dirname, '../public')));
1316

1417
app.get('/api/status', async (req, res) => {
1518
try {
@@ -31,10 +34,10 @@ app.get('/api/status', async (req, res) => {
3134
}
3235
});
3336

34-
module.exports = app;
37+
export default app;
3538

36-
if (require.main === module) {
39+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
3740
app.listen(PORT, () => {
38-
console.log(`vps-monitor listening on port ${URI}:${PORT}`);
41+
console.log(`vps-monitor listening on ${BASE_URL}`);
3942
});
4043
}

vps-monitor-app/api/server.test.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
const request = require('supertest');
1+
import { jest } from '@jest/globals';
22

3-
jest.mock('./services/docker', () => ({
3+
jest.unstable_mockModule('./services/docker.js', () => ({
44
getContainers: jest.fn().mockResolvedValue([
55
{ name: 'app1', status: 'running', image: 'img', ports: ['3000'], uptime: 'Up 1 hour' },
66
]),
77
}));
88

9-
jest.mock('./services/http', () => ({
9+
jest.unstable_mockModule('./services/http.js', () => ({
1010
checkWebsites: jest.fn().mockResolvedValue([
1111
{ name: 'SaintBarth Volley', url: '/saintbarthvolley/', httpCode: 200, status: 'OK' },
1212
]),
1313
}));
1414

15-
const app = require('./server');
15+
const { default: app } = await import('./server.js');
16+
const { default: request } = await import('supertest');
1617

1718
describe('GET /api/status', () => {
1819
it('répond 200 avec la structure attendue', async () => {
Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
const Dockerode = require('dockerode');
1+
import Dockerode from 'dockerode';
22

33
const docker = new Dockerode({ socketPath: '/var/run/docker.sock' });
44

5-
async function getContainers() {
5+
export async function getContainers() {
66
const containers = await docker.listContainers({ all: true });
77

8-
return containers.map((c) => {
9-
return {
10-
name: c.Names[0].replace(/^\//, ''),
11-
status: c.State,
12-
image: c.Image,
13-
ports: [...new Set(c.Ports.map((p) =>
14-
p.PublicPort ? `${p.PublicPort}:${p.PrivatePort}` : `${p.PrivatePort}`
15-
))],
16-
uptime: c.Status,
17-
};
18-
});
8+
return containers.map((c) => ({
9+
name: c.Names[0].replace(/^\//, ''),
10+
status: c.State,
11+
image: c.Image,
12+
ports: [...new Set(c.Ports.map((p) =>
13+
p.PublicPort ? `${p.PublicPort}:${p.PrivatePort}` : `${p.PrivatePort}`
14+
))],
15+
uptime: c.Status,
16+
}));
1917
}
20-
21-
module.exports = { getContainers };
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
const TIMEOUT_MS = 5000;
22

33
const WEBSITES = [
4-
{ name: 'TP Vue', path: '/B3dev-TP_VUE/' },
5-
{ name: 'SaintBarth Volley', path: '/saintbarthvolley/' },
6-
{ name: 'Lucky7', path: '/lucky7/' },
4+
{ name: 'TP Vue', path: '/B3dev-TP_VUE/' },
5+
{ name: 'SaintBarth Volley', path: '/saintbarthvolley/' },
6+
{ name: 'Lucky7', path: '/lucky7/' },
77
{ name: 'College La Boussole', path: '/collegelaboussole/' },
8-
{ name: 'Cinemap', path: '/cinemap/' },
8+
{ name: 'Cinemap', path: '/cinemap/' },
99
];
1010

1111
async function checkWebsite(baseUrl, site) {
@@ -14,12 +14,12 @@ async function checkWebsite(baseUrl, site) {
1414
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
1515

1616
try {
17-
const res = await fetch(url, { signal: controller.signal });
17+
const res = await fetch(url, { signal: controller.signal, redirect: 'manual' });
1818
return {
1919
name: site.name,
2020
url: site.path,
2121
httpCode: res.status,
22-
status: res.status === 200 ? 'OK' : 'DOWN',
22+
status: res.status < 500 ? 'OK' : 'DOWN',
2323
};
2424
} catch {
2525
return {
@@ -33,8 +33,6 @@ async function checkWebsite(baseUrl, site) {
3333
}
3434
}
3535

36-
async function checkWebsites(baseUrl) {
36+
export async function checkWebsites(baseUrl) {
3737
return Promise.all(WEBSITES.map((site) => checkWebsite(baseUrl, site)));
3838
}
39-
40-
module.exports = { checkWebsites };

vps-monitor-app/eslint.config.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
const js = require('@eslint/js');
1+
import js from '@eslint/js';
22

3-
module.exports = [
3+
export default [
44
js.configs.recommended,
55
{
66
files: ['api/**/*.js'],
77
languageOptions: {
8+
sourceType: 'module',
89
globals: {
9-
require: 'readonly',
10-
module: 'readonly',
11-
__dirname: 'readonly',
1210
process: 'readonly',
1311
console: 'readonly',
1412
fetch: 'readonly',
@@ -25,8 +23,8 @@ module.exports = [
2523
{
2624
files: ['api/**/*.test.js'],
2725
languageOptions: {
26+
sourceType: 'module',
2827
globals: {
29-
require: 'readonly',
3028
jest: 'readonly',
3129
describe: 'readonly',
3230
it: 'readonly',

vps-monitor-app/package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vps-monitor-app/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@
22
"name": "vps-monitor-app",
33
"version": "1.0.0",
44
"description": "",
5-
"main": "index.js",
5+
"type": "module",
66
"scripts": {
77
"start": "node api/server.js",
88
"dev": "nodemon api/server.js",
99
"lint": "eslint api/ public/",
10-
"test": "jest"
10+
"test": "node --experimental-vm-modules node_modules/.bin/jest"
11+
},
12+
"jest": {
13+
"testEnvironment": "node",
14+
"transform": {}
1115
},
1216
"keywords": [],
1317
"author": "",
1418
"license": "ISC",
15-
"type": "commonjs",
1619
"dependencies": {
1720
"dockerode": "^5.0.0",
21+
"dotenv": "^17.4.2",
1822
"express": "^5.2.1"
1923
},
2024
"devDependencies": {

vps-monitor-app/public/app.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,46 @@ function renderContainers(containers) {
2222
`).join('');
2323
}
2424

25+
function renderWebsites(websites) {
26+
const el = document.getElementById('websites');
27+
el.innerHTML = websites.map((w) => `
28+
<div class="card">
29+
<div class="card-header">
30+
<span class="card-name">${w.name}</span>
31+
<span class="dot ${w.status === 'OK' ? 'running' : 'exited'}" title="${w.status}"></span>
32+
</div>
33+
<div class="card-meta">
34+
<div>${w.url}</div>
35+
<div>HTTP ${w.httpCode ?? 'timeout'}</div>
36+
</div>
37+
</div>
38+
`).join('');
39+
}
40+
2541
function renderGlobalStatus(status) {
2642
const el = document.getElementById('global-status');
2743
el.textContent = status;
2844
el.className = `badge ${status.toLowerCase()}`;
2945
}
3046

47+
function renderSummary(containers, websites) {
48+
const all = [
49+
...containers.map((c) => c.status === 'running'),
50+
...websites.map((w) => w.status === 'OK'),
51+
];
52+
const ok = all.filter(Boolean).length;
53+
const ko = all.length - ok;
54+
const el = document.getElementById('summary');
55+
el.innerHTML = `<span class="ok-count">${ok} OK</span> / <span class="ko-count">${ko} KO</span>`;
56+
}
57+
3158
async function refresh() {
3259
try {
3360
const data = await fetchStatus();
3461
renderGlobalStatus(data.globalStatus);
62+
renderSummary(data.containers, data.websites);
3563
renderContainers(data.containers);
64+
renderWebsites(data.websites);
3665
} catch {
3766
renderGlobalStatus('KO');
3867
}

vps-monitor-app/public/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@
1010
<header>
1111
<h1>VPS Monitor</h1>
1212
<div id="global-status" class="badge">--</div>
13+
<div id="summary" class="summary"></div>
1314
</header>
1415

1516
<main>
1617
<section>
1718
<h2>Containers Docker</h2>
1819
<div id="containers" class="grid"></div>
1920
</section>
21+
22+
<section>
23+
<h2>Applications web</h2>
24+
<div id="websites" class="grid"></div>
25+
</section>
2026
</main>
2127

2228
<script src="app.js"></script>

vps-monitor-app/public/style.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ header {
1616
align-items: center;
1717
gap: 1rem;
1818
margin-bottom: 2rem;
19+
flex-wrap: wrap;
1920
}
2021

2122
h1 {
@@ -81,3 +82,17 @@ h2 {
8182
color: #666;
8283
line-height: 1.6;
8384
}
85+
86+
.summary {
87+
font-size: 0.85rem;
88+
color: #888;
89+
}
90+
91+
.ok-count { color: #4caf50; }
92+
.ko-count { color: #f44336; }
93+
94+
section {
95+
margin-bottom: 2.5rem;
96+
}
97+
98+
.dot.ok { background: #4caf50; }

0 commit comments

Comments
 (0)