import os from pathlib import Path from dotenv import load_dotenv from flask import Flask, jsonify, request, render_template, redirect, url_for, flash from classes import Equipment, Interface from database import ( init_database, reset_database, prompt_reset, fetch_all_equipments, fetch_equipment_by_id, insert_equipment, update_equipment, delete_equipment, ) load_dotenv() app = Flask(__name__) def _required_env(key: str) -> str: value = os.environ.get(key) if not value: raise RuntimeError( f"Variable d'environnement {key!r} manquante. " f"Vérifiez votre fichier .env (voir .env.example)." ) return value app.config["SECRET_KEY"] = _required_env("FLASK_SECRET_KEY") app.config["DEBUG"] = os.environ.get("FLASK_DEBUG", "False").lower() in {"1", "true", "yes"} app.config["DATABASE_PATH"] = os.environ.get("DATABASE_PATH", "databases/inventory.db") ALLOWED_TYPES = {"Server", "Instance", "Switch", "Gateway", "Router", "WAF", "Database"} def _parse_form_equipment(form) -> tuple[Equipment | None, str | None]: hostname = (form.get("hostname") or "").strip() if not hostname: return None, "Hostname requis." eq_type = form.get("type", "Server") if eq_type not in ALLOWED_TYPES: return None, f"Type invalide : {eq_type}." ip = (form.get("ip_address") or "").strip() or "255.255.255.0" is_active = form.get("is_active") == "on" latency_raw = (form.get("latency") or "").strip() latency = None if latency_raw: try: latency = float(latency_raw) if latency < 0: return None, "Latence doit être positive." except ValueError: return None, "Latence invalide." ports = form.getlist("ports[]") services = form.getlist("services[]") interfaces: list[Interface] = [] for port_raw, service in zip(ports, services): if not port_raw: continue try: port = int(port_raw) if not (0 <= port <= 65535): return None, f"Port invalide : {port_raw}." except ValueError: return None, f"Port non numérique : {port_raw}." if not service.strip(): return None, "Service requis pour chaque interface." interfaces.append(Interface(port=port, service=service.strip())) return Equipment( hostname=hostname, type=eq_type, ip_address=ip, is_active=is_active, latency=latency, interfaces=interfaces, ), None def _equipment_to_dict(eq: Equipment) -> dict: return { "id": eq.id, "hostname": eq.hostname, "type": eq.type, "ip_address": eq.ip_address, "is_active": eq.is_active, "latency": eq.latency, "interfaces": [ {"id": i.id, "port": i.port, "service": i.service} for i in eq.interfaces ], } # Partie FRONT @app.route('/') def index(): return render_template('index.html') @app.route('/equipments', methods=['GET']) def equipments_page(): equipments = fetch_all_equipments() return render_template('equipments.html', equipments=equipments, types=sorted(ALLOWED_TYPES)) @app.route('/equipments', methods=['POST']) def create_equipment_form(): eq, err = _parse_form_equipment(request.form) if err: flash(err, "error") return redirect(url_for('equipments_page')) insert_equipment(eq) flash(f"Équipement « {eq.hostname} » créé.", "success") return redirect(url_for('equipments_page')) @app.route('/equipments//toggle', methods=['POST']) def toggle_equipment(equipment_id: int): eq = fetch_equipment_by_id(equipment_id) if eq is None: flash(f"Équipement {equipment_id} introuvable.", "error") return redirect(url_for('equipments_page')) eq.is_active = not eq.is_active update_equipment(eq) flash(f"État de « {eq.hostname} » mis à jour.", "success") return redirect(url_for('equipments_page')) @app.route('/equipments//delete', methods=['POST']) def delete_equipment_form(equipment_id: int): eq = fetch_equipment_by_id(equipment_id) if eq is None: flash(f"Équipement {equipment_id} introuvable.", "error") return redirect(url_for('equipments_page')) delete_equipment(equipment_id) flash(f"Équipement « {eq.hostname} » supprimé.", "success") return redirect(url_for('equipments_page')) # Partie API @app.get("/api/equipments") def list_equipments_api(): return jsonify([_equipment_to_dict(eq) for eq in fetch_all_equipments()]) @app.get("/api/equipments/") def get_equipment_api(equipment_id: int): eq = fetch_equipment_by_id(equipment_id) if eq is None: return jsonify({"error": "Équipement introuvable."}), 404 return jsonify(_equipment_to_dict(eq)) @app.post("/api/equipments") def create_equipment_api(): data = request.get_json(silent=True) or {} hostname = (data.get("hostname") or "").strip() if not hostname: return jsonify({"error": "Hostname requis."}), 400 eq_type = data.get("type", "Server") if eq_type not in ALLOWED_TYPES: return jsonify({"error": f"Type invalide : {eq_type}."}), 400 eq = Equipment( hostname=hostname, type=eq_type, ip_address=(data.get("ip_address") or "255.255.255.0").strip(), is_active=bool(data.get("is_active", True)), latency=data.get("latency"), interfaces=[], ) new_id = insert_equipment(eq) eq.id = new_id return jsonify(_equipment_to_dict(eq)), 201 @app.put("/api/equipments/") def update_equipment_api(equipment_id: int): eq = fetch_equipment_by_id(equipment_id) if eq is None: return jsonify({"error": f"Équipement {equipment_id} introuvable."}), 404 data = request.get_json(silent=True) or {} hostname = (data.get("hostname") or "").strip() if not hostname: return jsonify({"error": "Hostname requis."}), 400 eq_type = data.get("type", "Server") if eq_type not in ALLOWED_TYPES: return jsonify({"error": f"Type invalide : {eq_type}."}), 400 latency = data.get("latency") if latency is not None: try: latency = float(latency) if latency < 0: return jsonify({"error": "Latence doit être positive."}), 400 except (ValueError, TypeError): return jsonify({"error": "Latence invalide."}), 400 eq.hostname = hostname eq.type = eq_type eq.ip_address = (data.get("ip_address") or "255.255.255.0").strip() eq.is_active = bool(data.get("is_active", True)) eq.latency = latency update_equipment(eq) return jsonify(_equipment_to_dict(eq)), 200 @app.patch("/api/equipments//toggle") def toggle_equipment_api(equipment_id: int): eq = fetch_equipment_by_id(equipment_id) if eq is None: return jsonify({"error": f"Équipement {equipment_id} introuvable."}), 404 eq.is_active = not eq.is_active update_equipment(eq) return jsonify(_equipment_to_dict(eq)), 200 @app.delete("/api/equipments/") def delete_equipment_api(equipment_id: int): eq = fetch_equipment_by_id(equipment_id) if eq is None: return jsonify({"error": f"Équipement {equipment_id} introuvable."}), 404 delete_equipment(equipment_id) return jsonify({"message": f"Équipement « {eq.hostname} » supprimé."}), 200 if __name__ == '__main__': host = os.environ.get("FLASK_HOST", "127.0.0.1") port = int(os.environ.get("FLASK_PORT", "5000")) if prompt_reset(): reset_database() print("Base réinitialisée.") else: init_database() app.run(host=host, port=port, debug=app.config["DEBUG"])