273 lines
7.3 KiB
JavaScript
273 lines
7.3 KiB
JavaScript
import { subMinutes } from 'date-fns'
|
|
import * as unifiController from './unifiController'
|
|
import * as ciscoController from './ciscoController'
|
|
|
|
import prisma from '../prisma'
|
|
|
|
import { pubsub, USER_PRESENCE_UPDATED, ACCESS_POINTS_UPDATED } from '../pubsub'
|
|
import { logError, logInfo, logLow, logSuccess } from './logger'
|
|
|
|
import { performance } from 'perf_hooks'
|
|
|
|
const RECENT_THRESHOLD_IN_MINUTES = 2
|
|
|
|
const OLD_DEVICES_THRESHOLD_IN_DAYS = 90
|
|
|
|
const TIMEOUT_IN_MILLISECONDS = process.env.TASK_TIMEOUT || 120000
|
|
|
|
let working = false
|
|
|
|
const wifiControllers = [unifiController, ciscoController]
|
|
|
|
async function getOnlineDevices() {
|
|
const onlineDevicesPromises = wifiControllers.map(wifiController =>
|
|
wifiController.getOnlineWifiDevices()
|
|
)
|
|
|
|
const onlineDevices = (await Promise.all(onlineDevicesPromises)).flat()
|
|
|
|
return onlineDevices
|
|
}
|
|
|
|
async function updateDevicesStatus(onlineDevices) {
|
|
const lastSeenThreshold = subMinutes(new Date(), RECENT_THRESHOLD_IN_MINUTES)
|
|
|
|
const onlineDevicesMacs = onlineDevices.map(device => device.mac)
|
|
|
|
const recent = prisma.wifiDevice.updateMany({
|
|
where: {
|
|
lastSeen: { gt: lastSeenThreshold },
|
|
status: 'ONLINE',
|
|
mac: { notIn: onlineDevicesMacs }
|
|
},
|
|
data: { status: 'RECENT' }
|
|
})
|
|
|
|
const offline = prisma.wifiDevice.updateMany({
|
|
where: {
|
|
lastSeen: { lte: lastSeenThreshold },
|
|
status: { not: 'OFFLINE' },
|
|
mac: { notIn: onlineDevicesMacs }
|
|
},
|
|
data: { status: 'OFFLINE' }
|
|
})
|
|
|
|
return prisma.$transaction([recent, offline])
|
|
}
|
|
|
|
async function forceUserDisconnect(mac) {
|
|
try {
|
|
await prisma.wifiDevice.update({
|
|
where: { mac },
|
|
data: {
|
|
user: {
|
|
disconnect: true
|
|
}
|
|
}
|
|
})
|
|
} catch (e) {
|
|
logError({
|
|
tags: ['wifiDevices'],
|
|
message: `Erro tentando desconectar o usuário do dispositivo "${mac}".`,
|
|
data: { error: e, mac }
|
|
})
|
|
}
|
|
}
|
|
|
|
function mockHostName({ mac, oui }) {
|
|
const shortOui = oui?.split(' ')[0] || 'desc'
|
|
const clearMac = mac ? mac.replaceAll(':', '') : ''
|
|
|
|
return `${shortOui}_${clearMac}`
|
|
}
|
|
|
|
async function updateDB(onlineDevices) {
|
|
const devicesUpsertPromises = onlineDevices.map(async device => {
|
|
if (!device.user) forceUserDisconnect(device.mac)
|
|
|
|
const user = device.user
|
|
? {
|
|
connect: {
|
|
sAMAccountName: device.user.replace('IFMS\\', '').toLowerCase()
|
|
}
|
|
}
|
|
: undefined
|
|
|
|
const hostname = device.hostname || mockHostName(device)
|
|
|
|
return prisma.wifiDevice
|
|
.upsert({
|
|
where: { mac: device.mac },
|
|
create: {
|
|
...device,
|
|
hostname,
|
|
firstSeen: device.firstSeen || new Date(),
|
|
user,
|
|
accessPoint: {
|
|
connect: {
|
|
hostname: device.apName
|
|
}
|
|
}
|
|
},
|
|
update: {
|
|
...device,
|
|
hostname,
|
|
user,
|
|
accessPoint: {
|
|
connect: {
|
|
hostname: device.apName
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.catch(async e => {
|
|
// If is a binding problem, probably the device has an user outside of AD, so save it anyway...
|
|
if (e.code == 'P2025')
|
|
try {
|
|
await forceUserDisconnect(device.mac)
|
|
await prisma.wifiDevice.upsert({
|
|
where: { mac: device.mac },
|
|
create: {
|
|
...device,
|
|
hostname,
|
|
firstSeen: device.firstSeen || new Date(),
|
|
user: undefined,
|
|
accessPoint: {
|
|
connect: {
|
|
hostname: device.apName
|
|
}
|
|
}
|
|
},
|
|
update: {
|
|
...device,
|
|
hostname,
|
|
user: undefined,
|
|
accessPoint: {
|
|
connect: {
|
|
hostname: device.apName
|
|
}
|
|
}
|
|
}
|
|
})
|
|
} catch (e) {
|
|
logError({
|
|
tags: ['wifiDevices'],
|
|
message: `Erro ao adicionar o dispositivo "${device.mac}" AP: ${device.apMac}. Ele tinha um usuário fora do AD que foi ignorado, mas falhou mesmo assim. ${e.message} `,
|
|
data: { error: e, device }
|
|
})
|
|
}
|
|
else
|
|
logError({
|
|
tags: ['wifiDevices'],
|
|
message: `Erro tentando adicionar o dispositivo "${device.mac}: ${e.message}".`,
|
|
data: { error: e, device }
|
|
})
|
|
})
|
|
})
|
|
|
|
return Promise.allSettled(devicesUpsertPromises)
|
|
}
|
|
|
|
function updateDevicesInfo() {
|
|
return new Promise(async (resolve, reject) => {
|
|
if (working) return reject('A última atualização ainda não terminou')
|
|
|
|
working = true
|
|
|
|
const updateTimeout = setTimeout(() => {
|
|
reject('A função atingiu seu tempo limite.')
|
|
working = false
|
|
}, TIMEOUT_IN_MILLISECONDS)
|
|
|
|
try {
|
|
const startTime = performance.now()
|
|
|
|
const onlineDevices = await getOnlineDevices()
|
|
|
|
await updateDevicesStatus(onlineDevices)
|
|
|
|
const endTime = performance.now()
|
|
|
|
const startTimeDB = performance.now()
|
|
|
|
await updateDB(onlineDevices)
|
|
const endTimeDB = performance.now()
|
|
|
|
onlineDevices.length > 0
|
|
? resolve(
|
|
`${onlineDevices.length} atualizados em ${Math.floor(
|
|
endTime - startTime
|
|
)}ms, DB em ${Math.floor(endTimeDB - startTimeDB)}ms`
|
|
)
|
|
: reject('Não há dispositivos conectados no momento.')
|
|
|
|
pubsub.publish(USER_PRESENCE_UPDATED, {
|
|
userPresenceUpdated: onlineDevices.length
|
|
})
|
|
|
|
const updatedDbAccessPoints = await prisma.accessPoint.findMany({
|
|
include: {
|
|
wifiDevices: {
|
|
where: {
|
|
status: 'ONLINE'
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
pubsub.publish(ACCESS_POINTS_UPDATED, {
|
|
accessPointsUpdated: updatedDbAccessPoints
|
|
})
|
|
|
|
logSuccess({
|
|
tags: ['wifiDevices'],
|
|
message: `${onlineDevices.length} dispositivos Wi-Fi atualizados em
|
|
${((endTime - startTime) / 1000).toFixed(2)}s. BD em ${(
|
|
(endTimeDB - startTimeDB) /
|
|
1000
|
|
).toFixed(2)}s`
|
|
})
|
|
} catch (e) {
|
|
logError({
|
|
tags: ['wifiDevices'],
|
|
message: `Erro atualizando dispositivos Wi-Fi: ${e.message}`,
|
|
data: e
|
|
})
|
|
|
|
reject('Não foi possível atualizar as informações dos dispositivos.')
|
|
} finally {
|
|
working = false
|
|
clearTimeout(updateTimeout)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Delete devices that are offline for more than OLD_DEVICES_THRESHOLD_IN_DAYS days
|
|
async function deleteOldDevices() {
|
|
const oldDevicesThresholdInMilliseconds =
|
|
OLD_DEVICES_THRESHOLD_IN_DAYS * 24 * 60 * 60 * 1000
|
|
|
|
const oldDevices = await prisma.wifiDevice.deleteMany({
|
|
where: {
|
|
lastSeen: {
|
|
lt: new Date(Date.now() - oldDevicesThresholdInMilliseconds)
|
|
},
|
|
status: 'OFFLINE'
|
|
}
|
|
})
|
|
|
|
if (oldDevices.count > 0)
|
|
logInfo({
|
|
tags: ['wifiDevices', 'deleteOldDevices'],
|
|
message: `${oldDevices.count} dispositivos Wi-Fi não vistos há mais de ${OLD_DEVICES_THRESHOLD_IN_DAYS} dias foram excluídos.`,
|
|
data: { oldDevices }
|
|
})
|
|
else
|
|
logInfo({
|
|
tags: ['wifiDevices', 'deleteOldDevices'],
|
|
message: `Nenhum dispositivo Wi-Fi não visto há mais de ${OLD_DEVICES_THRESHOLD_IN_DAYS} dias foi encontrado.`
|
|
})
|
|
}
|
|
|
|
export { updateDevicesInfo, deleteOldDevices }
|