This commit is contained in:
2025-12-07 01:16:52 +01:00
parent 13b3c58ec8
commit 78e6909405
22 changed files with 4664 additions and 217 deletions

View File

@@ -33,6 +33,7 @@ class ScanConfig(BaseModel):
class PortsConfig(BaseModel):
"""Configuration des ports à scanner"""
ranges: List[str] = ["22", "80", "443", "3389", "8080"]
protocols: Optional[Dict[int, str]] = None # Mapping port -> protocole
class HistoryConfig(BaseModel):
@@ -45,6 +46,11 @@ class UIConfig(BaseModel):
offline_transparency: float = 0.5
show_mac: bool = True
show_vendor: bool = True
cell_size: int = 30
font_size: int = 10
cell_gap: float = 2
details_font_size: int = 13
details_spacing: int = 2
class ColorsConfig(BaseModel):
@@ -61,15 +67,23 @@ class DatabaseConfig(BaseModel):
path: str = "./data/db.sqlite"
class HostConfig(BaseModel):
"""Configuration d'un hôte avec sa localisation"""
name: str
location: str
class IPWatchConfig(BaseModel):
"""Configuration complète IPWatch"""
model_config = {"arbitrary_types_allowed": True}
app: AppConfig = Field(default_factory=AppConfig)
network: NetworkConfig
ip_classes: Dict[str, Any] = Field(default_factory=dict)
scan: ScanConfig = Field(default_factory=ScanConfig)
ports: PortsConfig = Field(default_factory=PortsConfig)
locations: List[str] = Field(default_factory=list)
hosts: List[str] = Field(default_factory=list)
hosts: List[HostConfig] = Field(default_factory=list)
history: HistoryConfig = Field(default_factory=HistoryConfig)
ui: UIConfig = Field(default_factory=UIConfig)
colors: ColorsConfig = Field(default_factory=ColorsConfig)
@@ -97,8 +111,15 @@ class ConfigManager:
yaml_data = yaml.safe_load(f)
self._config = IPWatchConfig(**yaml_data)
self._config_path = config_path
return self._config
def reload_config(self) -> IPWatchConfig:
"""Recharge la configuration depuis le fichier"""
if not hasattr(self, '_config_path'):
self._config_path = "./config.yaml"
return self.load_config(self._config_path)
@property
def config(self) -> IPWatchConfig:
"""Retourne la configuration actuelle"""

View File

