@xenon-device-management/xenon 1.1.17 → 1.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/package.json +1 -1
- package/lib/schema.json +4 -0
- package/lib/src/app/index.js +27 -13
- package/lib/src/device-managers/AndroidDeviceManager.js +13 -1
- package/lib/src/device-utils.js +16 -0
- package/lib/src/interfaces/IPluginArgs.js +1 -0
- package/lib/src/services/ServerManager.js +5 -0
- package/package.json +1 -1
- package/schema.json +4 -0
package/lib/package.json
CHANGED
package/lib/schema.json
CHANGED
package/lib/src/app/index.js
CHANGED
|
@@ -140,6 +140,7 @@ const findPublicPath = () => {
|
|
|
140
140
|
path_1.default.resolve(__dirname, '../public'), // Alternative structure
|
|
141
141
|
path_1.default.resolve(__dirname, '../../public'),
|
|
142
142
|
path_1.default.resolve(__dirname, '../../../public'),
|
|
143
|
+
path_1.default.resolve(__dirname, '../../../../public'),
|
|
143
144
|
];
|
|
144
145
|
for (const p of searchPaths) {
|
|
145
146
|
if (fs_1.default.existsSync(path_1.default.join(p, 'index.html'))) {
|
|
@@ -153,10 +154,18 @@ const findPublicPath = () => {
|
|
|
153
154
|
return fallback;
|
|
154
155
|
};
|
|
155
156
|
const publicPath = findPublicPath();
|
|
156
|
-
logger_1.default.info(`[Xenon]
|
|
157
|
-
|
|
157
|
+
logger_1.default.info(`[Xenon] Dashboard assets path: ${publicPath}`);
|
|
158
|
+
logger_1.default.info(`[Xenon] Dashboard available at: /xenon/ (e.g. http://localhost:4723/xenon/)`);
|
|
159
|
+
// Principal Security: Add permissive CSP and CORS for the dashboard
|
|
160
|
+
router.use((req, res, next) => {
|
|
161
|
+
res.setHeader('Content-Security-Policy', "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:; frame-ancestors 'self';");
|
|
162
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
163
|
+
next();
|
|
164
|
+
});
|
|
165
|
+
staticFilesRouter.use(express_1.default.static(publicPath, { index: false }));
|
|
158
166
|
router.use('/api', apiRouter);
|
|
159
|
-
|
|
167
|
+
// Principal Fix: Rename collision route from /assets to /session-recordings to avoid conflict with dashboard's /assets folder
|
|
168
|
+
router.use('/session-recordings', express_1.default.static(config_1.config.sessionAssetsPath));
|
|
160
169
|
router.use(staticFilesRouter);
|
|
161
170
|
function createRouter(pluginArgs) {
|
|
162
171
|
dashboard_1.default.register(apiRouter);
|
|
@@ -184,19 +193,24 @@ function createRouter(pluginArgs) {
|
|
|
184
193
|
});
|
|
185
194
|
// Fallback route for client-side routing - serve index.html for all non-API routes
|
|
186
195
|
// MUST be registered after Swagger to avoid interception
|
|
187
|
-
router.get(
|
|
196
|
+
router.get('*', (req, res) => {
|
|
188
197
|
const indexPath = path_1.default.resolve(publicPath, 'index.html');
|
|
189
198
|
const url = req.originalUrl || req.url;
|
|
199
|
+
// Skip if it's an API call that somehow reached here
|
|
200
|
+
if (url.includes('/api/'))
|
|
201
|
+
return res.status(404).json({ error: true, message: 'Not Found' });
|
|
190
202
|
logger_1.default.debug(`[Xenon] UI Fallback triggered for: ${url}. Targeting: ${indexPath}`);
|
|
191
203
|
if (fs_1.default.existsSync(indexPath)) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
try {
|
|
205
|
+
const html = fs_1.default.readFileSync(indexPath, 'utf-8');
|
|
206
|
+
res.set('Content-Type', 'text/html');
|
|
207
|
+
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
208
|
+
return res.send(html);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
logger_1.default.error(`[Xenon] UI Fallback read error for ${indexPath}: ${err.message}`);
|
|
212
|
+
return res.status(500).send(`Xenon UI Asset Error: ${err.message}`);
|
|
213
|
+
}
|
|
200
214
|
}
|
|
201
215
|
else {
|
|
202
216
|
logger_1.default.error(`[Xenon] UI Fallback failed: index.html not found at ${indexPath}`);
|
|
@@ -208,7 +222,7 @@ function createRouter(pluginArgs) {
|
|
|
208
222
|
catch (e) {
|
|
209
223
|
logger_1.default.error(`[Xenon] Could not even read directory ${publicPath}: ${e.message}`);
|
|
210
224
|
}
|
|
211
|
-
res.status(404).send('Xenon UI assets not found. Check installation.');
|
|
225
|
+
return res.status(404).send('Xenon UI assets not found. Check installation.');
|
|
212
226
|
}
|
|
213
227
|
});
|
|
214
228
|
return router;
|
|
@@ -148,12 +148,24 @@ let AndroidDeviceManager = class AndroidDeviceManager {
|
|
|
148
148
|
});
|
|
149
149
|
}
|
|
150
150
|
else if (deviceTypes.androidDeviceType === 'simulated') {
|
|
151
|
-
|
|
151
|
+
const simulated = devices.filter((device) => {
|
|
152
152
|
return device.deviceType === 'emulator';
|
|
153
153
|
});
|
|
154
|
+
if (this.pluginArgs.bootedEmulators) {
|
|
155
|
+
return simulated.filter((device) => device.state === 'device');
|
|
156
|
+
}
|
|
157
|
+
return simulated;
|
|
154
158
|
// return both real and simulated (emulated) devices
|
|
155
159
|
}
|
|
156
160
|
else {
|
|
161
|
+
if (this.pluginArgs.bootedEmulators) {
|
|
162
|
+
return devices.filter((device) => {
|
|
163
|
+
if (device.deviceType === 'emulator') {
|
|
164
|
+
return device.state === 'device';
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
157
169
|
return devices;
|
|
158
170
|
}
|
|
159
171
|
}
|
package/lib/src/device-utils.js
CHANGED
|
@@ -66,6 +66,7 @@ exports.unblockCandidateDevices = unblockCandidateDevices;
|
|
|
66
66
|
exports.releaseBlockedDevices = releaseBlockedDevices;
|
|
67
67
|
exports.setupCronReleaseBlockedDevices = setupCronReleaseBlockedDevices;
|
|
68
68
|
exports.setupCronUpdateDeviceList = setupCronUpdateDeviceList;
|
|
69
|
+
exports.setupCronLocalDiscovery = setupCronLocalDiscovery;
|
|
69
70
|
exports.cleanPendingSessions = cleanPendingSessions;
|
|
70
71
|
exports.setupCronCleanPendingSessions = setupCronCleanPendingSessions;
|
|
71
72
|
exports.setupCronCleanExpiredReservations = setupCronCleanExpiredReservations;
|
|
@@ -578,6 +579,21 @@ function setupCronUpdateDeviceList(host, hubArgument, intervalMs, tlsRejectUnaut
|
|
|
578
579
|
}), intervalMs);
|
|
579
580
|
});
|
|
580
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Principal discovery: Periodically poll for local devices to prune stales/offlines.
|
|
584
|
+
* Critical for standalone Hubs where setupCronUpdateDeviceList isn't called.
|
|
585
|
+
*/
|
|
586
|
+
function setupCronLocalDiscovery(host, intervalMs) {
|
|
587
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
588
|
+
if (timer) {
|
|
589
|
+
clearInterval(timer);
|
|
590
|
+
}
|
|
591
|
+
logger_1.default.info(`Local device discovery poll started every ${intervalMs} ms`);
|
|
592
|
+
timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
593
|
+
yield updateDeviceList(host);
|
|
594
|
+
}), intervalMs);
|
|
595
|
+
});
|
|
596
|
+
}
|
|
581
597
|
function cleanPendingSessions(timeoutMs) {
|
|
582
598
|
return __awaiter(this, void 0, void 0, function* () {
|
|
583
599
|
const pendingSessions = yield pendingStore.getAllPendingSessions();
|
|
@@ -34,6 +34,7 @@ exports.DefaultPluginArgs = {
|
|
|
34
34
|
bindHostOrIp: ip_1.default.address(),
|
|
35
35
|
enableDashboard: false,
|
|
36
36
|
bootedSimulators: false,
|
|
37
|
+
bootedEmulators: false,
|
|
37
38
|
healthCheckIntervalMs: 86400000,
|
|
38
39
|
healthCheckSchedule: undefined,
|
|
39
40
|
removeDevicesFromDatabaseBeforeRunningThePlugin: false,
|
|
@@ -246,6 +246,11 @@ let ServerManager = ServerManager_1 = class ServerManager {
|
|
|
246
246
|
else {
|
|
247
247
|
ServerManager_1.IS_HUB = true;
|
|
248
248
|
this.logger.info(`🌐 I'm a hub and I'm listening on ${pluginArgs.bindHostOrIp}:${cliArgs.port}`);
|
|
249
|
+
// Principal discovery: Background poll to prune stale/offline devices on the Hub itself
|
|
250
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
const { setupCronLocalDiscovery } = yield Promise.resolve().then(() => __importStar(require('../device-utils')));
|
|
252
|
+
yield setupCronLocalDiscovery(pluginArgs.bindHostOrIp, pluginArgs.sendNodeDevicesToHubIntervalMs);
|
|
253
|
+
}))();
|
|
249
254
|
const socketServer = typedi_1.Container.get(SocketServer_1.SocketServer);
|
|
250
255
|
socketServer.initialize(httpServer);
|
|
251
256
|
const tracingService = typedi_1.Container.get(TracingService_1.TracingService);
|
package/package.json
CHANGED
package/schema.json
CHANGED