diff --git a/backend/routers/vlans.py b/backend/routers/vlans.py
index db343ef..6edf041 100644
--- a/backend/routers/vlans.py
+++ b/backend/routers/vlans.py
@@ -97,6 +97,21 @@ def delete_vlan(vlan_pk: int, db: Session = Depends(get_db)):
db_vlan = db.query(models.Vlan).filter(models.Vlan.id == vlan_pk).first()
if not db_vlan:
raise HTTPException(status_code=404, detail="VLAN introuvable")
+
+ # Collect devices with an interface in this VLAN
+ ifaces = (
+ db.query(models.DeviceInterface)
+ .filter(models.DeviceInterface.vlan_id == vlan_pk)
+ .all()
+ )
+ device_ids = {i.device_id for i in ifaces}
+
+ for device_id in device_ids:
+ device = db.query(models.Device).filter(models.Device.id == device_id).first()
+ if device and not device.is_gateway and not device.is_livebox:
+ db.delete(device) # cascade deletes all its interfaces
+
+ # Gateway/livebox interfaces in this VLAN will be SET NULL by SQLAlchemy
db.delete(db_vlan)
db.commit()
return {"ok": True}
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 7120695..beb5c48 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -107,7 +107,7 @@
:devices="devices"
:vlans="vlans"
/>
-
+
diff --git a/frontend/src/components/VlanManager.vue b/frontend/src/components/VlanManager.vue
index 240e19a..b366f27 100644
--- a/frontend/src/components/VlanManager.vue
+++ b/frontend/src/components/VlanManager.vue
@@ -90,7 +90,7 @@ import { ref, reactive } from 'vue'
import { vlansApi } from '../api.js'
import { t, tFmt } from '../i18n.js'
-const props = defineProps({ vlans: Array })
+const props = defineProps({ vlans: Array, devices: { type: Array, default: () => [] } })
const emit = defineEmits(['refresh'])
const showForm = ref(false)
@@ -145,9 +145,18 @@ async function save() {
}
}
+function _affectedCount(vlan) {
+ return props.devices.filter(
+ d => !d.is_gateway && !d.is_livebox && d.interfaces.some(i => i.vlan_id === vlan.id)
+ ).length
+}
+
async function remove(vlan) {
const label = vlan.vlan_id != null ? `VLAN ${vlan.vlan_id} — ${vlan.name}` : `LAN ${vlan.name}`
- if (!confirm(`Supprimer ${tFmt('confirmDeleteNetwork', label)}`)) return
+ const count = _affectedCount(vlan)
+ let msg = `Supprimer ${tFmt('confirmDeleteNetwork', label)}`
+ if (count > 0) msg += '\n' + tFmt('confirmDeleteNetworkHosts', count)
+ if (!confirm(msg)) return
try {
await vlansApi.remove(vlan.id)
emit('refresh')
diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js
index b4ca8a1..4988b55 100644
--- a/frontend/src/i18n.js
+++ b/frontend/src/i18n.js
@@ -70,6 +70,7 @@ const LANGS = {
badgeLivebox: 'Livebox',
confirmDeleteDevice: '{0} et tous ses liens ?',
confirmDeleteNetwork: '{0} ?',
+ confirmDeleteNetworkHosts: '{0} équipement(s) hors passerelle seront également supprimés.',
saveError: 'Erreur lors de la sauvegarde',
deleteError: 'Erreur lors de la suppression',
descPlaceholder: 'Rôle, OS, notes…',
@@ -229,6 +230,7 @@ const LANGS = {
badgeLivebox: 'ISP Box',
confirmDeleteDevice: '{0} and all its links?',
confirmDeleteNetwork: '{0}?',
+ confirmDeleteNetworkHosts: '{0} non-gateway device(s) will also be deleted.',
saveError: 'Error while saving',
deleteError: 'Error while deleting',
descPlaceholder: 'Role, OS, notes…',
@@ -384,6 +386,7 @@ const LANGS = {
badgeLivebox: 'Router ISP',
confirmDeleteDevice: '{0} y todos sus enlaces?',
confirmDeleteNetwork: '{0}?',
+ confirmDeleteNetworkHosts: '{0} equipo(s) no gateway también serán eliminados.',
saveError: 'Error al guardar',
deleteError: 'Error al eliminar',
descPlaceholder: 'Rol, SO, notas…',