docs: add French and Spanish translations of README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 10:11:04 +02:00
parent 74fcc6d6e7
commit 5828f6c46b
2 changed files with 462 additions and 0 deletions
+231
View File
@@ -0,0 +1,231 @@
# <img src="frontend/public/favicon.svg" width="22" height="22" alt="" /> Stupid Simple Network Inventory
![Python](https://img.shields.io/badge/Python-3.11-3776AB?logo=python&logoColor=white)
![FastAPI](https://img.shields.io/badge/FastAPI-009688?logo=fastapi&logoColor=white)
![Vue.js](https://img.shields.io/badge/Vue.js-3-4FC08D?logo=vue.js&logoColor=white)
![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite&logoColor=white)
![Nginx](https://img.shields.io/badge/Nginx-009639?logo=nginx&logoColor=white)
![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white)
Aplicación web autoalojada para inventario manual de red y visualización de topología de red lógica.
![Vista de Topología](img/tpology.png)
## Características
- **Inventario manual** — añade y gestiona dispositivos (18 tipos) con IPs, VLANs, descripciones y enlaces web opcionales
- **Vista de topología** — disposición en tarjetas por red (LAN / VLAN 802.1Q), con secciones WAN y puerta de enlace
- **Ping ICMP** — comprueba la accesibilidad de todos los hosts conocidos con un clic
- **Descubrimiento automático** — ping sweep + consulta PTR DNS en una subred para importar nuevos hosts
- **Autenticación** — inicio de sesión JWT con cambio de contraseña obligatorio en el primer uso
- **Modo oscuro** — alternancia entre tema claro y oscuro
- **i18n** — francés, inglés, español
## Stack
| Capa | Tecnología |
|------|-----------|
| Backend | FastAPI + SQLAlchemy + SQLite (Python 3.11) |
| Frontend | Vue 3 + Vite, servido por Nginx |
| Auth | JWT HS256, expiración 24h |
| Runtime | Docker Compose |
---
## Inicio rápido
```bash
# 1. Clonar y entrar en el proyecto
git clone https://git.raspot.in/olivier/stupid-simple-network-inventory.git
cd stupid-simple-network-inventory
# 2. Crear el directorio de datos propiedad del usuario actual
mkdir -p db_data
# 3. Configurar el entorno (necesario para la propiedad correcta del bind-mount)
cp .env.example .env
# Editar .env:
# DOCKER_UID / DOCKER_GID → resultado de: id -u && id -g
# INITIAL_ADMIN_PASSWORD → definir para evitar el bootstrap admin/admin por defecto
# 4. Construir e iniciar
docker compose --env-file .env up --build -d
# 5. Abrir http://localhost:8080 en el navegador
```
### Primer inicio de sesión
| Caso | Credenciales | Comportamiento |
|------|-------------|----------------|
| `INITIAL_ADMIN_PASSWORD` definido | `admin` / `<tu contraseña>` | Inicio de sesión normal |
| `INITIAL_ADMIN_PASSWORD` no definido | `admin` / `admin` | Cambio de contraseña obligatorio antes de acceder a la aplicación |
---
## Configuración
Toda la configuración se realiza mediante variables de entorno. Ver `.env.example` para la lista completa con descripciones.
| Variable | Por defecto | Descripción |
|----------|-------------|-------------|
| `SECRET_KEY` | auto-generada | Clave de firma JWT. Definir explícitamente en producción. |
| `INITIAL_ADMIN_PASSWORD` | _(vacío)_ | Contraseña de bootstrap del admin. Si no se define, se usa `admin/admin` con cambio forzado. |
| `ALLOWED_ORIGINS` | `*` | Orígenes CORS permitidos (separados por comas). Definir en tu dominio en producción. |
| `BIND_ADDRESS` | `0.0.0.0` | Dirección IP de escucha. Definir en la interfaz frente al reverse proxy. |
| `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID para el proceso backend. Debe coincidir con el usuario propietario de `./db_data/`. |
### Usar .env con Docker Compose
```bash
cp .env.example .env
# Editar .env — definir como mínimo DOCKER_UID, DOCKER_GID, INITIAL_ADMIN_PASSWORD
docker compose --env-file .env up --build -d
```
---
## Seguridad
### Gestión de secretos
Dos opciones según sus requisitos de seguridad.
#### Opción A — Secreto auto-generado (recomendado para nodo único)
Dejar `SECRET_KEY` sin definir (o vacío) en `.env`. En el primer arranque, el backend genera una clave hexadecimal aleatoria de 64 caracteres, la escribe en `db_data/secret_key.txt` con permisos **0600** y la reutiliza en cada reinicio posterior. El secreto nunca aparece en una variable de entorno, un archivo compose o un log.
```bash
# .env — dejar la línea vacía o eliminarla
SECRET_KEY=
```
El único requisito es que `db_data/` esté respaldado (ya contiene la base de datos).
#### Opción B — Secreto mediante archivo Docker Compose
Almacena el secreto en un archivo en el host, fuera del control de versiones, y lo monta en el contenedor. El valor nunca aparece en una variable de entorno.
```bash
# Generar y almacenar el secreto fuera del directorio del proyecto
mkdir -p ~/.secrets
python3 -c "import secrets; print(secrets.token_hex(32))" > ~/.secrets/topologie_secret_key
chmod 600 ~/.secrets/topologie_secret_key
```
Luego descomentar los bloques `secrets:` en `docker-compose.yml` (ver los comentarios en ese archivo) y eliminar `SECRET_KEY` de `.env`. Docker Compose fusiona la anulación automáticamente:
```bash
docker compose up -d
```
---
### Rotación de clave
Para rotar el secreto JWT (invalida todas las sesiones activas):
```bash
# Opción A — variable de entorno (recomendado)
# Definir un nuevo SECRET_KEY en la configuración de despliegue y reiniciar
# Opción B — rotación por archivo
docker compose stop backend
rm db_data/secret_key.txt
docker compose start backend
# Todos los usuarios deberán iniciar sesión de nuevo
```
### HTTPS
Esta aplicación no termina TLS. Para uso en producción, colócala detrás de un reverse proxy que gestione HTTPS:
```nginx
# Ejemplo de reverse proxy nginx (externo, en el host o en un contenedor dedicado)
server {
listen 443 ssl;
server_name inventory.example.com;
ssl_certificate /etc/letsencrypt/live/inventory.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/inventory.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Para uso solo local, enlazar a loopback para evitar exposición accidental en la LAN:
```yaml
# docker-compose.override.yml
services:
frontend:
ports:
- "127.0.0.1:8080:8080"
```
### Endurecimiento de contenedores
Los contenedores se ejecutan con privilegios reducidos:
| Medida | Backend | Frontend |
|--------|---------|----------|
| Usuario no-root | `DOCKER_UID:DOCKER_GID` (usuario host) | `nginx` (UID 101) |
| `cap_drop: ALL` | ✓ | ✓ |
| `cap_add: NET_RAW` | ✓ (ping) | — |
| `no-new-privileges` | — ¹ | ✓ |
| Healthcheck | ✓ | ✓ |
¹ Omitido en el backend: el ping usa file capabilities (`cap_net_raw=ep`); `no-new-privileges` suprimiría el bit efectivo del archivo e impediría al subproceso adquirir `CAP_NET_RAW` en su conjunto efectivo aunque el padre lo tenga en su conjunto permitido.
---
## Persistencia de datos
Todos los datos se almacenan en `./db_data/`:
| Archivo | Descripción |
|---------|-------------|
| `topology.db` | Base de datos SQLite |
| `secret_key.txt` | Secreto JWT auto-generado (permisos 0600) |
**Copia de seguridad**: `cp -r db_data/ db_data.bak/`
**Restauración**: detener la stack, reemplazar `db_data/`, reiniciar.
---
## Desarrollo
### Tests del backend
```bash
cd backend
pip install -r requirements.txt -r requirements-test.txt
pytest tests/ -v
```
### Dev local (sin Docker)
```bash
# Backend
cd backend
pip install -r requirements.txt
uvicorn main:app --reload
# Frontend (terminal separada)
cd frontend
npm install
npm run dev # Servidor dev Vite en :5173, proxifica /api/ hacia :8000
```
---
## Arquitectura
Ver [`docs/architecture.md`](docs/architecture.md) para el flujo de solicitudes detallado, la configuración Docker y el modelo de autenticación.
+231
View File
@@ -0,0 +1,231 @@
# <img src="frontend/public/favicon.svg" width="22" height="22" alt="" /> Stupid Simple Network Inventory
![Python](https://img.shields.io/badge/Python-3.11-3776AB?logo=python&logoColor=white)
![FastAPI](https://img.shields.io/badge/FastAPI-009688?logo=fastapi&logoColor=white)
![Vue.js](https://img.shields.io/badge/Vue.js-3-4FC08D?logo=vue.js&logoColor=white)
![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite&logoColor=white)
![Nginx](https://img.shields.io/badge/Nginx-009639?logo=nginx&logoColor=white)
![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white)
Application web auto-hébergée pour l'inventaire manuel de réseau et la visualisation de topologie réseau logique.
![Vue Topologie](img/tpology.png)
## Fonctionnalités
- **Inventaire manuel** — ajout et gestion d'équipements (18 types) avec IPs, VLANs, descriptions et liens web optionnels
- **Vue topologie** — disposition en cards par réseau (LAN / VLAN 802.1Q), avec sections WAN et passerelle
- **Ping ICMP** — vérification de l'accessibilité de tous les hôtes connus en un clic
- **Découverte automatique** — ping sweep + lookup PTR DNS sur un sous-réseau pour importer de nouveaux hôtes
- **Authentification** — connexion JWT avec changement de mot de passe forcé à la première utilisation
- **Mode sombre** — bascule thème clair / sombre
- **i18n** — français, anglais, espagnol
## Stack
| Couche | Technologie |
|--------|------------|
| Backend | FastAPI + SQLAlchemy + SQLite (Python 3.11) |
| Frontend | Vue 3 + Vite, servi par Nginx |
| Auth | JWT HS256, expiration 24h |
| Runtime | Docker Compose |
---
## Démarrage rapide
```bash
# 1. Cloner et entrer dans le projet
git clone https://git.raspot.in/olivier/stupid-simple-network-inventory.git
cd stupid-simple-network-inventory
# 2. Créer le répertoire de données appartenant à l'utilisateur courant
mkdir -p db_data
# 3. Configurer l'environnement (requis pour la propriété correcte du bind-mount)
cp .env.example .env
# Éditer .env :
# DOCKER_UID / DOCKER_GID → résultat de : id -u && id -g
# INITIAL_ADMIN_PASSWORD → à définir pour éviter le bootstrap admin/admin par défaut
# 4. Construire et démarrer
docker compose --env-file .env up --build -d
# 5. Ouvrir http://localhost:8080 dans votre navigateur
```
### Première connexion
| Cas | Identifiants | Comportement |
|-----|-------------|--------------|
| `INITIAL_ADMIN_PASSWORD` défini | `admin` / `<votre mot de passe>` | Connexion normale |
| `INITIAL_ADMIN_PASSWORD` non défini | `admin` / `admin` | Changement de mot de passe forcé avant d'accéder à l'application |
---
## Configuration
Toute la configuration se fait via des variables d'environnement. Voir `.env.example` pour la liste complète avec descriptions.
| Variable | Défaut | Description |
|----------|--------|-------------|
| `SECRET_KEY` | auto-générée | Clé de signature JWT. À définir explicitement en production. |
| `INITIAL_ADMIN_PASSWORD` | _(vide)_ | Mot de passe admin de bootstrap. Si non défini, `admin/admin` est utilisé avec changement forcé. |
| `ALLOWED_ORIGINS` | `*` | Origines CORS autorisées (séparées par des virgules). À définir sur votre domaine en production. |
| `BIND_ADDRESS` | `0.0.0.0` | Adresse IP d'écoute. À définir sur l'interface face au reverse proxy. |
| `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID pour le processus backend. Doit correspondre à l'utilisateur propriétaire de `./db_data/`. |
### Utilisation de .env avec Docker Compose
```bash
cp .env.example .env
# Éditer .env — définir au minimum DOCKER_UID, DOCKER_GID, INITIAL_ADMIN_PASSWORD
docker compose --env-file .env up --build -d
```
---
## Sécurité
### Gestion des secrets
Deux options selon vos exigences de sécurité.
#### Option A — Secret auto-généré (recommandé pour un nœud unique)
Laisser `SECRET_KEY` non défini (ou vide) dans `.env`. Au premier démarrage, le backend génère une clé hexadécimale aléatoire de 64 caractères, l'écrit dans `db_data/secret_key.txt` avec les permissions **0600**, et la réutilise à chaque redémarrage. Le secret n'apparaît jamais dans une variable d'environnement, un fichier compose ou un log.
```bash
# .env — laisser la ligne vide ou la supprimer
SECRET_KEY=
```
La seule exigence est que `db_data/` soit sauvegardé (il contient déjà la base de données).
#### Option B — Secret via fichier Docker Compose
Stocke le secret dans un fichier sur l'hôte, hors du contrôle de version, et le monte dans le conteneur. La valeur n'apparaît jamais dans une variable d'environnement.
```bash
# Générer et stocker le secret en dehors du répertoire du projet
mkdir -p ~/.secrets
python3 -c "import secrets; print(secrets.token_hex(32))" > ~/.secrets/topologie_secret_key
chmod 600 ~/.secrets/topologie_secret_key
```
Décommenter ensuite les blocs `secrets:` dans `docker-compose.yml` (voir les commentaires dans ce fichier) et supprimer `SECRET_KEY` de `.env`. Docker Compose fusionne la surcharge automatiquement :
```bash
docker compose up -d
```
---
### Rotation de clé
Pour faire tourner le secret JWT (invalide toutes les sessions actives) :
```bash
# Option A — variable d'environnement (recommandé)
# Définir un nouveau SECRET_KEY dans la configuration de déploiement et redémarrer
# Option B — rotation par fichier
docker compose stop backend
rm db_data/secret_key.txt
docker compose start backend
# Tous les utilisateurs devront se reconnecter
```
### HTTPS
Cette application ne termine pas TLS. Pour un usage en production, placez-la derrière un reverse proxy gérant HTTPS :
```nginx
# Exemple de reverse proxy nginx (externe, sur l'hôte ou un conteneur dédié)
server {
listen 443 ssl;
server_name inventory.example.com;
ssl_certificate /etc/letsencrypt/live/inventory.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/inventory.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Pour un usage local uniquement, lier à la boucle locale pour éviter une exposition LAN accidentelle :
```yaml
# docker-compose.override.yml
services:
frontend:
ports:
- "127.0.0.1:8080:8080"
```
### Durcissement des conteneurs
Les conteneurs s'exécutent avec des privilèges réduits :
| Mesure | Backend | Frontend |
|--------|---------|----------|
| Utilisateur non-root | `DOCKER_UID:DOCKER_GID` (utilisateur hôte) | `nginx` (UID 101) |
| `cap_drop: ALL` | ✓ | ✓ |
| `cap_add: NET_RAW` | ✓ (ping) | — |
| `no-new-privileges` | — ¹ | ✓ |
| Healthcheck | ✓ | ✓ |
¹ Omis sur le backend : le ping utilise les file capabilities (`cap_net_raw=ep`) ; `no-new-privileges` supprimerait le bit effectif du fichier et empêcherait le sous-processus d'acquérir `CAP_NET_RAW` dans son ensemble effectif même si le parent le détient dans son ensemble permis.
---
## Persistance des données
Toutes les données sont stockées dans `./db_data/` :
| Fichier | Description |
|---------|-------------|
| `topology.db` | Base de données SQLite |
| `secret_key.txt` | Secret JWT auto-généré (permissions 0600) |
**Sauvegarde** : `cp -r db_data/ db_data.bak/`
**Restauration** : arrêter la stack, remplacer `db_data/`, redémarrer.
---
## Développement
### Tests backend
```bash
cd backend
pip install -r requirements.txt -r requirements-test.txt
pytest tests/ -v
```
### Dev local (sans Docker)
```bash
# Backend
cd backend
pip install -r requirements.txt
uvicorn main:app --reload
# Frontend (terminal séparé)
cd frontend
npm install
npm run dev # Serveur dev Vite sur :5173, proxifie /api/ vers :8000
```
---
## Architecture
Voir [`docs/architecture.md`](docs/architecture.md) pour le flux de requêtes détaillé, la configuration Docker et le modèle d'authentification.