@@ -12,6 +12,7 @@ from pathlib import Path
from backend.app.core.config import config_manager
from backend.app.core.database import init_database, get_db
from backend.app.routers import ips_router, scan_router, websocket_router
from backend.app.routers import config as config_router
from backend.app.services.scheduler import scan_scheduler
from backend.app.routers.scan import perform_scan
@@ -119,6 +120,7 @@ app.add_middleware(
app.include_router(ips_router)
app.include_router(scan_router)
app.include_router(websocket_router)
app.include_router(config_router.router)
@app.get("/health")

View File

@@ -35,6 +35,7 @@ class IP(Base):
mac = Column(String, nullable=True) # Adresse MAC
vendor = Column(String, nullable=True) # Fabricant (lookup MAC)
hostname = Column(String, nullable=True) # Nom d'hôte réseau
link = Column(String, nullable=True) # Lien personnalisé (URL)
# Ports ouverts (stocké en JSON)
open_ports = Column(JSON, default=list) # Liste des ports ouverts

View File

@@ -0,0 +1,40 @@
"""
Routes pour la configuration
"""
from fastapi import APIRouter, HTTPException
from backend.app.core.config import config_manager
router = APIRouter(prefix="/api/config", tags=["config"])
@router.get("/ui")
async def get_ui_config():
"""Récupérer la configuration UI"""
config = config_manager.config
return {
"cell_size": config.ui.cell_size,
"font_size": config.ui.font_size,
"cell_gap": config.ui.cell_gap,
"offline_transparency": config.ui.offline_transparency,
"show_mac": config.ui.show_mac,
"show_vendor": config.ui.show_vendor
}
@router.post("/reload")
async def reload_config():
"""Recharger la configuration depuis le fichier config.yaml"""
try:
config = config_manager.reload_config()
return {
"success": True,
"message": "Configuration rechargée avec succès",
"ui": {
"cell_size": config.ui.cell_size,
"font_size": config.ui.font_size,
"cell_gap": config.ui.cell_gap,
"offline_transparency": config.ui.offline_transparency,
"show_mac": config.ui.show_mac,
"show_vendor": config.ui.show_vendor
}
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur rechargement config: {str(e)}")

View File

@@ -1,14 +1,17 @@
"""
Endpoints API pour la gestion des IPs
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from sqlalchemy.orm import Session
from sqlalchemy import desc
from typing import List, Optional
from datetime import datetime, timedelta
import xml.etree.ElementTree as ET
import yaml
from backend.app.core.database import get_db
from backend.app.models.ip import IP, IPHistory
from backend.app.core.config import config_manager
from pydantic import BaseModel
router = APIRouter(prefix="/api/ips", tags=["IPs"])
@@ -21,6 +24,7 @@ class IPUpdate(BaseModel):
known: Optional[bool] = None
location: Optional[str] = None
host: Optional[str] = None
link: Optional[str] = None
class IPResponse(BaseModel):
@@ -36,6 +40,7 @@ class IPResponse(BaseModel):
mac: Optional[str]
vendor: Optional[str]
hostname: Optional[str]
link: Optional[str]
open_ports: List[int]
class Config:
@@ -214,3 +219,189 @@ async def get_stats(db: Session = Depends(get_db)):
"known": known,
"unknown": unknown
}
@router.get("/config/options")
async def get_config_options():
"""
Récupère les options de configuration (locations, hosts, port_protocols, version)
Returns:
Dictionnaire avec locations, hosts, port_protocols et version
"""
config = config_manager.config
# Récupérer les protocoles de ports depuis la config
port_protocols = {}
if hasattr(config.ports, 'protocols') and config.ports.protocols:
port_protocols = config.ports.protocols
return {
"locations": config.locations,
"hosts": [{"name": h.name, "location": h.location} for h in config.hosts],
"port_protocols": port_protocols,
"version": config.app.version
}
@router.get("/config/content")
async def get_config_content():
"""
Récupère le contenu brut du fichier config.yaml
Returns:
Contenu du fichier YAML
"""
try:
with open("./config.yaml", "r", encoding="utf-8") as f:
content = f.read()
return {"content": content}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur lecture config: {str(e)}")
@router.post("/config/reload")
async def reload_config():
"""
Recharge la configuration depuis config.yaml
Returns:
Message de confirmation
"""
try:
config_manager.reload_config()
return {"message": "Configuration rechargée avec succès"}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur rechargement config: {str(e)}")
@router.post("/import/ipscan")
async def import_ipscan(file: UploadFile = File(...), db: Session = Depends(get_db)):
"""
Importe les données depuis un fichier XML Angry IP Scanner
Args:
file: Fichier XML uploadé
db: Session de base de données
Returns:
Statistiques d'import
"""
if not file.filename.endswith('.xml'):
raise HTTPException(status_code=400, detail="Le fichier doit être un XML")
try:
# Lire le contenu du fichier
content = await file.read()
# Essayer de parser le XML avec récupération d'erreurs
try:
root = ET.fromstring(content)
except ET.ParseError as e:
# Si le parsing échoue, essayer de nettoyer le contenu
import re
content_str = content.decode('utf-8', errors='ignore')
# Supprimer les caractères de contrôle invalides (sauf tab, CR, LF)
content_str = ''.join(char for char in content_str
if ord(char) >= 32 or char in '\t\r\n')
try:
root = ET.fromstring(content_str.encode('utf-8'))
except ET.ParseError:
raise HTTPException(status_code=400, detail=f"Fichier XML invalide même après nettoyage: {str(e)}")
imported = 0
updated = 0
errors = []
# Parser chaque host
for host in root.findall('.//host'):
try:
# Extraire l'adresse IP
ip_address = host.get('address')
if not ip_address:
continue
# Extraire les informations
hostname = None
mac = None
vendor = None
ports = []
for result in host.findall('result'):
name = result.get('name')
value = result.text.strip() if result.text else ""
# Nettoyer les valeurs [n/a]
if value == "[n/a]":
value = None
if name == "Nom d'hôte" and value:
hostname = value
elif name == "Adresse MAC" and value:
mac = value
elif name == "Constructeur MAC" and value:
vendor = value
elif name == "Ports" and value:
# Parser les ports (format: "22,80,443")
try:
ports = [int(p.strip()) for p in value.split(',') if p.strip().isdigit()]
except Exception as e:
ports = []
# Vérifier si l'IP existe déjà
existing_ip = db.query(IP).filter(IP.ip == ip_address).first()
if existing_ip:
# Mettre à jour avec de nouvelles informations
if hostname:
if not existing_ip.hostname:
existing_ip.hostname = hostname
if not existing_ip.name:
existing_ip.name = hostname
if mac and not existing_ip.mac:
existing_ip.mac = mac
# Toujours mettre à jour vendor et ports depuis IPScan (plus complet et à jour)
if vendor:
existing_ip.vendor = vendor
if ports:
existing_ip.open_ports = ports
existing_ip.last_status = "online"
existing_ip.last_seen = datetime.utcnow()
updated += 1
else:
# Créer une nouvelle entrée
new_ip = IP(
ip=ip_address,
name=hostname,
hostname=hostname,
mac=mac,
vendor=vendor,
open_ports=ports or [],
last_status="online",
known=False,
first_seen=datetime.utcnow(),
last_seen=datetime.utcnow()
)
db.add(new_ip)
imported += 1
except Exception as e:
errors.append(f"Erreur pour {ip_address}: {str(e)}")
continue
# Commit des changements
db.commit()
return {
"message": "Import terminé",
"imported": imported,
"updated": updated,
"errors": errors[:10] # Limiter à 10 erreurs
}
except ET.ParseError as e:
raise HTTPException(status_code=400, detail=f"Fichier XML invalide: {str(e)}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur import: {str(e)}")

View File

@@ -174,6 +174,72 @@ async def start_scan(background_tasks: BackgroundTasks, db: Session = Depends(ge
}
@router.post("/ports/{ip_address}")
async def scan_ip_ports(ip_address: str, db: Session = Depends(get_db)):
"""
Scanne les ports d'une IP spécifique
Args:
ip_address: Adresse IP à scanner
db: Session de base de données
Returns:
Liste des ports ouverts
"""
try:
# Récupérer la config
config = config_manager.config
# Convertir les ports en liste d'entiers
port_list = []
for port_range in config.ports.ranges:
if '-' in port_range:
start, end = map(int, port_range.split('-'))
port_list.extend(range(start, end + 1))
else:
port_list.append(int(port_range))
# Initialiser le scanner
scanner = NetworkScanner(
cidr=config.network.cidr,
timeout=config.scan.timeout
)
# Scanner les ports de cette IP
print(f"[{datetime.now()}] Scan ports pour {ip_address}...")
open_ports = await scanner.scan_ports(ip_address, port_list)
print(f"[{datetime.now()}] Ports ouverts pour {ip_address}: {open_ports}")
# Mettre à jour la base de données
ip_record = db.query(IP).filter(IP.ip == ip_address).first()
if ip_record:
ip_record.open_ports = open_ports
ip_record.last_seen = datetime.utcnow()
db.commit()
# Notifier via WebSocket
await ws_manager.broadcast_ip_update({
"ip": ip_address,
"open_ports": open_ports
})
return {
"message": "Scan de ports terminé",
"ip": ip_address,
"open_ports": open_ports,
"timestamp": datetime.utcnow()
}
except Exception as e:
print(f"Erreur scan ports {ip_address}: {e}")
return {
"message": f"Erreur: {str(e)}",
"ip": ip_address,
"open_ports": [],
"timestamp": datetime.utcnow()
}
@router.post("/cleanup-history")
async def cleanup_history(hours: int = 24, db: Session = Depends(get_db)):
"""

View File

@@ -3,7 +3,7 @@
app:
name: "IPWatch"
version: "1.0.0"
version: "1.0.5"
debug: true
network:
@@ -23,7 +23,7 @@ subnets:
- name: "dhcp"
cidr: "10.0.1.0/24"
start: "10.0.1.1"
end: "10.0.1locations.255"
end: "10.0.1.255"
description: "DHCP"
- name: "iot"
cidr: "10.0.2.0/24"
@@ -49,25 +49,66 @@ ports:
- "22" # SSH
- "80" # HTTP
- "443" # HTTPS
- "445" # SAMBA
- "1880" #nodered
- "3000"
- "3389" # RDP
- "8080" # HTTP alternatif
- "8081" # HTTP alternatif
- "3306" # MySQL
- "3552" #arcane
- "5432" # PostgreSQL
- "8006"
- "6053"
- "8266"
- "9000"
# Mapping port -> protocole pour générer des liens cliquables
protocols:
22: "ssh"
80: "http"
443: "https"
445: "smb"
1880: "http"
3000: "http"
3306: "mysql"
3389: "rdp"
3552: "http"
5432: "postgresql"
6053: "http"
8006: "https"
8080: "http"
8081: "http"
8266: "http"
9000: "http"
locations:
- "Bureau"
- "Salon"
- "Comble"
- "Bureau RdC"
- "Garage"
- "Exterieur"
- "SdB"
- ""
# la localisation est herité de l'host il faudrait adapter config en consequence
# Hosts avec localisation associée
hosts:
- "physique"
- "elitedesk"
- "m710Q"
- "HP Proliant"
- "pve MSI"
- "HP Proxmox"
- name: "physique"
location: ""
- name: "elitedesk"
location: "Comble"
- name: "m710Q"
location: "Bureau RdC"
- name: "HP Proliant"
location: "Bureau RdC"
- name: "pve MSI"
location: "Bureau RdC"
- name: "HP Proxmox"
location: "Bureau"
- name: "IoT"
location: ""
history:
@@ -77,6 +118,11 @@ ui:
offline_transparency: 0.5 # Transparence des IPs offline
show_mac: true
show_vendor: true
cell_size: 30 # Taille des cellules IP en pixels (30, 35, 40...)
font_size: 12 # Taille de la police dans les cellules en pixels
cell_gap: 2.5 # Espacement entre les cellules en pixels
details_font_size: 13 # Taille de la police dans le volet détails en pixels
details_spacing: 2 # Espacement entre les champs du volet détails en pixels
colors:
free: "#75715E" # IP libre (gris Monokai)

2405
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ipwatch-frontend",
"version": "1.0.0",
"version": "1.0.1",
"private": true,
"type": "module",
"scripts": {
@@ -9,15 +9,16 @@
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.15",
"@mdi/font": "^7.4.47",
"axios": "^1.6.5",
"pinia": "^2.1.7",
"axios": "^1.6.5"
"vue": "^3.4.15"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.3",
"vite": "^5.0.11",
"tailwindcss": "^3.4.1",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.33"
"postcss": "^8.4.33",
"tailwindcss": "^3.4.1",
"vite": "^5.0.11"
}
}

View File

@@ -1,7 +1,7 @@
<template>
<div class="h-screen flex flex-col bg-monokai-bg">
<!-- Header -->
<AppHeader />
<AppHeader @openSettings="showSettings = true" />
<!-- Layout 3 colonnes selon consigne-design_webui.md -->
<div class="flex-1 flex overflow-hidden">
@@ -20,20 +20,37 @@
<NewDetections />
</div>
</div>
<!-- Modal Paramètres -->
<SettingsModal
:isOpen="showSettings"
@close="showSettings = false"
@configReloaded="handleConfigReloaded"
/>
</div>
</template>
<script setup>
import { onMounted, onUnmounted } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useIPStore } from '@/stores/ipStore'
import AppHeader from '@/components/AppHeader.vue'
import IPDetails from '@/components/IPDetails.vue'
import IPGridTree from '@/components/IPGridTree.vue'
import NewDetections from '@/components/NewDetections.vue'
import SettingsModal from '@/components/SettingsModal.vue'
const ipStore = useIPStore()
const showSettings = ref(false)
async function handleConfigReloaded() {
// Recharger les IPs après un import ou un rechargement de config
await ipStore.fetchIPs()
}
onMounted(async () => {
// Charger la configuration UI
await ipStore.fetchUIConfig()
// Charger les données initiales
await ipStore.fetchIPs()

View File

@@ -42,17 +42,22 @@ body {
animation: ping-pulse 1.5s ease-in-out infinite;
}
/* Grille des IPs avec espacement configurable */
.ip-grid {
gap: var(--cell-gap, 2px);
}
/* Cases IP compactes - Version minimale */
.ip-cell-compact {
@apply rounded cursor-pointer transition-all duration-200 relative;
border: 2px solid;
width: 50px;
height: 50px;
border: 1px solid;
width: var(--cell-size, 30px);
height: var(--cell-size, 30px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 14px;
font-size: var(--font-size, 10px);
}
/* Cases IP - États selon guidelines-css.md */

View File

@@ -4,7 +4,10 @@
<!-- Logo et titre -->
<div class="flex items-center gap-4">
<h1 class="text-3xl font-bold text-monokai-green">IPWatch</h1>
<span class="text-monokai-comment">Scanner Réseau</span>
<div class="flex flex-col">
<span class="text-monokai-comment">Scanner Réseau</span>
<span class="text-xs text-monokai-comment/60">v{{ appVersion }}</span>
</div>
</div>
<!-- Stats et contrôles -->
@@ -25,13 +28,37 @@
</div>
</div>
<!-- Dernier scan -->
<div v-if="lastScanDate" class="text-sm text-monokai-comment">
Dernier scan: {{ formatScanDate(lastScanDate) }}
</div>
<!-- Progression du scan -->
<div v-if="isScanning" class="flex items-center gap-2 text-sm">
<span class="text-monokai-cyan">
{{ scanProgress.current }} / {{ scanProgress.total }}
</span>
<span v-if="scanProgress.currentIP" class="text-monokai-comment">
({{ scanProgress.currentIP }})
</span>
</div>
<!-- Bouton scan -->
<button
@click="triggerScan"
:disabled="loading"
:disabled="isScanning"
class="px-4 py-2 rounded bg-monokai-cyan text-monokai-bg font-bold hover:bg-monokai-green transition-colors disabled:opacity-50"
>
{{ loading ? 'Scan en cours...' : 'Lancer Scan' }}
{{ isScanning ? 'Scan en cours...' : 'Lancer Scan' }}
</button>
<!-- Bouton Paramètres -->
<button
@click="openSettings"
class="px-4 py-2 rounded bg-monokai-purple text-monokai-bg text-sm hover:bg-monokai-pink transition-colors"
title="Paramètres"
>
Paramètres
</button>
<!-- Indicateur WebSocket -->
@@ -52,11 +79,29 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useIPStore } from '@/stores/ipStore'
const emit = defineEmits(['openSettings'])
const ipStore = useIPStore()
const { stats, loading, wsConnected } = storeToRefs(ipStore)
const { stats, loading, wsConnected, lastScanDate, scanProgress, isScanning } = storeToRefs(ipStore)
const appVersion = ref('')
onMounted(async () => {
// Charger la version depuis le config
try {
const response = await fetch('/api/ips/config/options')
if (response.ok) {
const config = await response.json()
appVersion.value = config.version || '1.0.0'
}
} catch (error) {
console.error('Erreur chargement version:', error)
appVersion.value = '1.0.0'
}
})
async function triggerScan() {
try {
@@ -65,4 +110,26 @@ async function triggerScan() {
console.error('Erreur lancement scan:', err)
}
}
function openSettings() {
emit('openSettings')
}
function formatScanDate(date) {
if (!date) return ''
const d = new Date(date)
const now = new Date()
const diff = now - d
if (diff < 60000) return 'il y a quelques secondes'
if (diff < 3600000) return `il y a ${Math.floor(diff / 60000)} min`
if (diff < 86400000) return `il y a ${Math.floor(diff / 3600000)}h`
return d.toLocaleString('fr-FR', {
day: '2-digit',
month: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
</script>

View File

@@ -10,18 +10,13 @@
:title="getTooltip"
>
<!-- Afficher seulement le dernier octet -->
<div class="font-mono font-bold text-2xl">
<div class="font-mono">
{{ lastOctet }}
</div>
<!-- Nom très court si connu -->
<div v-if="ip.name" class="text-xs opacity-75 truncate mt-1">
{{ ip.name }}
</div>
<!-- Indicateur ports ouverts (petit badge) -->
<!-- Indicateur ports ouverts (petit badge décalé et réduit) -->
<div v-if="ip.open_ports && ip.open_ports.length > 0"
class="absolute top-1 right-1 w-2 h-2 rounded-full bg-monokai-cyan">
class="absolute top-0.5 right-0.5 w-1 h-1 rounded-full bg-monokai-cyan">
</div>
</div>
</template>

View File

@@ -1,149 +1,215 @@
<template>
<div class="h-full flex flex-col bg-monokai-bg border-r border-monokai-comment">
<!-- Header -->
<div class="p-4 border-b border-monokai-comment">
<h2 class="text-xl font-bold text-monokai-cyan">Détails IP</h2>
</div>
<!-- Contenu -->
<div class="flex-1 overflow-auto p-4">
<div v-if="selectedIP" class="space-y-4">
<!-- Adresse IP -->
<div>
<div class="text-sm text-monokai-comment mb-1">Adresse IP</div>
<div class="text-2xl font-mono font-bold text-monokai-green">
{{ selectedIP.ip }}
</div>
</div>
<!-- État -->
<div>
<div class="text-sm text-monokai-comment mb-1">État</div>
<div class="flex items-center gap-2">
<div
:class="[
'w-3 h-3 rounded-full',
selectedIP.last_status === 'online' ? 'bg-monokai-green' : 'bg-monokai-pink'
]"
></div>
<span class="text-monokai-text capitalize">
{{ selectedIP.last_status || 'Inconnu' }}
</span>
</div>
</div>
<!-- Formulaire d'édition -->
<div class="space-y-3 pt-4 border-t border-monokai-comment">
<!-- Nom -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Nom</label>
<input
v-model="formData.name"
type="text"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
placeholder="Ex: Serveur Principal"
/>
</div>
<!-- Connue -->
<div>
<label class="flex items-center gap-2 cursor-pointer">
<input
v-model="formData.known"
type="checkbox"
class="form-checkbox"
/>
<span class="text-sm text-monokai-text">IP connue</span>
</label>
</div>
<!-- Localisation -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Localisation</label>
<input
v-model="formData.location"
type="text"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
placeholder="Ex: Bureau"
/>
</div>
<!-- Type d'hôte -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Type d'hôte</label>
<input
v-model="formData.host"
type="text"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
placeholder="Ex: PC, Serveur, Imprimante"
/>
</div>
<!-- Boutons -->
<div class="flex gap-2 pt-2">
<button
@click="saveChanges"
class="px-4 py-2 bg-monokai-green text-monokai-bg rounded font-bold hover:bg-monokai-cyan transition-colors"
>
Enregistrer
</button>
<button
@click="resetForm"
class="px-4 py-2 bg-monokai-comment text-monokai-bg rounded hover:bg-monokai-text transition-colors"
>
Annuler
</button>
</div>
</div>
<!-- Informations réseau -->
<div class="pt-4 border-t border-monokai-comment space-y-2">
<h3 class="text-monokai-cyan font-bold mb-2">Informations réseau</h3>
<div v-if="selectedIP.mac">
<div class="text-sm text-monokai-comment">MAC</div>
<div class="text-monokai-text font-mono">{{ selectedIP.mac }}</div>
</div>
<div v-if="selectedIP.vendor">
<div class="text-sm text-monokai-comment">Fabricant</div>
<div class="text-monokai-text">{{ selectedIP.vendor }}</div>
</div>
<div v-if="selectedIP.hostname">
<div class="text-sm text-monokai-comment">Hostname</div>
<div class="text-monokai-text font-mono">{{ selectedIP.hostname }}</div>
</div>
<div v-if="selectedIP.open_ports && selectedIP.open_ports.length > 0">
<div class="text-sm text-monokai-comment">Ports ouverts</div>
<div class="flex flex-wrap gap-1 mt-1">
<span
v-for="port in selectedIP.open_ports"
:key="port"
class="px-2 py-1 bg-monokai-cyan/20 text-monokai-cyan rounded text-xs font-mono"
<div
class="h-full flex flex-col bg-monokai-bg border-r border-monokai-comment"
:style="detailsStyles"
>
<!-- Contenu scrollable -->
<div class="flex-1 overflow-hidden flex flex-col">
<div v-if="selectedIP" class="flex flex-col h-full">
<!-- Section fixe (sticky) : IP et État sur la même ligne -->
<div class="bg-monokai-bg border-b border-monokai-comment" :style="headerPadding">
<div class="flex items-center justify-between gap-2">
<!-- Adresse IP -->
<div class="flex-1 min-w-0">
<a
v-if="selectedIP.link"
:href="selectedIP.link"
target="_blank"
class="font-mono font-bold text-monokai-green hover:text-monokai-cyan transition-colors cursor-pointer underline block truncate"
:style="{ fontSize: ipFontSize }"
>
{{ port }}
{{ selectedIP.ip }}
</a>
<div v-else class="font-mono font-bold text-monokai-green truncate" :style="{ fontSize: ipFontSize }">
{{ selectedIP.ip }}
</div>
</div>
<!-- État -->
<div class="flex items-center gap-1 flex-shrink-0">
<div
:class="[
'w-2 h-2 rounded-full',
selectedIP.last_status === 'online' ? 'bg-monokai-green' : 'bg-monokai-pink'
]"
></div>
<span class="text-monokai-text capitalize text-xs">
{{ selectedIP.last_status || 'Inconnu' }}
</span>
</div>
</div>
</div>
<!-- Timestamps -->
<div class="pt-4 border-t border-monokai-comment space-y-2 text-sm">
<div v-if="selectedIP.first_seen">
<span class="text-monokai-comment">Première vue:</span>
<span class="text-monokai-text ml-2">{{ formatDate(selectedIP.first_seen) }}</span>
<!-- Section scrollable : Formulaire et reste -->
<div class="flex-1 overflow-auto" :style="contentPadding">
<!-- Formulaire d'édition -->
<div :style="formSpacing">
<!-- Nom -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Nom</label>
<input
v-model="formData.name"
type="text"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
placeholder="Ex: Serveur Principal"
/>
</div>
<!-- Connue -->
<div>
<label class="flex items-center gap-2 cursor-pointer">
<input
v-model="formData.known"
type="checkbox"
class="form-checkbox"
/>
<span class="text-sm text-monokai-text">IP connue</span>
</label>
</div>
<!-- Type d'hôte -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Type d'hôte</label>
<select
v-model="formData.host"
@change="onHostChange"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
>
<option value="">-- Sélectionner --</option>
<option v-for="host in hosts" :key="host.name" :value="host.name">{{ host.name }}</option>
</select>
</div>
<!-- Localisation -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Localisation</label>
<select
v-model="formData.location"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
>
<option value="">-- Sélectionner --</option>
<option v-for="loc in locations" :key="loc" :value="loc">{{ loc }}</option>
</select>
</div>
<!-- Lien -->
<div>
<label class="text-sm text-monokai-comment mb-1 block">Lien (URL)</label>
<input
v-model="formData.link"
type="url"
class="w-full px-3 py-2 bg-monokai-bg border border-monokai-comment rounded text-monokai-text"
placeholder="https://exemple.com"
/>
</div>
</div>
<div v-if="selectedIP.last_seen">
<span class="text-monokai-comment">Dernière vue:</span>
<span class="text-monokai-text ml-2">{{ formatDate(selectedIP.last_seen) }}</span>
<!-- Informations réseau -->
<div class="border-t border-monokai-comment" :style="{ paddingTop: `${uiConfig.details_spacing * 2}px`, marginTop: `${uiConfig.details_spacing * 2}px` }">
<div :style="formSpacing">
<h3 class="text-monokai-cyan font-bold mb-2">Informations réseau</h3>
<div v-if="selectedIP.mac">
<div class="text-sm text-monokai-comment">MAC</div>
<div class="text-monokai-text font-mono">{{ selectedIP.mac }}</div>
</div>
<div v-if="selectedIP.vendor">
<div class="text-sm text-monokai-comment">Fabricant</div>
<div class="text-monokai-text">{{ selectedIP.vendor }}</div>
</div>
<div v-if="selectedIP.hostname">
<div class="text-sm text-monokai-comment">Hostname</div>
<div class="text-monokai-text font-mono">{{ selectedIP.hostname }}</div>
</div>
<div v-if="selectedIP.open_ports && selectedIP.open_ports.length > 0">
<div class="text-sm text-monokai-comment">Ports ouverts</div>
<div class="flex flex-wrap gap-1 mt-1">
<a
v-for="port in selectedIP.open_ports"
:key="port"
:href="getPortUrl(port)"
:target="getPortUrl(port) ? '_blank' : undefined"
:class="[
'px-2 py-1 rounded text-xs font-mono',
getPortUrl(port)
? 'bg-monokai-cyan/20 text-monokai-cyan hover:bg-monokai-cyan/30 cursor-pointer underline'
: 'bg-monokai-cyan/20 text-monokai-cyan cursor-default'
]"
:title="getPortUrl(port) ? `Ouvrir ${portProtocols[port] || portProtocols[String(port)]}://${selectedIP.ip}:${port}` : `Port ${port}`"
>
{{ port }}
</a>
</div>
</div>
<!-- Boutons d'action avec icônes -->
<div class="flex gap-2 justify-center" :style="{ marginTop: `${uiConfig.details_spacing * 3}px` }">
<button
@click="saveChanges"
:class="[
'p-2 rounded transition-colors',
saveButtonState === 'saved'
? 'bg-green-800 text-monokai-text'
: 'bg-monokai-green text-monokai-bg hover:bg-monokai-cyan'
]"
:disabled="saveButtonState === 'saving'"
title="Enregistrer"
>
<span class="mdi mdi-content-save text-xl"></span>
</button>
<button
@click="scanPorts"
:class="[
'p-2 rounded transition-colors',
portScanState === 'scanning'
? 'bg-monokai-orange text-monokai-bg'
: 'bg-monokai-cyan text-monokai-bg hover:bg-monokai-purple'
]"
:disabled="portScanState === 'scanning'"
title="Scanner les ports"
>
<span class="mdi mdi-wifi-settings text-xl"></span>
</button>
<button
@click="resetForm"
class="p-2 bg-monokai-comment text-monokai-bg rounded hover:bg-monokai-text transition-colors"
title="Annuler"
>
<span class="mdi mdi-close-box text-xl"></span>
</button>
<button
@click="deleteIP"
class="p-2 bg-monokai-pink text-monokai-bg rounded hover:bg-red-700 transition-colors"
title="Effacer"
>
<span class="mdi mdi-eraser text-xl"></span>
</button>
</div>
</div>
</div>
<!-- Timestamps -->
<div class="border-t border-monokai-comment text-sm" :style="{ paddingTop: `${uiConfig.details_spacing * 2}px`, marginTop: `${uiConfig.details_spacing * 2}px` }">
<div :style="formSpacing">
<div v-if="selectedIP.first_seen">
<span class="text-monokai-comment">Première vue:</span>
<span class="text-monokai-text ml-2">{{ formatDate(selectedIP.first_seen) }}</span>
</div>
<div v-if="selectedIP.last_seen">
<span class="text-monokai-comment">Dernière vue:</span>
<span class="text-monokai-text ml-2">{{ formatDate(selectedIP.last_seen) }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Placeholder si rien sélectionné -->
<div v-else class="text-center text-monokai-comment mt-10">
<div v-else class="text-center text-monokai-comment mt-10 p-4">
<p>Sélectionnez une IP pour voir les détails</p>
</div>
</div>
@@ -151,39 +217,118 @@
</template>
<script setup>
import { ref, watch } from 'vue'
import { ref, watch, onMounted, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useIPStore } from '@/stores/ipStore'
const ipStore = useIPStore()
const { selectedIP } = storeToRefs(ipStore)
// Données de configuration depuis config.yaml (chargées dynamiquement)
const locations = ref([])
const hosts = ref([])
const portProtocols = ref({}) // Mapping port -> protocole
const uiConfig = ref({
details_font_size: 13,
details_spacing: 2
})
const formData = ref({
name: '',
known: false,
location: '',
host: ''
host: '',
link: ''
})
// État du bouton enregistrer
const saveButtonState = ref('idle') // 'idle', 'saving', 'saved'
// État du scan de ports
const portScanState = ref('idle') // 'idle', 'scanning'
// Styles dynamiques basés sur la config
const detailsStyles = computed(() => ({
fontSize: `${uiConfig.value.details_font_size}px`
}))
const formSpacing = computed(() => ({
display: 'flex',
flexDirection: 'column',
gap: `${uiConfig.value.details_spacing}px`
}))
const headerPadding = computed(() => ({
padding: `${uiConfig.value.details_spacing * 2}px ${uiConfig.value.details_spacing * 3}px`
}))
const contentPadding = computed(() => ({
padding: `${uiConfig.value.details_spacing * 3}px`
}))
const ipFontSize = computed(() => `${uiConfig.value.details_font_size * 1.2}px`)
// Charger les options de configuration au montage
onMounted(async () => {
try {
const response = await fetch('/api/ips/config/options')
if (response.ok) {
const data = await response.json()
locations.value = data.locations || []
hosts.value = data.hosts || []
portProtocols.value = data.port_protocols || {}
}
} catch (error) {
console.error('Erreur chargement options config:', error)
}
// Charger la config UI depuis le store
if (ipStore.uiConfig) {
uiConfig.value = {
details_font_size: ipStore.uiConfig.details_font_size || 13,
details_spacing: ipStore.uiConfig.details_spacing || 2
}
}
})
// Mettre à jour le formulaire quand l'IP change
watch(selectedIP, (newIP) => {
if (newIP) {
// Auto-remplir l'URL si vide et port 80 ouvert
let autoLink = newIP.link || ''
if (!autoLink && newIP.open_ports && newIP.open_ports.includes(80)) {
autoLink = `http://${newIP.ip}`
}
formData.value = {
name: newIP.name || '',
known: newIP.known || false,
location: newIP.location || '',
host: newIP.host || ''
host: newIP.host || '',
link: autoLink
}
// Réinitialiser l'état du bouton
saveButtonState.value = 'idle'
}
}, { immediate: true })
// Auto-remplir la localisation quand l'hôte change
function onHostChange() {
const selectedHost = hosts.value.find(h => h.name === formData.value.host)
if (selectedHost && selectedHost.location) {
formData.value.location = selectedHost.location
}
}
function resetForm() {
if (selectedIP.value) {
formData.value = {
name: selectedIP.value.name || '',
known: selectedIP.value.known || false,
location: selectedIP.value.location || '',
host: selectedIP.value.host || ''
host: selectedIP.value.host || '',
link: selectedIP.value.link || ''
}
}
}
@@ -192,10 +337,73 @@ async function saveChanges() {
if (!selectedIP.value) return
try {
saveButtonState.value = 'saving'
await ipStore.updateIP(selectedIP.value.ip, formData.value)
saveButtonState.value = 'saved'
console.log('IP mise à jour')
// Retour à l'état normal après 2 secondes
setTimeout(() => {
saveButtonState.value = 'idle'
}, 2000)
} catch (err) {
console.error('Erreur mise à jour IP:', err)
saveButtonState.value = 'idle'
}
}
async function scanPorts() {
if (!selectedIP.value) return
try {
portScanState.value = 'scanning'
const response = await fetch(`/api/scan/ports/${selectedIP.value.ip}`, {
method: 'POST'
})
if (!response.ok) {
throw new Error('Erreur lors du scan de ports')
}
const result = await response.json()
console.log('Scan de ports terminé:', result)
// Rafraîchir les données de l'IP
await ipStore.fetchIPs()
portScanState.value = 'idle'
} catch (err) {
console.error('Erreur scan ports:', err)
portScanState.value = 'idle'
}
}
async function deleteIP() {
if (!selectedIP.value) return
if (confirm(`Voulez-vous vraiment effacer les données de ${selectedIP.value.ip} ?`)) {
try {
// Effacer les données et passer en offline-unknown
const resetData = {
name: '',
known: false,
location: '',
host: '',
link: '',
last_status: 'offline'
}
await ipStore.updateIP(selectedIP.value.ip, resetData)
formData.value = {
name: '',
known: false,
location: '',
host: '',
link: ''
}
console.log('IP effacée')
} catch (err) {
console.error('Erreur effacement IP:', err)
}
}
}
@@ -204,4 +412,28 @@ function formatDate(dateString) {
const date = new Date(dateString)
return date.toLocaleString('fr-FR')
}
// Générer une URL cliquable pour un port
function getPortUrl(port) {
if (!selectedIP.value) return null
// Chercher le protocole (la clé peut être un nombre ou une string)
const protocol = portProtocols.value[port] || portProtocols.value[String(port)]
if (!protocol) return null
const ip = selectedIP.value.ip
// Mapping protocole -> URL
const urlMap = {
'http': `http://${ip}:${port}`,
'https': `https://${ip}:${port}`,
'ssh': `ssh://${ip}:${port}`,
'smb': `smb://${ip}`,
'rdp': `rdp://${ip}:${port}`,
'mysql': `mysql://${ip}:${port}`,
'postgresql': `postgresql://${ip}:${port}`
}
return urlMap[protocol] || null
}
</script>

View File

@@ -24,18 +24,30 @@
</label>
</div>
<!-- Grille d'IPs -->
<!-- Grille d'IPs par sous-réseaux -->
<div class="flex-1 overflow-auto p-4">
<div class="grid grid-cols-4 gap-3">
<IPCell
v-for="ip in filteredIPs"
:key="ip.ip"
:ip="ip"
/>
<!-- Pour chaque sous-réseau -->
<div v-for="subnet in groupedSubnets" :key="subnet.name" class="mb-6">
<!-- En-tête de section -->
<div class="mb-3 pb-2 border-b-2 border-monokai-cyan/30">
<p class="text-xs text-monokai-comment">{{ subnet.name }}</p>
<h3 v-if="subnet.start && subnet.end" class="text-lg font-bold text-monokai-cyan mt-0.5">
{{ subnet.start }} à {{ subnet.end }}
</h3>
</div>
<!-- Grille des IPs de ce sous-réseau -->
<div class="grid grid-cols-4 gap-3">
<IPCell
v-for="ip in subnet.ips"
:key="ip.ip"
:ip="ip"
/>
</div>
</div>
<!-- Message si vide -->
<div v-if="filteredIPs.length === 0" class="text-center text-monokai-comment mt-10">
<div v-if="groupedSubnets.length === 0" class="text-center text-monokai-comment mt-10">
<p>Aucune IP à afficher</p>
<p class="text-sm mt-2">Ajustez les filtres ou lancez un scan</p>
</div>
@@ -70,10 +82,98 @@
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useIPStore } from '@/stores/ipStore'
import IPCell from './IPCell.vue'
const ipStore = useIPStore()
const { filteredIPs, filters } = storeToRefs(ipStore)
// Subnets depuis la config
const subnets = ref([])
// Charger les subnets depuis la config
onMounted(async () => {
try {
const response = await fetch('/api/ips/config/content')
if (response.ok) {
const data = await response.json()
// Parser le YAML pour extraire les subnets
const yamlContent = data.content
const subnetMatches = yamlContent.match(/subnets:[\s\S]*?(?=\n\w+:|\n$)/)?.[0]
if (subnetMatches) {
// Simple parsing des subnets (améliorer si nécessaire)
const subnetLines = subnetMatches.split('\n')
let currentSubnet = null
subnetLines.forEach(line => {
if (line.includes('- name:')) {
if (currentSubnet) subnets.value.push(currentSubnet)
currentSubnet = { name: line.split('"')[1] }
} else if (currentSubnet) {
if (line.includes('start:')) currentSubnet.start = line.split('"')[1]
if (line.includes('end:')) currentSubnet.end = line.split('"')[1]
if (line.includes('cidr:')) currentSubnet.cidr = line.split('"')[1]
}
})
if (currentSubnet) subnets.value.push(currentSubnet)
console.log('=== SUBNETS LOADED V2 ===', Date.now(), subnets.value)
}
}
} catch (error) {
console.error('Erreur chargement subnets:', error)
}
})
// Fonction pour vérifier si une IP appartient à un subnet
function ipInSubnet(ip, start, end) {
const ipNum = ipToNumber(ip)
const startNum = ipToNumber(start)
const endNum = ipToNumber(end)
return ipNum >= startNum && ipNum <= endNum
}
// Convertir une IP en nombre
function ipToNumber(ip) {
return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0
}
// Grouper les IPs par subnet
const groupedSubnets = computed(() => {
const groups = []
// Pour chaque subnet défini
subnets.value.forEach(subnet => {
const subnetIPs = filteredIPs.value.filter(ip =>
ipInSubnet(ip.ip, subnet.start, subnet.end)
)
if (subnetIPs.length > 0) {
groups.push({
name: subnet.name,
start: subnet.start,
end: subnet.end,
ips: subnetIPs.sort((a, b) => ipToNumber(a.ip) - ipToNumber(b.ip))
})
}
})
// Ajouter section "Autres" pour les IPs hors subnets
const otherIPs = filteredIPs.value.filter(ip => {
return !subnets.value.some(subnet => ipInSubnet(ip.ip, subnet.start, subnet.end))
})
if (otherIPs.length > 0) {
groups.push({
name: 'Autres',
start: '',
end: '',
ips: otherIPs.sort((a, b) => ipToNumber(a.ip) - ipToNumber(b.ip))
})
}
return groups
})
</script>

View File

@@ -38,11 +38,12 @@
</div>
<!-- Grille compacte des IPs du sous-réseau -->
<div class="flex flex-wrap gap-2 pl-8">
<div class="flex flex-wrap pl-8 ip-grid">
<IPCell
v-for="ip in subnet.ips"
:key="ip.ip"
:ip="ip"
:is-pinging="scanProgress.currentIP === ip.ip"
/>
</div>
</div>
@@ -89,7 +90,7 @@ import { useIPStore } from '@/stores/ipStore'
import IPCell from './IPCell.vue'
const ipStore = useIPStore()
const { filteredIPs, filters } = storeToRefs(ipStore)
const { filteredIPs, filters, scanProgress } = storeToRefs(ipStore)
// Définition des sous-réseaux (devrait venir de la config mais en dur pour l'instant)
const subnets = [
@@ -98,6 +99,21 @@ const subnets = [
{ name: 'iot', cidr: '10.0.2.0/24', description: 'IoT' }
]
// Fonction pour trier les IPs par ordre numérique
function sortIPsNumerically(ips) {
return ips.slice().sort((a, b) => {
const partsA = a.ip.split('.').map(Number)
const partsB = b.ip.split('.').map(Number)
for (let i = 0; i < 4; i++) {
if (partsA[i] !== partsB[i]) {
return partsA[i] - partsB[i]
}
}
return 0
})
}
// Organiser les IPs par sous-réseau
const organizedSubnets = computed(() => {
return subnets.map(subnet => {
@@ -110,18 +126,21 @@ const organizedSubnets = computed(() => {
return ip.ip.startsWith(prefix + '.')
})
// Trier par ordre numérique
const sortedIPs = sortIPsNumerically(subnetIPs)
// Calculer les stats
const stats = {
total: subnetIPs.length,
online: subnetIPs.filter(ip => ip.last_status === 'online').length,
offline: subnetIPs.filter(ip => ip.last_status === 'offline').length
total: sortedIPs.length,
online: sortedIPs.filter(ip => ip.last_status === 'online').length,
offline: sortedIPs.filter(ip => ip.last_status === 'offline').length
}
return {
name: subnet.name,
cidr: subnet.cidr,
description: subnet.description,
ips: subnetIPs,
ips: sortedIPs,
stats
}
}).filter(subnet => subnet.ips.length > 0) // Ne montrer que les sous-réseaux avec des IPs

View File

@@ -7,46 +7,51 @@
<!-- Liste -->
<div class="flex-1 overflow-auto p-4">
<div v-if="newIPs.length > 0" class="space-y-3">
<div v-if="newIPs.length > 0" class="space-y-2">
<div
v-for="ip in newIPs"
:key="ip.ip"
@click="selectIP(ip)"
class="p-3 rounded border-2 border-monokai-pink bg-monokai-pink/10 cursor-pointer hover:bg-monokai-pink/20 transition-colors"
:class="[
'p-2 rounded border-2 cursor-pointer transition-colors',
isOlderThanOneHour(ip.first_seen)
? 'border-monokai-purple-dark bg-monokai-purple-dark/10 hover:bg-monokai-purple-dark/20'
: 'border-monokai-pink bg-monokai-pink/10 hover:bg-monokai-pink/20'
]"
>
<!-- IP -->
<div class="font-mono font-bold text-monokai-text">
{{ ip.ip }}
<!-- IP + Statut sur la même ligne -->
<div class="flex items-center justify-between">
<span class="font-mono font-bold text-monokai-text text-sm">
{{ ip.ip }}
</span>
<div class="flex items-center gap-2 text-xs">
<span
:class="[
'px-2 py-0.5 rounded',
ip.last_status === 'online'
? 'bg-monokai-green/20 text-monokai-green'
: 'bg-monokai-comment/20 text-monokai-comment'
]"
>
{{ ip.last_status || 'Inconnu' }}
</span>
<span
v-if="!ip.known"
class="px-2 py-0.5 rounded bg-monokai-cyan/20 text-monokai-cyan"
>
Inconnue
</span>
</div>
</div>
<!-- État -->
<div class="text-sm mt-1">
<span
:class="[
'px-2 py-1 rounded text-xs',
ip.last_status === 'online'
? 'bg-monokai-green/20 text-monokai-green'
: 'bg-monokai-comment/20 text-monokai-comment'
]"
>
{{ ip.last_status || 'Inconnu' }}
</span>
<span
v-if="!ip.known"
class="ml-2 px-2 py-1 rounded text-xs bg-monokai-purple/20 text-monokai-purple"
>
Inconnue
</span>
</div>
<!-- MAC et Vendor -->
<div v-if="ip.mac" class="text-xs text-monokai-comment mt-2 font-mono">
<!-- MAC et Vendor (si disponible) -->
<div v-if="ip.mac" class="text-xs text-monokai-comment mt-1 font-mono">
{{ ip.mac }}
<span v-if="ip.vendor" class="ml-1">({{ ip.vendor }})</span>
</div>
<!-- Timestamp -->
<div class="text-xs text-monokai-comment mt-2">
<div class="text-xs text-monokai-comment mt-1">
{{ formatTime(ip.first_seen) }}
</div>
</div>
@@ -69,15 +74,21 @@ import { useIPStore } from '@/stores/ipStore'
const ipStore = useIPStore()
const { ips } = storeToRefs(ipStore)
// IPs nouvellement détectées (dans les dernières 24h)
// IPs nouvellement détectées (dans les dernières 24h ET online ET non enregistrées)
const newIPs = computed(() => {
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000)
return ips.value
.filter(ip => {
// Doit être online
if (ip.last_status !== 'online') return false
// Doit avoir un first_seen récent
if (!ip.first_seen) return false
const firstSeen = new Date(ip.first_seen)
return firstSeen > oneDayAgo
if (firstSeen <= oneDayAgo) return false
// Ne pas afficher les IPs déjà enregistrées (avec nom ou connue)
if (ip.known || ip.name) return false
return true
})
.sort((a, b) => {
const dateA = new Date(a.first_seen)
@@ -91,6 +102,15 @@ function selectIP(ip) {
ipStore.selectIP(ip)
}
// Vérifier si la détection date de plus d'une heure
function isOlderThanOneHour(dateString) {
if (!dateString) return false
const date = new Date(dateString)
const now = new Date()
const diff = now - date
return diff >= 3600000 // 1 heure = 3600000 ms
}
function formatTime(dateString) {
if (!dateString) return ''
const date = new Date(dateString)

View File

@@ -0,0 +1,193 @@
<template>
<div
v-if="isOpen"
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
@click.self="close"
>
<div class="bg-monokai-bg border-2 border-monokai-cyan rounded-lg w-[800px] max-h-[80vh] flex flex-col">
<!-- Header -->
<div class="p-4 border-b border-monokai-comment flex justify-between items-center">
<h2 class="text-xl font-bold text-monokai-cyan">Paramètres</h2>
<button
@click="close"
class="text-monokai-comment hover:text-monokai-text text-2xl leading-none"
>
×
</button>
</div>
<!-- Content -->
<div class="flex-1 overflow-auto p-4 space-y-4">
<!-- Boutons d'action -->
<div class="flex gap-2">
<button
@click="reloadConfig"
:disabled="loading"
class="px-4 py-2 bg-monokai-green text-monokai-bg rounded hover:bg-monokai-cyan transition-colors disabled:opacity-50"
>
{{ loading ? 'Chargement...' : 'Recharger Config' }}
</button>
<label class="px-4 py-2 bg-monokai-cyan text-monokai-bg rounded hover:bg-monokai-green transition-colors cursor-pointer">
<input
type="file"
accept=".xml"
@change="importIpscan"
class="hidden"
/>
Importer IPScan XML
</label>
</div>
<!-- Messages -->
<div v-if="message" :class="['p-3 rounded', messageClass]">
{{ message }}
</div>
<!-- Contenu config.yaml -->
<div>
<h3 class="text-monokai-cyan font-bold mb-2">Fichier config.yaml</h3>
<pre class="bg-monokai-bg border border-monokai-comment rounded p-4 text-sm text-monokai-text overflow-auto max-h-[400px] font-mono">{{ configContent }}</pre>
</div>
</div>
<!-- Footer -->
<div class="p-4 border-t border-monokai-comment flex justify-end">
<button
@click="close"
class="px-4 py-2 bg-monokai-comment text-monokai-bg rounded hover:bg-monokai-text transition-colors"
>
Fermer
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
isOpen: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['close', 'configReloaded'])
const configContent = ref('')
const loading = ref(false)
const message = ref('')
const messageType = ref('info')
// Computed class pour le message
const messageClass = ref('')
watch(() => props.isOpen, async (newVal) => {
if (newVal) {
await loadConfigContent()
}
})
async function loadConfigContent() {
try {
const response = await fetch('/api/ips/config/content')
if (response.ok) {
const data = await response.json()
configContent.value = data.content
} else {
configContent.value = 'Erreur de chargement du fichier config.yaml'
}
} catch (error) {
console.error('Erreur chargement config:', error)
configContent.value = 'Erreur de chargement du fichier config.yaml'
}
}
async function reloadConfig() {
loading.value = true
message.value = ''
try {
const response = await fetch('/api/ips/config/reload', {
method: 'POST'
})
if (response.ok) {
const data = await response.json()
message.value = data.message
messageType.value = 'success'
messageClass.value = 'bg-monokai-green/20 text-monokai-green border border-monokai-green'
emit('configReloaded')
// Recharger le contenu du config
await loadConfigContent()
} else {
const error = await response.json()
message.value = error.detail || 'Erreur lors du rechargement'
messageType.value = 'error'
messageClass.value = 'bg-monokai-pink/20 text-monokai-pink border border-monokai-pink'
}
} catch (error) {
console.error('Erreur rechargement config:', error)
message.value = 'Erreur de connexion au serveur'
messageType.value = 'error'
messageClass.value = 'bg-monokai-pink/20 text-monokai-pink border border-monokai-pink'
} finally {
loading.value = false
setTimeout(() => {
message.value = ''
}, 5000)
}
}
async function importIpscan(event) {
const file = event.target.files[0]
if (!file) return
loading.value = true
message.value = ''
const formData = new FormData()
formData.append('file', file)
try {
const response = await fetch('/api/ips/import/ipscan', {
method: 'POST',
body: formData
})
if (response.ok) {
const data = await response.json()
message.value = `Import réussi: ${data.imported} nouvelles IPs, ${data.updated} mises à jour`
if (data.errors && data.errors.length > 0) {
message.value += `\nErreurs: ${data.errors.join(', ')}`
}
messageType.value = 'success'
messageClass.value = 'bg-monokai-green/20 text-monokai-green border border-monokai-green'
emit('configReloaded') // Rafraîchir la liste des IPs
} else {
const error = await response.json()
message.value = error.detail || 'Erreur lors de l\'import'
messageType.value = 'error'
messageClass.value = 'bg-monokai-pink/20 text-monokai-pink border border-monokai-pink'
}
} catch (error) {
console.error('Erreur import:', error)
message.value = 'Erreur de connexion au serveur'
messageType.value = 'error'
messageClass.value = 'bg-monokai-pink/20 text-monokai-pink border border-monokai-pink'
} finally {
loading.value = false
event.target.value = '' // Reset input
setTimeout(() => {
message.value = ''
}, 8000)
}
}
function close() {
emit('close')
}
</script>

View File

@@ -2,6 +2,7 @@ import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import './assets/main.css'
import '@mdi/font/css/materialdesignicons.css'
const app = createApp(App)
const pinia = createPinia()

View File

@@ -18,6 +18,16 @@ export const useIPStore = defineStore('ip', () => {
known: 0,
unknown: 0
})
const lastScanDate = ref(null)
const scanProgress = ref({
current: 0,
total: 0,
currentIP: null
})
const isScanning = ref(false)
const uiConfig = ref({
cell_size: 30
})
// Filtres
const filters = ref({
@@ -51,6 +61,36 @@ export const useIPStore = defineStore('ip', () => {
})
// Actions
async function fetchUIConfig() {
try {
const response = await axios.get('/api/config/ui')
uiConfig.value = response.data
// Appliquer la taille des cellules, de la police et de l'espacement via variables CSS
document.documentElement.style.setProperty('--cell-size', `${response.data.cell_size}px`)
document.documentElement.style.setProperty('--font-size', `${response.data.font_size}px`)
document.documentElement.style.setProperty('--cell-gap', `${response.data.cell_gap}px`)
} catch (err) {
console.error('Erreur chargement config UI:', err)
}
}
async function reloadConfig() {
try {
const response = await axios.post('/api/config/reload')
if (response.data.success) {
// Appliquer la nouvelle config UI
uiConfig.value = response.data.ui
document.documentElement.style.setProperty('--cell-size', `${response.data.ui.cell_size}px`)
document.documentElement.style.setProperty('--font-size', `${response.data.ui.font_size}px`)
document.documentElement.style.setProperty('--cell-gap', `${response.data.ui.cell_gap}px`)
return response.data.message
}
} catch (err) {
console.error('Erreur rechargement config:', err)
throw err
}
}
async function fetchIPs() {
loading.value = true
error.value = null
@@ -97,6 +137,27 @@ export const useIPStore = defineStore('ip', () => {
}
}
async function deleteIP(ipAddress) {
try {
await axios.delete(`/api/ips/${ipAddress}`)
// Retirer du store
const index = ips.value.findIndex(ip => ip.ip === ipAddress)
if (index !== -1) {
ips.value.splice(index, 1)
}
if (selectedIP.value?.ip === ipAddress) {
selectedIP.value = null
}
await fetchStats()
} catch (err) {
error.value = err.message
throw err
}
}
async function getIPHistory(ipAddress, hours = 24) {
try {
const response = await axios.get(`/api/ips/${ipAddress}/history?hours=${hours}`)
@@ -172,12 +233,28 @@ export const useIPStore = defineStore('ip', () => {
switch (message.type) {
case 'scan_start':
// Notification début de scan
isScanning.value = true
scanProgress.value = {
current: 0,
total: message.total || 0,
currentIP: null
}
break
case 'scan_progress':
// Progression du scan
if (message.current) scanProgress.value.current = message.current
if (message.total) scanProgress.value.total = message.total
if (message.ip) scanProgress.value.currentIP = message.ip
break
case 'scan_complete':
// Rafraîchir les données après scan
isScanning.value = false
lastScanDate.value = new Date()
scanProgress.value = { current: 0, total: 0, currentIP: null }
fetchIPs()
stats.value = message.stats
if (message.stats) stats.value = message.stats
break
case 'ip_update':
@@ -212,14 +289,21 @@ export const useIPStore = defineStore('ip', () => {
stats,
filters,
wsConnected,
lastScanDate,
scanProgress,
isScanning,
uiConfig,
// Computed
filteredIPs,
// Actions
fetchUIConfig,
reloadConfig,
fetchIPs,
fetchStats,
updateIP,
deleteIP,
getIPHistory,
startScan,
selectIP,

View File

@@ -16,6 +16,7 @@ export default {
pink: '#F92672',
cyan: '#66D9EF',
purple: '#AE81FF',
'purple-dark': '#5E4B8C', // Violet foncé pour détections anciennes
yellow: '#E6DB74',
orange: '#FD971F',
},

940
ipscan.xml Normal file
View File

@@ -0,0 +1,940 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- This file has been generated by Angry IP Scanner 3.9.2 -->
<!-- Visit the website at https://angryip.org -->
<scanning_report time="2025-12-06 08:04:28">
<feeder><![CDATA[10.0.0.0 - 10.0.3.255]]></feeder>
<hosts>
<host address="10.0.0.2">
<result name="IP"><![CDATA[10.0.0.2]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[homeassistant.local]]></result>
<result name="Ports"><![CDATA[22,8123,9000]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[02:E2:88:D4:02:62]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\HOMEASSISTANT@HOMEASSISTANT [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.5">
<result name="IP"><![CDATA[10.0.0.5]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[debian-vm5.local]]></result>
<result name="Ports"><![CDATA[22,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:2C:D8:8F]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\DEBIAN-VM5@DEBIAN-VM5 [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.3">
<result name="IP"><![CDATA[10.0.0.3]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-mqtt.local]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[92:A5:9D:DC:FF:A6]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.8">
<result name="IP"><![CDATA[10.0.0.8]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-docker8.local]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[26:F9:1C:CD:2E:68]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-DOCKER8@VM-DOCKER8 [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.4">
<result name="IP"><![CDATA[10.0.0.4]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[domotic.local]]></result>
<result name="Ports"><![CDATA[22,8006]]></result>
<result name="Constructeur MAC"><![CDATA[Hewlett Packard]]></result>
<result name="Adresse MAC"><![CDATA[EC:B1:D7:69:21:B7]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.9">
<result name="IP"><![CDATA[10.0.0.9]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-nodered.local]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:37:07:A1]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.10">
<result name="IP"><![CDATA[10.0.0.10]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[serv-dns.local]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[7E:A7:24:20:4C:1F]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.13">
<result name="IP"><![CDATA[10.0.0.13]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[VM-DETECT-CAM]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:FE:8C:BA]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-DETECT-CAM@VM-DETECT-CAM [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.15">
<result name="IP"><![CDATA[10.0.0.15]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[adguard.home]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:ED:2B:2B]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.6">
<result name="IP"><![CDATA[10.0.0.6]]></result>
<result name="Ping"><![CDATA[28 ms]]></result>
<result name="Nom d'hôte"><![CDATA[pizeroComble.local]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Raspberry Pi Foundation]]></result>
<result name="Adresse MAC"><![CDATA[B8:27:EB:7C:6D:F4]]></result>
<result name="Détection Web"><![CDATA[Apache/2.4.25 (Raspbian)]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\PIZEROCOMBLE@PIZEROCOMBLE [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.26">
<result name="IP"><![CDATA[10.0.0.26]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-docker26.local]]></result>
<result name="Ports"><![CDATA[22,8006]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[8E:B4:AB:2A:BA:7B]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-DOCKER26@VM-DOCKER26 [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.17">
<result name="IP"><![CDATA[10.0.0.17]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:FA:37:3E]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.7">
<result name="IP"><![CDATA[10.0.0.7]]></result>
<result name="Ping"><![CDATA[3 ms]]></result>
<result name="Nom d'hôte"><![CDATA[pichaudiere.local]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Raspberry Pi Foundation]]></result>
<result name="Adresse MAC"><![CDATA[B8:27:EB:33:74:41]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\PICHAUDIERE@PICHAUDIERE [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.101">
<result name="IP"><![CDATA[10.0.0.101]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[elitedesk.local]]></result>
<result name="Ports"><![CDATA[22,8006]]></result>
<result name="Constructeur MAC"><![CDATA[Hewlett Packard]]></result>
<result name="Adresse MAC"><![CDATA[18:60:24:B0:80:EC]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\ELITEDESK@ELITEDESK [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.90">
<result name="IP"><![CDATA[10.0.0.90]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Jinan USR IOT]]></result>
<result name="Adresse MAC"><![CDATA[D4:AD:20:3C:BD:A6]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.105">
<result name="IP"><![CDATA[10.0.0.105]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[VM-ZIGBEE2MQTT]]></result>
<result name="Ports"><![CDATA[22,8080,9000]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:AF:57:52]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-ZIGBEE2MQTT@VM-ZIGBEE2MQTT [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.106">
<result name="IP"><![CDATA[10.0.0.106]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[VM-ZIGBEE2MQTT]]></result>
<result name="Ports"><![CDATA[22,8080,9000]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:F9:8F:44]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-ZIGBEE2MQTT@VM-ZIGBEE2MQTT [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.116">
<result name="IP"><![CDATA[10.0.0.116]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[VM-DOCKER116]]></result>
<result name="Ports"><![CDATA[22,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:5A:27:67]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-DOCKER116@VM-DOCKER116 [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.135">
<result name="IP"><![CDATA[10.0.0.135]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[VM-DOCKER135]]></result>
<result name="Ports"><![CDATA[22,8080,9000]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:B9:96:72]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-DOCKER135@VM-DOCKER135 [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.201">
<result name="IP"><![CDATA[10.0.0.201]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-backup-pve.local]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:E0:A5:E0]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-BACKUP-PVE@VM-BACKUP-PVE [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.202">
<result name="IP"><![CDATA[10.0.0.202]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-vaultwarden.local]]></result>
<result name="Ports"><![CDATA[22,80,443,8080,9000]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:B3:15:32]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-VAULTWARDEN@VM-VAULTWARDEN [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.203">
<result name="IP"><![CDATA[10.0.0.203]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[VM-HTTP]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:89:DF:80]]></result>
<result name="Détection Web"><![CDATA[nginx]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-HTTP@VM-HTTP [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.214">
<result name="IP"><![CDATA[10.0.0.214]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-partage.local]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:DF:4B:F9]]></result>
<result name="Détection Web"><![CDATA[Apache/2.4.65 (Debian)]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\DEBIAN-SHARE@DEBIAN-SHARE [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.186">
<result name="IP"><![CDATA[10.0.0.186]]></result>
<result name="Ping"><![CDATA[1 ms]]></result>
<result name="Nom d'hôte"><![CDATA[n-addrarpa x
sonoff-r2-vmclocal]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[60:01:94:9E:7E:8C]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.92">
<result name="IP"><![CDATA[10.0.0.92]]></result>
<result name="Ping"><![CDATA[1000 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Jinan USR IOT]]></result>
<result name="Adresse MAC"><![CDATA[D4:AD:20:6A:B3:62]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.222">
<result name="IP"><![CDATA[10.0.0.222]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[vm-wireguard.local]]></result>
<result name="Ports"><![CDATA[22,9000]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[CA:07:65:BD:89:32]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\VM-WIREGUARD@VM-WIREGUARD [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.0.233">
<result name="IP"><![CDATA[10.0.0.233]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[F6:70:00:08:DB:33]]></result>
<result name="Détection Web"><![CDATA[thttpd/2.29 23May2018]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.234">
<result name="IP"><![CDATA[10.0.0.234]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,80,443]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:76:89:2F]]></result>
<result name="Détection Web"><![CDATA[nginx/1.27.5]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.251">
<result name="IP"><![CDATA[10.0.0.251]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,80,443]]></result>
<result name="Constructeur MAC"><![CDATA[TP-LINK]]></result>
<result name="Adresse MAC"><![CDATA[50:C7:BF:3B:FD:0A]]></result>
<result name="Détection Web"><![CDATA[TP-LINK HTTPD/1.0]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.3">
<result name="IP"><![CDATA[10.0.1.3]]></result>
<result name="Ping"><![CDATA[2 ms]]></result>
<result name="Nom d'hôte"><![CDATA[linux.local]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Amazon]]></result>
<result name="Adresse MAC"><![CDATA[4C:53:FD:27:AF:65]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.12">
<result name="IP"><![CDATA[10.0.1.12]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[solar-assistant-7.local]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Raspberry Pi Trading]]></result>
<result name="Adresse MAC"><![CDATA[DC:A6:32:72:EF:EC]]></result>
<result name="Détection Web"><![CDATA[Cowboy]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.14">
<result name="IP"><![CDATA[10.0.1.14]]></result>
<result name="Ping"><![CDATA[10 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[48:3F:DA:89:AF:AD]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.22">
<result name="IP"><![CDATA[10.0.1.22]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[40:91:51:29:27:5B]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.23">
<result name="IP"><![CDATA[10.0.1.23]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[08:A6:F7:A1:F7:43]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.12">
<result name="IP"><![CDATA[10.0.0.12]]></result>
<result name="Ping"><![CDATA[1001 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[28:67:96:C1:D7:C0]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.38">
<result name="IP"><![CDATA[10.0.1.38]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,80,443]]></result>
<result name="Constructeur MAC"><![CDATA[Ubiquiti]]></result>
<result name="Adresse MAC"><![CDATA[68:72:51:30:E5:72]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.40">
<result name="IP"><![CDATA[10.0.1.40]]></result>
<result name="Ping"><![CDATA[3 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443]]></result>
<result name="Constructeur MAC"><![CDATA[TP-Link]]></result>
<result name="Adresse MAC"><![CDATA[98:03:8E:9F:84:24]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.41">
<result name="IP"><![CDATA[10.0.1.41]]></result>
<result name="Ping"><![CDATA[1 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,80,443]]></result>
<result name="Constructeur MAC"><![CDATA[Ubiquiti]]></result>
<result name="Adresse MAC"><![CDATA[68:72:51:30:E4:03]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.0.1">
<result name="IP"><![CDATA[10.0.0.1]]></result>
<result name="Ping"><![CDATA[1001 ms]]></result>
<result name="Nom d'hôte"><![CDATA[_gateway]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:74:64:7E]]></result>
<result name="Détection Web"><![CDATA[OPNsense]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.85">
<result name="IP"><![CDATA[10.0.1.85]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[ShellyPro3EM-ECE334EAA844.local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[EC:E3:34:EA:A8:47]]></result>
<result name="Détection Web"><![CDATA[ShellyHTTP/1.0.0]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.105">
<result name="IP"><![CDATA[10.0.1.105]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[homeassistant4.local]]></result>
<result name="Ports"><![CDATA[8123]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[02:7E:D5:BA:2F:1B]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\HOMEASSISTANT@HOMEASSISTANT [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.1.109">
<result name="IP"><![CDATA[10.0.1.109]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[aorus]]></result>
<result name="Ports"><![CDATA[22,8080]]></result>
<result name="Constructeur MAC"><![CDATA[GIGA-BYTE]]></result>
<result name="Adresse MAC"><![CDATA[18:C0:4D:B5:65:74]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[HOME\AORUS@AORUS [00-00-00-00-00-00]]]></result>
</host>
<host address="10.0.1.99">
<result name="IP"><![CDATA[10.0.1.99]]></result>
<result name="Ping"><![CDATA[2 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443]]></result>
<result name="Constructeur MAC"><![CDATA[TP-Link]]></result>
<result name="Adresse MAC"><![CDATA[A8:29:48:36:99:04]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.97">
<result name="IP"><![CDATA[10.0.1.97]]></result>
<result name="Ping"><![CDATA[14 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,8006,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Intel Corporate]]></result>
<result name="Adresse MAC"><![CDATA[F4:8C:50:88:10:FD]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.123">
<result name="IP"><![CDATA[10.0.1.123]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22]]></result>
<result name="Constructeur MAC"><![CDATA[Proxmox Server]]></result>
<result name="Adresse MAC"><![CDATA[BC:24:11:12:2E:00]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.130">
<result name="IP"><![CDATA[10.0.1.130]]></result>
<result name="Ping"><![CDATA[4 ms]]></result>
<result name="Nom d'hôte"><![CDATA[ext.local]]></result>
<result name="Ports"><![CDATA[80,443]]></result>
<result name="Constructeur MAC"><![CDATA[NETGEAR]]></result>
<result name="Adresse MAC"><![CDATA[54:07:7D:8C:B8:94]]></result>
<result name="Détection Web"><![CDATA[nginx/1.21.0]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.129">
<result name="IP"><![CDATA[10.0.1.129]]></result>
<result name="Ping"><![CDATA[18 ms]]></result>
<result name="Nom d'hôte"><![CDATA[ipad-2-de-Denis.local]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[[n/a]]]></result>
<result name="Adresse MAC"><![CDATA[26:F6:36:98:5B:27]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.144">
<result name="IP"><![CDATA[10.0.1.144]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[TP-LINK]]></result>
<result name="Adresse MAC"><![CDATA[90:9A:4A:BA:1C:55]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.146">
<result name="IP"><![CDATA[10.0.1.146]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[JRC TOKKI]]></result>
<result name="Adresse MAC"><![CDATA[00:12:33:CC:E2:BC]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.149">
<result name="IP"><![CDATA[10.0.1.149]]></result>
<result name="Ping"><![CDATA[3 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443]]></result>
<result name="Constructeur MAC"><![CDATA[TP-Link]]></result>
<result name="Adresse MAC"><![CDATA[98:03:8E:9F:AA:50]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.10">
<result name="IP"><![CDATA[10.0.1.10]]></result>
<result name="Ping"><![CDATA[292 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[9000]]></result>
<result name="Constructeur MAC"><![CDATA[Google]]></result>
<result name="Adresse MAC"><![CDATA[48:D6:D5:7C:6E:1B]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.82">
<result name="IP"><![CDATA[10.0.1.82]]></result>
<result name="Ping"><![CDATA[140 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Xiaomi]]></result>
<result name="Adresse MAC"><![CDATA[34:1C:F0:CC:06:64]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.195">
<result name="IP"><![CDATA[10.0.1.195]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443]]></result>
<result name="Constructeur MAC"><![CDATA[TP-Link]]></result>
<result name="Adresse MAC"><![CDATA[A8:29:48:36:7C:7C]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.198">
<result name="IP"><![CDATA[10.0.1.198]]></result>
<result name="Ping"><![CDATA[2 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443]]></result>
<result name="Constructeur MAC"><![CDATA[TP-Link]]></result>
<result name="Adresse MAC"><![CDATA[A8:29:48:36:98:E0]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.192">
<result name="IP"><![CDATA[10.0.1.192]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443,9000]]></result>
<result name="Constructeur MAC"><![CDATA[Reolink Innovation]]></result>
<result name="Adresse MAC"><![CDATA[EC:71:DB:D9:4D:BF]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.212">
<result name="IP"><![CDATA[10.0.1.212]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[solar-assistant-7.local]]></result>
<result name="Ports"><![CDATA[22,80]]></result>
<result name="Constructeur MAC"><![CDATA[Raspberry Pi Trading]]></result>
<result name="Adresse MAC"><![CDATA[DC:A6:32:72:EF:EC]]></result>
<result name="Détection Web"><![CDATA[Cowboy]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.232">
<result name="IP"><![CDATA[10.0.1.232]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[22,8006]]></result>
<result name="Constructeur MAC"><![CDATA[LiteON]]></result>
<result name="Adresse MAC"><![CDATA[6C:4B:90:5D:7B:1C]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.233">
<result name="IP"><![CDATA[10.0.1.233]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[JRC TOKKI]]></result>
<result name="Adresse MAC"><![CDATA[00:12:33:CC:E2:BC]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.243">
<result name="IP"><![CDATA[10.0.1.243]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Altenergy Power System]]></result>
<result name="Adresse MAC"><![CDATA[80:97:1B:04:12:1E]]></result>
<result name="Détection Web"><![CDATA[lighttpd/1.4.55]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.244">
<result name="IP"><![CDATA[10.0.1.244]]></result>
<result name="Ping"><![CDATA[0 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,443,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Altenergy Power System]]></result>
<result name="Adresse MAC"><![CDATA[80:97:1B:04:12:1E]]></result>
<result name="Détection Web"><![CDATA[lighttpd/1.4.55]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.181">
<result name="IP"><![CDATA[10.0.1.181]]></result>
<result name="Ping"><![CDATA[202 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Tuya Smart]]></result>
<result name="Adresse MAC"><![CDATA[50:8B:B9:E5:F7:92]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.57">
<result name="IP"><![CDATA[10.0.2.57]]></result>
<result name="Ping"><![CDATA[4 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa x
sonoff-bureaulocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[2C:F4:32:A5:2F:EF]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.60">
<result name="IP"><![CDATA[10.0.2.60]]></result>
<result name="Ping"><![CDATA[4 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xeclairage-boislocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[DC:4F:22:4B:74:BA]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.62">
<result name="IP"><![CDATA[10.0.2.62]]></result>
<result name="Ping"><![CDATA[6 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xsonoff-dressinglocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[2C:F4:32:BE:D8:97]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.70">
<result name="IP"><![CDATA[10.0.2.70]]></result>
<result name="Ping"><![CDATA[5 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xsonoff-4ch-garagelocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[9C:9C:1F:9F:B6:13]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.89">
<result name="IP"><![CDATA[10.0.2.89]]></result>
<result name="Ping"><![CDATA[9 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa x
wemos_cavelocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[50:02:91:78:DC:0A]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.84">
<result name="IP"><![CDATA[10.0.2.84]]></result>
<result name="Ping"><![CDATA[6 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa x
wemos-bureau0local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:DB:84:DC:C8:B3]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.13">
<result name="IP"><![CDATA[10.0.2.13]]></result>
<result name="Ping"><![CDATA[198 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:68:E7:5E:BE:56]]></result>
<result name="Détection Web"><![CDATA[Tasmota/12.4.0 (ESP8266EX)]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.19">
<result name="IP"><![CDATA[10.0.2.19]]></result>
<result name="Ping"><![CDATA[258 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[48:55:19:60:56:46]]></result>
<result name="Détection Web"><![CDATA[Tasmota/13.1.0 (ESP8285N08)]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.59">
<result name="IP"><![CDATA[10.0.2.59]]></result>
<result name="Ping"><![CDATA[615 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xsonoff-couloirlocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[DC:4F:22:B0:B1:9F]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.75">
<result name="IP"><![CDATA[10.0.2.75]]></result>
<result name="Ping"><![CDATA[530 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletsalon2local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[A0:20:A6:1C:43:73]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.78">
<result name="IP"><![CDATA[10.0.2.78]]></result>
<result name="Ping"><![CDATA[520 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletcuisine1local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:DB:84:9A:DF:FB]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.58">
<result name="IP"><![CDATA[10.0.2.58]]></result>
<result name="Ping"><![CDATA[586 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xsonoff-escalierlocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[DC:4F:22:9E:AB:D3]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.81">
<result name="IP"><![CDATA[10.0.2.81]]></result>
<result name="Ping"><![CDATA[415 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletchambre2local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[9C:9C:1F:47:98:97]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.79">
<result name="IP"><![CDATA[10.0.2.79]]></result>
<result name="Ping"><![CDATA[546 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletcuisine2local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:DB:84:98:B8:CA]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.76">
<result name="IP"><![CDATA[10.0.2.76]]></result>
<result name="Ping"><![CDATA[527 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletportelocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:DB:84:98:F8:BA]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.80">
<result name="IP"><![CDATA[10.0.2.80]]></result>
<result name="Ping"><![CDATA[541 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletchambre1local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:DB:84:9C:3E:92]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.154">
<result name="IP"><![CDATA[10.0.2.154]]></result>
<result name="Ping"><![CDATA[7 ms]]></result>
<result name="Nom d'hôte"><![CDATA[n-addrarpa xsonoff-pow-r2-333fcflocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[80:7D:3A:33:3F:CF]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.77">
<result name="IP"><![CDATA[10.0.2.77]]></result>
<result name="Ping"><![CDATA[15 ms]]></result>
<result name="Nom d'hôte"><![CDATA[-addrarpa xnmcuvoletarriere1local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[E8:DB:84:99:72:31]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.201">
<result name="IP"><![CDATA[10.0.2.201]]></result>
<result name="Ping"><![CDATA[5 ms]]></result>
<result name="Nom d'hôte"><![CDATA[n-addrarpa x
sonoff-4chlocal]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[DC:4F:22:CA:A5:51]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.16">
<result name="IP"><![CDATA[10.0.1.16]]></result>
<result name="Ping"><![CDATA[4004 ms]]></result>
<result name="Nom d'hôte"><![CDATA[none.local]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Amazon]]></result>
<result name="Adresse MAC"><![CDATA[CC:9E:A2:8A:04:02]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.232">
<result name="IP"><![CDATA[10.0.2.232]]></result>
<result name="Ping"><![CDATA[4 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[50:02:91:66:96:F9]]></result>
<result name="Détection Web"><![CDATA[Tasmota/12.4.0 (ESP8266EX)]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.163">
<result name="IP"><![CDATA[10.0.1.163]]></result>
<result name="Ping"><![CDATA[1002 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[TP-Link]]></result>
<result name="Adresse MAC"><![CDATA[00:31:92:B3:EF:F3]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.221">
<result name="IP"><![CDATA[10.0.2.221]]></result>
<result name="Ping"><![CDATA[11 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[34:94:54:05:39:A4]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.43">
<result name="IP"><![CDATA[10.0.2.43]]></result>
<result name="Ping"><![CDATA[716 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[80:65:99:4D:C2:E8]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.241">
<result name="IP"><![CDATA[10.0.2.241]]></result>
<result name="Ping"><![CDATA[264 ms]]></result>
<result name="Nom d'hôte"><![CDATA[n-addrarpa xmeuble-dashboard-02b0e3local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[4C:EB:D6:02:B0:E3]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.215">
<result name="IP"><![CDATA[10.0.2.215]]></result>
<result name="Ping"><![CDATA[1035 ms]]></result>
<result name="Nom d'hôte"><![CDATA[n-addrarpa xh801light-6f9188local]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[60:01:94:6F:91:88]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.153">
<result name="IP"><![CDATA[10.0.2.153]]></result>
<result name="Ping"><![CDATA[284 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[30:C6:F7:5A:10:C8]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.177">
<result name="IP"><![CDATA[10.0.2.177]]></result>
<result name="Ping"><![CDATA[1394 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[10:97:BD:D7:58:D0]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.238">
<result name="IP"><![CDATA[10.0.2.238]]></result>
<result name="Ping"><![CDATA[148 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[58:BF:25:37:D3:F8]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.2.239">
<result name="IP"><![CDATA[10.0.2.239]]></result>
<result name="Ping"><![CDATA[413 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[80,8080]]></result>
<result name="Constructeur MAC"><![CDATA[Espressif]]></result>
<result name="Adresse MAC"><![CDATA[94:B9:7E:D2:9B:2C]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.217">
<result name="IP"><![CDATA[10.0.1.217]]></result>
<result name="Ping"><![CDATA[4004 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Apple]]></result>
<result name="Adresse MAC"><![CDATA[C4:2A:D0:7B:38:04]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>
</host>
<host address="10.0.1.226">
<result name="IP"><![CDATA[10.0.1.226]]></result>
<result name="Ping"><![CDATA[4003 ms]]></result>
<result name="Nom d'hôte"><![CDATA[[n/a]]]></result>
<result name="Ports"><![CDATA[[n/a]]]></result>
<result name="Constructeur MAC"><![CDATA[Apple]]></result>
<result name="Adresse MAC"><![CDATA[A4:F6:E8:44:DB:2C]]></result>
<result name="Détection Web"><![CDATA[[n/a]]]></result>
<result name="Info NetBIOS"><![CDATA[[n/a]]]></result>