2 * @file mobileDevice.js
3 * @module routes/mobileDevice
4 * @description Mobile device inventory management endpoint.
5 * Provides paginated list of mobile devices with filtering and tenant isolation.
7 * @requires services/db
8 * @requires middleware/auth
9 * @requires middleware/tenant
10 * @author RMM-PSA Development Team
11 * @copyright 2026 RMM-PSA Platform
12 * @license Proprietary
16 * @apiDefine MobileDevice Mobile Devices
17 * Mobile device inventory management
20const express = require('express');
21const router = express.Router();
22const pool = require('../services/db');
23const authenticateToken = require('../middleware/auth');
24const { getTenantFilter } = require('../middleware/tenant');
27 * @api {get} /api/mobile-device/list List mobile devices
28 * @apiName ListMobileDevices
29 * @apiGroup MobileDevice
30 * @apiDescription Retrieve paginated list of mobile devices with filtering and search.
31 * Supports filtering by customer, OS, and full-text search. Enforces tenant isolation.
32 * Returns device inventory with customer/contract associations.
33 * @apiParam {number} [page=1] Page number (query parameter)
34 * @apiParam {number} [limit=20] Results per page (query parameter)
35 * @apiParam {number} [customer_id] Filter by customer ID (query parameter)
36 * @apiParam {string} [os] Filter by operating system (query parameter)
37 * @apiParam {string} [search] Search device name, hostname, customer, or OS (query parameter)
38 * @apiSuccess {object[]} devices Array of mobile devices
39 * @apiSuccess {string} devices.device_uuid Device UUID
40 * @apiSuccess {number} devices.device_id Device ID
41 * @apiSuccess {number} devices.tenant_id Tenant ID
42 * @apiSuccess {number} devices.customer_id Customer ID
43 * @apiSuccess {string} devices.hostname Device hostname
44 * @apiSuccess {string} devices.os Operating system (e.g., "iOS", "Android")
45 * @apiSuccess {string} devices.os_version OS version
46 * @apiSuccess {string} devices.version App version
47 * @apiSuccess {DateTime} devices.last_seen Last contact timestamp
48 * @apiSuccess {string} devices.name Device friendly name
49 * @apiSuccess {string} devices.ip_address Device IP address
50 * @apiSuccess {string} devices.user_list Associated users (comma-separated)
51 * @apiSuccess {string} devices.customer_name Customer name
52 * @apiSuccess {String} devices.contract_title Associated contract title
53 * @apiSuccess {Number} total Total device count (for pagination)
55 * @apiError (500) {String} error="Failed to list devices" Database query failed
57 * @apiExample {curl} Example:
58 * curl -X GET "http://localhost:3000/api/mobile-device/list?page=1&limit=20&search=iPhone&customer_id=5" \\\\
59 * -H "Authorization: Bearer YOUR_TOKEN"
61 * @apiSuccessExample {json} Success-Response:
66 * "device_uuid": "mobile-abc-123",
70 * "hostname": "iPhone-John",
72 * "os_version": "17.3",
74 * "last_seen": "2026-03-12T10:30:00.000Z",
75 * "name": "John's iPhone",
76 * "ip_address": "192.168.1.150",
77 * "user_list": "john@acme.com",
78 * "customer_name": "Acme Corp",
79 * "contract_title": "Annual Support"
85router.get('/list', authenticateToken, async (req, res) => {
87 const { clause: tenantClause, params: tenantParams } = getTenantFilter(req, 'm');
88 const conditions = [];
89 const params = [...tenantParams];
90 let paramIdx = tenantParams.length + 1;
91 if (tenantClause) conditions.push(tenantClause);
92 if (req.query.customer_id) {
93 params.push(req.query.customer_id);
94 conditions.push(`m.customer_id = $${paramIdx++}`);
96 if (req.query.search) {
97 const search = `%${req.query.search}%`;
99 conditions.push(`(m.name ILIKE $${paramIdx} OR m.hostname ILIKE $${paramIdx} OR c.name ILIKE $${paramIdx} OR m.os ILIKE $${paramIdx})`);
103 params.push(req.query.os);
104 conditions.push(`m.os = $${paramIdx++}`);
106 const whereClause = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : '';
107 const page = parseInt(req.query.page, 10) || 1;
108 const limit = parseInt(req.query.limit, 10) || 20;
109 const offset = (page - 1) * limit;
110 const totalResult = await pool.query(`
111 SELECT COUNT(*) AS total
112 FROM mobile_devices m
113 LEFT JOIN customers c ON c.customer_id = m.customer_id
116 const total = parseInt(totalResult.rows[0]?.total || '0', 10);
117 const result = await pool.query(`
131 c.name AS customer_name,
132 ct.title AS contract_title
133 FROM mobile_devices m
134 LEFT JOIN customers c ON c.customer_id = m.customer_id
135 LEFT JOIN contracts ct ON ct.contract_id = m.contract_id
138 LIMIT $${paramIdx} OFFSET $${paramIdx + 1}
139 `, [...params, limit, offset]);
140 res.json({ devices: result.rows, total });
142 console.error('[MobileDevice] Failed to list devices:', err);
143 res.status(500).json({ error: 'Failed to list devices' });
147module.exports = router;