156 lines
5.2 KiB
Vue
Executable File
156 lines
5.2 KiB
Vue
Executable File
<template>
|
|
<div class="flex flex-col h-full">
|
|
<!-- Grille d'IPs par sous-réseaux -->
|
|
<div class="flex-1 overflow-auto p-4">
|
|
<!-- 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="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>
|
|
</div>
|
|
|
|
<!-- Légende -->
|
|
<div class="bg-monokai-bg border-t border-monokai-comment p-3">
|
|
<div class="flex gap-6 text-xs">
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-4 h-4 rounded border-2 border-monokai-green bg-monokai-green/15"></div>
|
|
<span class="text-monokai-text">En ligne (connue)</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-4 h-4 rounded border-2 border-monokai-cyan bg-monokai-cyan/15"></div>
|
|
<span class="text-monokai-text">En ligne (inconnue)</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-4 h-4 rounded border-2 border-dashed border-monokai-pink bg-monokai-pink/10 opacity-50"></div>
|
|
<span class="text-monokai-text">Hors ligne (connue)</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-4 h-4 rounded border-2 border-dashed border-monokai-purple bg-monokai-purple/10 opacity-50"></div>
|
|
<span class="text-monokai-text">Hors ligne (inconnue)</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-4 h-4 rounded border-2 border-monokai-comment bg-monokai-comment/20"></div>
|
|
<span class="text-monokai-text">Libre</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</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 } = 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>
|