// src/integrations/mdm/client.js // VMware Workspace ONE / AirWatch MDM integration import axios from 'axios'; import { logger } from '../../utils/logger.js'; const MDM_BASE_URL = 'https://as1991.awmdm.com'; const TOKEN_URL = 'https://na.uemauth.workspaceone.com/connect/token'; // Dedicated axios instance export const mdmAxios = axios.create({ baseURL: MDM_BASE_URL, timeout: 12000, headers: { 'Accept': 'application/json', }, }); // ────────────────────────────────────────────── // Get OAuth token (client_credentials flow) // ────────────────────────────────────────────── export async function getMDMToken() { logger('mdm:token', 'Requesting new MDM OAuth token', 'debug'); try { const response = await axios.post( TOKEN_URL, new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.WS1_CLIENT_ID, client_secret: process.env.WS1_CLIENT_SECRET, }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, } ); const accessToken = response.data.access_token; logger('mdm:token', `MDM token acquired successfully (length: ${accessToken.length})`, 'debug'); return accessToken; } catch (err) { const msg = err.response ? `${err.response.status} – ${JSON.stringify(err.response.data)}` : err.message; logger('mdm:token', `Failed to acquire MDM token: ${msg}`, 'error'); throw new Error(`MDM token fetch failed: ${msg}`); } } // ────────────────────────────────────────────── // Get MDM devices for a store // ────────────────────────────────────────────── export async function getMDMDevices2(storeNum) { logger('mdm:device', `Fetching devices for store ${storeNum}`, 'debug'); const storeNumPadded = String(storeNum).padStart(6, '0'); try { const accessToken = await getMDMToken(); logger('mdm:device', `Searching devices for user/store: ${storeNumPadded}`, 'debug'); const response = await axios.get('/api/mdm/devices/search', { baseURL: MDM_BASE_URL, params: { user: storeNumPadded }, headers: { Authorization: `Bearer ${accessToken}`, 'aw-tenant-code': process.env.WS1_TENANT_CODE, Accept: 'application/json' } }); let devices = response.data.Devices || []; logger('mdm:device', `Raw devices returned from MDM: ${devices.length}`, 'debug'); logger('mdm:client', `Fetched ${response.data.Devices?.length || 0} devices from MDM`); const result = devices.map(d => ({ friendlyName: d.DeviceFriendlyName || 'Unknown', serialNumber: d.SerialNumber || '—', lastSeen: d.LastSeen || d.LastSystemSampleTime || 'Unknown', locationGroup: d.LocationGroupId?.Name || d.LocationGroupName || 'Unknown', orgGroupId: parseInt(d.OrganizationalGroupID || d.orgGroupId || d.OrganizationalGroup || d.groupId || 0, 10) })); logger('mdm:device', `Returning ${result.length} MDM devices for store ${storeNum}`, 'debug'); return result; } catch (err) { logger('mdm:device', `Error fetching devices for store ${storeNum}: ${err.message}`, 'error'); if (err.response) { logger('mdm:device', `MDM API error status: ${err.response.status}`, 'error'); } return []; } } export async function getMDMDevices(storeNum) { logger('mdm:device', `Fetching devices for store ${storeNum}`, 'debug'); const storeNumPadded = String(storeNum).padStart(6, '0'); try { const accessToken = await getMDMToken(); logger('mdm:device', `Searching devices for user/store: ${storeNumPadded}`, 'debug'); const response = await axios.get('/api/mdm/devices/search', { baseURL: MDM_BASE_URL, params: { user: storeNumPadded }, headers: { Authorization: `Bearer ${accessToken}`, 'aw-tenant-code': process.env.WS1_TENANT_CODE, Accept: 'application/json' } }); let devices = response.data.Devices || []; logger('mdm:device', `Raw devices returned from MDM: ${devices.length}`, 'debug'); logger('mdm:device', `Returning ${devices.length} MDM devices for store ${storeNum}`, 'debug'); return devices; } catch (err) { logger('mdm:device', `Error fetching devices for store ${storeNum}: ${err.message}`, 'error'); if (err.response) { logger('mdm:device', `MDM API error status: ${err.response.status}`, 'error'); } return []; } } /** * Get ALL devices with NO filtering whatsoever * No platform parameter, no client-side filtering. */ export async function getMDMDevicesByPlatform(platformFilter = null, maxDevices = 9999) { logger('mdm:device', `Fetching ALL devices (broad search, no filters at all)`); let allDevices = []; let page = 0; const pageSize = 500; try { while (allDevices.length < maxDevices) { const accessToken = await getMDMToken(); const response = await axios.get('/api/mdm/devices/search', { baseURL: 'https://as1991.awmdm.com', params: { page: page, page_size: pageSize, platform: platformFilter // NO platform, NO group, NO other filters }, headers: { Authorization: `Bearer ${accessToken}`, 'aw-tenant-code': process.env.WS1_TENANT_CODE, Accept: 'application/json' } }); const pageDevices = response.data.Devices || response.data.devices || response.data.results || []; allDevices = allDevices.concat(pageDevices); logger('mdm:device', `Page ${page} returned ${pageDevices.length} devices (total so far: ${allDevices.length})`); if (pageDevices.length < pageSize) break; page++; } logger('mdm:device', `Broad fetch complete – ${allDevices.length} total devices returned (NO filtering applied)`); return allDevices.slice(0, maxDevices); } catch (err) { logger('mdm:device', `Broad fetch failed: ${err.message}`, 'error'); return []; } } /** * Returns ONLY Audio-Visual devices from MDM (VW, MSC, LED in name) * Used specifically for the AV modal view */ export async function getAVMDMDevices(storeNum) { logger('mdm:av', `Fetching AV devices (VW/MSC/LED) for store ${storeNum}`, 'debug'); const allDevices = await getMDMDevices(storeNum); if (!allDevices || allDevices.length === 0) return []; const AV_PATTERN = /(VW|MSC|LED|AppleTV)/i; const avDevices = allDevices.filter(device => { const name = (device.UserName || '').toUpperCase(); return AV_PATTERN.test(name); }); logger('mdm:av', `MDM AV filter: ${allDevices.length} total → ${avDevices.length} AV devices`, 'debug'); return avDevices; } export default mdmAxios;