bla
Some checks failed
Build and Push Docker Images / docker (push) Failing after 10s

This commit is contained in:
jeanotx32
2026-05-18 23:14:26 -04:00
parent a93d11b718
commit bb5a7005a7
7 changed files with 106 additions and 35 deletions

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
# Python
__pycache__/
*.py[cod]
*.pyo
.env
# Node
node_modules/
dist/
.DS_Store
# Data persistante — SQLite & secrets (ne pas committer)
vps-monitor/data/*.db
vps-monitor/data/.jwt_secret
# Anciens fichiers JSON de données
vps-monitor/backend/data/

View File

@@ -1,2 +1,2 @@
97732 1456
97787 1540

View File

@@ -1 +1,10 @@
[] [
{
"id": "test-1",
"name": "test",
"host": "45.145.167.66",
"port": 8001,
"api_key": "1fbbcb4998f99ab74c88e5076355e99531a76eb7c2e34b82318b7c334900a848",
"description": ""
}
]

View File

@@ -5,9 +5,10 @@ Agrège les données de tous les agents et expose une API REST pour le frontend.
""" """
import asyncio import asyncio
import json
import os import os
import secrets import secrets
import sqlite3
from contextlib import contextmanager
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from typing import Annotated from typing import Annotated
@@ -22,18 +23,13 @@ from pydantic import BaseModel
# ─── Config ─────────────────────────────────────────────────────────────────── # ─── Config ───────────────────────────────────────────────────────────────────
CONFIG_FILE = Path(os.getenv("CONFIG_FILE", "data/vps.json")) DB_FILE = Path(os.getenv("DB_FILE", "data/monitor.db"))
USERS_FILE = Path(os.getenv("USERS_FILE", "data/users.json"))
SECRET_FILE = Path(os.getenv("SECRET_FILE", "data/.jwt_secret")) SECRET_FILE = Path(os.getenv("SECRET_FILE", "data/.jwt_secret"))
AGENT_TIMEOUT = int(os.getenv("AGENT_TIMEOUT", "5")) AGENT_TIMEOUT = int(os.getenv("AGENT_TIMEOUT", "5"))
JWT_ALGORITHM = "HS256" JWT_ALGORITHM = "HS256"
JWT_EXPIRE_MIN = int(os.getenv("JWT_EXPIRE_MINUTES", "1440")) # 24 h JWT_EXPIRE_MIN = int(os.getenv("JWT_EXPIRE_MINUTES", "1440")) # 24 h
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) DB_FILE.parent.mkdir(parents=True, exist_ok=True)
if not CONFIG_FILE.exists():
CONFIG_FILE.write_text("[]")
if not USERS_FILE.exists():
USERS_FILE.write_text("[]")
# Clé JWT : env var > fichier persisté > génération + sauvegarde # Clé JWT : env var > fichier persisté > génération + sauvegarde
def _load_jwt_secret() -> str: def _load_jwt_secret() -> str:
@@ -76,22 +72,78 @@ class LoginRequest(BaseModel):
password: str password: str
# ─── SQLite ───────────────────────────────────────────────────────────────────
@contextmanager
def get_db():
conn = sqlite3.connect(DB_FILE, check_same_thread=False)
conn.row_factory = sqlite3.Row
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
def init_db() -> None:
with get_db() as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS users (
username TEXT PRIMARY KEY,
password TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'admin'
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS vps (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
host TEXT NOT NULL,
port INTEGER NOT NULL DEFAULT 8001,
api_key TEXT NOT NULL,
description TEXT NOT NULL DEFAULT ''
)
""")
init_db()
# ─── Persistance ────────────────────────────────────────────────────────────── # ─── Persistance ──────────────────────────────────────────────────────────────
def load_vps() -> list[dict]:
return json.loads(CONFIG_FILE.read_text())
def save_vps(data: list[dict]) -> None:
CONFIG_FILE.write_text(json.dumps(data, indent=2))
def load_users() -> list[dict]: def load_users() -> list[dict]:
return json.loads(USERS_FILE.read_text()) with get_db() as conn:
return [dict(r) for r in conn.execute("SELECT * FROM users").fetchall()]
def save_users(data: list[dict]) -> None: def add_user(user: dict) -> None:
USERS_FILE.write_text(json.dumps(data, indent=2)) with get_db() as conn:
conn.execute(
"INSERT INTO users (username, password, role) VALUES (?, ?, ?)",
(user["username"], user["password"], user["role"]),
)
def load_vps() -> list[dict]:
with get_db() as conn:
return [dict(r) for r in conn.execute("SELECT * FROM vps").fetchall()]
def insert_vps(vps: dict) -> None:
with get_db() as conn:
conn.execute(
"INSERT INTO vps (id, name, host, port, api_key, description) VALUES (?, ?, ?, ?, ?, ?)",
(vps["id"], vps["name"], vps["host"], vps["port"], vps["api_key"], vps.get("description", "")),
)
def remove_vps(vps_id: str) -> bool:
with get_db() as conn:
cur = conn.execute("DELETE FROM vps WHERE id = ?", (vps_id,))
return cur.rowcount > 0
# ─── Auth helpers ───────────────────────────────────────────────────────────── # ─── Auth helpers ─────────────────────────────────────────────────────────────
@@ -189,8 +241,7 @@ def auth_status():
@app.post("/api/auth/register", status_code=201) @app.post("/api/auth/register", status_code=201)
def register(body: RegisterRequest): def register(body: RegisterRequest):
"""Enregistre le premier utilisateur (admin). Fermé ensuite.""" """Enregistre le premier utilisateur (admin). Fermé ensuite."""
users = load_users() if len(load_users()) > 0:
if len(users) > 0:
raise HTTPException( raise HTTPException(
status_code=403, status_code=403,
detail="L'enregistrement public est désactivé. Seul l'admin peut créer des comptes." detail="L'enregistrement public est désactivé. Seul l'admin peut créer des comptes."
@@ -202,8 +253,7 @@ def register(body: RegisterRequest):
"password": _bcrypt.hashpw(body.password.encode(), _bcrypt.gensalt()).decode(), "password": _bcrypt.hashpw(body.password.encode(), _bcrypt.gensalt()).decode(),
"role": "admin", "role": "admin",
} }
users.append(user) add_user(user)
save_users(users)
token = create_token(user["username"], user["role"]) token = create_token(user["username"], user["role"])
return {"access_token": token, "token_type": "bearer", "role": user["role"]} return {"access_token": token, "token_type": "bearer", "role": user["role"]}
@@ -238,22 +288,17 @@ def list_vps(_: Annotated[dict, Depends(get_current_user)]):
@app.post("/api/vps", status_code=201) @app.post("/api/vps", status_code=201)
def add_vps(vps: VpsConfig, _: Annotated[dict, Depends(get_current_user)]): def add_vps(vps: VpsConfig, _: Annotated[dict, Depends(get_current_user)]):
"""Ajoute un nouveau VPS.""" """Ajoute un nouveau VPS."""
data = load_vps() if any(v["id"] == vps.id for v in load_vps()):
if any(v["id"] == vps.id for v in data):
raise HTTPException(status_code=409, detail="Un VPS avec cet ID existe déjà") raise HTTPException(status_code=409, detail="Un VPS avec cet ID existe déjà")
data.append(vps.model_dump()) insert_vps(vps.model_dump())
save_vps(data)
return {"status": "ok", "id": vps.id} return {"status": "ok", "id": vps.id}
@app.delete("/api/vps/{vps_id}") @app.delete("/api/vps/{vps_id}")
def delete_vps(vps_id: str, _: Annotated[dict, Depends(get_current_user)]): def delete_vps(vps_id: str, _: Annotated[dict, Depends(get_current_user)]):
"""Supprime un VPS de la configuration.""" """Supprime un VPS de la configuration."""
data = load_vps() if not remove_vps(vps_id):
filtered = [v for v in data if v["id"] != vps_id]
if len(filtered) == len(data):
raise HTTPException(status_code=404, detail="VPS introuvable") raise HTTPException(status_code=404, detail="VPS introuvable")
save_vps(filtered)
return {"status": "ok"} return {"status": "ok"}

View File

View File

@@ -4,7 +4,7 @@ services:
ports: ports:
- "8000:8000" - "8000:8000"
volumes: volumes:
- ./backend/data:/app/data - ./data:/app/data
env_file: ./backend/.env env_file: ./backend/.env
restart: unless-stopped restart: unless-stopped