@xenon-device-management/xenon 2.12.0 → 2.13.0
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 +2 -2
- package/lib/public/assets/{AnnotationOverlay-Crkn72_3.js → AnnotationOverlay-CzbFYtPI.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-BThVlj_j.js → ApiKeyGate-CA5KFHHJ.js} +1 -1
- package/lib/public/assets/{BugReportButton-Dwa0GnBh.js → BugReportButton-D-DSGg8C.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-Ccc-pOls.js → DeviceMosaicView-paDEFU4B.js} +1 -1
- package/lib/public/assets/{EmptyState-BeuTrlN_.js → EmptyState-BE_5f8Vw.js} +1 -1
- package/lib/public/assets/{FieldGroup-DBIaNxl1.js → FieldGroup-DqMf7Rf3.js} +1 -1
- package/lib/public/assets/{FilterMenu-BgSAS6iT.js → FilterMenu-BQY75Su3.js} +1 -1
- package/lib/public/assets/{Menu-GCbP6gYN.js → Menu-BiW3os9u.js} +1 -1
- package/lib/public/assets/{Modal-ByESeBRC.js → Modal-DfawTxih.js} +1 -1
- package/lib/public/assets/{RecordingPage-Bwyf3WMl.js → RecordingPage-BvemyRiy.js} +1 -1
- package/lib/public/assets/{RecordingsPage-7kM1rB3i.js → RecordingsPage-CXfw7wpd.js} +1 -1
- package/lib/public/assets/{SegmentedControl-B8tTU8kD.js → SegmentedControl-B7m9kpom.js} +1 -1
- package/lib/public/assets/{SettingCard-Clg_Ijbc.js → SettingCard-Cw6lDD0l.js} +1 -1
- package/lib/public/assets/{Table-CIaR0QWV.js → Table-DlEuFgv-.js} +1 -1
- package/lib/public/assets/{activity-Bengh9SD.js → activity-CnLvNwae.js} +1 -1
- package/lib/public/assets/{ai-settings-G9SymiPH.js → ai-settings-D7Te8o-a.js} +1 -1
- package/lib/public/assets/{api-keys-CrGlbfn-.js → api-keys-DphSVzBr.js} +1 -1
- package/lib/public/assets/{apps-DIbcGVlU.js → apps-vUfu15lX.js} +1 -1
- package/lib/public/assets/{arrow-left-BtJIXXMY.js → arrow-left-CLQWAiCY.js} +1 -1
- package/lib/public/assets/{arrow-right-zm-NagQh.js → arrow-right-K6A5e_Ee.js} +1 -1
- package/lib/public/assets/{arrow-up-right-DdU8YEYp.js → arrow-up-right-DUA_lq6f.js} +1 -1
- package/lib/public/assets/{auth-shell-CHmBFmOz.js → auth-shell-BYRHcZd6.js} +2 -2
- package/lib/public/assets/{builds-page-JkzWKRpU.js → builds-page-C0fTvIG8.js} +1 -1
- package/lib/public/assets/{button-CqhPBGRj.js → button-BxnHCCsX.js} +1 -1
- package/lib/public/assets/{calendar-C7eYYJTo.js → calendar-CqVsn28A.js} +1 -1
- package/lib/public/assets/{check-DpbIM4E0.js → check-R6V3bot0.js} +1 -1
- package/lib/public/assets/{chevron-right-Dl4X1PRz.js → chevron-right-BA2Fntu-.js} +1 -1
- package/lib/public/assets/{circle-check-Du3sjbfV.js → circle-check-D4EzyGzs.js} +1 -1
- package/lib/public/assets/{circle-x-BSSqhpXi.js → circle-x-Cf6AF2B2.js} +1 -1
- package/lib/public/assets/{clock-DMT60v1C.js → clock-EZfet0sS.js} +1 -1
- package/lib/public/assets/{copy-BKNyOehd.js → copy-Bc6P8v9V.js} +1 -1
- package/lib/public/assets/{device-explorer-CoPR8DW3.js → device-explorer-BFXpRq6d.js} +1 -1
- package/lib/public/assets/{download-BM6Xn22t.js → download-Bd8OGLzi.js} +1 -1
- package/lib/public/assets/{forgot-password-B8WqMqBT.js → forgot-password-CoE7Ugtx.js} +1 -1
- package/lib/public/assets/{index-DauQh6ie.js → index-BiL0Enl_.js} +1 -1
- package/lib/public/assets/{index-ClrpAMAT.js → index-CHOr4JCs.js} +2 -2
- package/lib/public/assets/{input-CyKdLnEx.js → input-CJ8z3IO5.js} +1 -1
- package/lib/public/assets/{line-chart-EIBXwYGo.js → line-chart-CPK0ObjV.js} +1 -1
- package/lib/public/assets/{list-checks-fWsgD9bI.js → list-checks-mXGHUVbB.js} +1 -1
- package/lib/public/assets/{lock-CVCe56TH.js → lock-BSXM1xvq.js} +1 -1
- package/lib/public/assets/{login-BJ8a7yVD.js → login-Ct2iW066.js} +1 -1
- package/lib/public/assets/{maintenance-settings-DcRmTLS6.js → maintenance-settings-DaOs00P6.js} +1 -1
- package/lib/public/assets/{monitor-Bw1YQZnL.js → monitor-D8WQK8md.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-C01jbqkO.js → mouse-pointer-2-Cw2tWzDX.js} +1 -1
- package/lib/public/assets/{network-CBGUjJDJ.js → network-Ba_uFxWo.js} +1 -1
- package/lib/public/assets/{overview-Cxe8aQ7C.js → overview-CxrZ7_6k.js} +1 -1
- package/lib/public/assets/{page-header-B92DKLiq.js → page-header-Cm1XL49f.js} +1 -1
- package/lib/public/assets/{play-Ck0L-0_m.js → play-C0CuKLXj.js} +1 -1
- package/lib/public/assets/{plus-Dq3tCy2N.js → plus-CIIvR_6Z.js} +1 -1
- package/lib/public/assets/{profile-page--dkDiKbr.js → profile-page-BZ_e-Bgw.js} +1 -1
- package/lib/public/assets/{recording-group-store-5BYIFFN9.js → recording-group-store-BWeeNs2_.js} +1 -1
- package/lib/public/assets/{reset-password-KYWlfjia.js → reset-password-lfNz8Rm1.js} +1 -1
- package/lib/public/assets/{runbook-page-mnsxgS1d.js → runbook-page-Cnec3VSx.js} +1 -1
- package/lib/public/assets/{select-BBOIZTYm.js → select-CZuCnold.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-CcI2j_Su.js → selector-detail-redirect-BG3pxFLw.js} +1 -1
- package/lib/public/assets/{selector-health-page-BdJb_x5L.js → selector-health-page-DsyL58tb.js} +1 -1
- package/lib/public/assets/{session-detail-page-BjLaJy8z.js → session-detail-page-ZZyGM413.js} +1 -1
- package/lib/public/assets/{settings-B6cpMhsx.js → settings-DGZ7B_AR.js} +1 -1
- package/lib/public/assets/{stat-tile-BU9e4s36.js → stat-tile-DFhv8JoI.js} +1 -1
- package/lib/public/assets/{tablet-hgbEwrWq.js → tablet-68J5f6zh.js} +1 -1
- package/lib/public/assets/{teams-uiiG4hZM.js → teams-eHSyEpbD.js} +1 -1
- package/lib/public/assets/{trash-2-NK_Iazmg.js → trash-2-CcSsv4fS.js} +1 -1
- package/lib/public/assets/{upload-BUX8TNFi.js → upload-BYf26K71.js} +1 -1
- package/lib/public/assets/{use-builds-data-DZszmFyl.js → use-builds-data-CHW-MhaP.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-BK4B9kRC.js → use-password-reset-mode-DozShXkk.js} +1 -1
- package/lib/public/assets/{users-BU4XBbMT.js → users-BpYnpOl2.js} +1 -1
- package/lib/public/assets/{users-BDh1xjad.js → users-CcSQRoK9.js} +1 -1
- package/lib/public/assets/{video-off-BOjNwT4R.js → video-off-dFKgps73.js} +1 -1
- package/lib/public/assets/{webhook-settings-BMzyUcx5.js → webhook-settings-DD1XjiOD.js} +1 -1
- package/lib/public/assets/{zap-DTWwVMUg.js → zap-COC2tZaH.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/app/apiErrors.js +118 -0
- package/lib/src/app/index.js +6 -1
- package/lib/src/app/openapi/control.yaml +3129 -0
- package/lib/src/app/openapi/grid.yaml +2295 -0
- package/lib/src/app/openapi/identity.yaml +2168 -0
- package/lib/src/app/openapi/platform.yaml +2885 -0
- package/lib/src/app/openapi/sessions.yaml +3784 -0
- package/lib/src/app/routers/bug-report.js +4 -1
- package/lib/src/app/routers/config.js +6 -107
- package/lib/src/app/routers/control.js +117 -59
- package/lib/src/app/routers/dashboard.js +13 -7
- package/lib/src/app/routers/grid.js +54 -14
- package/lib/src/app/routers/profile.js +27 -13
- package/lib/src/app/routers/recordings.js +11 -5
- package/lib/src/app/routers/reservation.js +63 -15
- package/lib/src/app/routers/users.js +4 -0
- package/lib/src/app/routers/webhook.js +17 -8
- package/lib/src/app/swagger.js +259 -177
- package/lib/src/data-service/device-service.js +4 -1
- package/lib/src/data-service/deviceFieldOwners.js +1 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +5 -2
- package/lib/src/device-managers/ios/WDAClient.js +32 -32
- package/lib/src/generated/client/edge.js +4 -3
- package/lib/src/generated/client/index-browser.js +1 -0
- package/lib/src/generated/client/index.d.ts +38 -0
- package/lib/src/generated/client/index.js +4 -3
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +2 -0
- package/lib/src/generated/client/wasm.js +1 -0
- package/lib/src/middleware/csrfMiddleware.js +13 -5
- package/lib/src/middleware/rateLimitMiddleware.js +19 -5
- package/lib/src/middleware/roleGuard.js +20 -0
- package/lib/src/services/AIService.js +13 -3
- package/lib/src/services/NotificationService.js +28 -28
- package/lib/src/services/bug-report/BugReportService.js +8 -2
- package/lib/src/services/lease/LeaseService.js +71 -20
- package/lib/src/services/omni-vision/OmniVisionService.js +14 -5
- package/lib/src/services/recording/RecordingOrchestrator.js +16 -2
- package/lib/test/helpers/expressRoutes.js +41 -0
- package/lib/test/integration/team-visibility-control.spec.js +2 -2
- package/lib/test/unit/access-scopes.spec.js +227 -0
- package/lib/test/unit/api-error-handling.spec.js +179 -0
- package/lib/test/unit/bug-report/route.spec.js +29 -0
- package/lib/test/unit/bug-report/service.spec.js +30 -0
- package/lib/test/unit/control-honest-answers.spec.js +134 -0
- package/lib/test/unit/device-allocation-routes.spec.js +232 -0
- package/lib/test/unit/healing-state-endpoints.spec.js +8 -5
- package/lib/test/unit/install-repository-app-team.spec.js +4 -1
- package/lib/test/unit/lease/LeaseService.spec.js +5 -4
- package/lib/test/unit/lease/lease-device-match.spec.js +161 -0
- package/lib/test/unit/lease/lease-session-ownership.spec.js +10 -9
- package/lib/test/unit/omni-vision-failures.spec.js +81 -0
- package/lib/test/unit/openapi-coverage.spec.js +114 -0
- package/lib/test/unit/profile-router.test.js +42 -0
- package/lib/test/unit/rateLimitMiddleware.test.js +49 -0
- package/lib/test/unit/recording-orchestrator.spec.js +80 -0
- package/lib/test/unit/recordings-library-routes.spec.js +37 -0
- package/lib/test/unit/reservation-team-visibility.spec.js +4 -3
- package/lib/test/unit/reset-link.test.js +2 -1
- package/lib/test/unit/stream-ticket-identity.spec.js +1 -1
- package/lib/test/unit/users-router.test.js +14 -1
- package/lib/test/unit/wda-client-failures.spec.js +90 -0
- package/lib/test/unit/webhook-delivery.spec.js +142 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/prisma/migrations/20261004120000_reservation_holder/migration.sql +2 -0
- package/prisma/schema.prisma +2 -0
- package/lib/src/app/swagger-docs.js +0 -1701
|
@@ -75,6 +75,7 @@ const iosStreamPort_1 = require("./iosStreamPort");
|
|
|
75
75
|
const androidH264Config_1 = require("./androidH264Config");
|
|
76
76
|
const recording_store_1 = require("../../services/recording/recording-store");
|
|
77
77
|
const clipboardErrors_1 = require("../../device-managers/clipboardErrors");
|
|
78
|
+
const WDAClient_1 = require("../../device-managers/ios/WDAClient");
|
|
78
79
|
const scopeGuard_1 = require("../../middleware/scopeGuard");
|
|
79
80
|
const roleGuard_1 = require("../../middleware/roleGuard");
|
|
80
81
|
const deviceAccessGuard_1 = require("../../middleware/deviceAccessGuard");
|
|
@@ -87,6 +88,12 @@ const streamLeave_1 = require("./streamLeave");
|
|
|
87
88
|
const SessionOwnerResolver_1 = require("../../services/device-access/SessionOwnerResolver");
|
|
88
89
|
const actor_1 = require("../../services/device-access/actor");
|
|
89
90
|
const deviceAccessPolicy_1 = require("../../services/device-access/deviceAccessPolicy");
|
|
91
|
+
/**
|
|
92
|
+
* The answer for an unknown udid, and for another team's phone (see
|
|
93
|
+
* deviceTeamGuard): the same JSON on every route. Most routes used to send
|
|
94
|
+
* this as plain text, which a JSON client couldn't read.
|
|
95
|
+
*/
|
|
96
|
+
const DEVICE_NOT_FOUND = { error: 'not_found', message: 'Device not found' };
|
|
90
97
|
const router = (0, express_1.Router)();
|
|
91
98
|
/** True when an in-memory iOS/Android (MJPEG or H.264) stream session exists. */
|
|
92
99
|
function hasActiveManualStream(udid) {
|
|
@@ -152,7 +159,7 @@ router.post('/:udid/tap', (req, res) => __awaiter(void 0, void 0, void 0, functi
|
|
|
152
159
|
const { x, y } = req.body;
|
|
153
160
|
const device = yield getDeviceInfo(udid);
|
|
154
161
|
if (!device)
|
|
155
|
-
return res.status(404).
|
|
162
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
156
163
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
157
164
|
if (manager && manager.tap) {
|
|
158
165
|
try {
|
|
@@ -175,14 +182,16 @@ router.post('/:udid/tap', (req, res) => __awaiter(void 0, void 0, void 0, functi
|
|
|
175
182
|
return res.status(500).send({ error: err.message });
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
|
-
res
|
|
185
|
+
res
|
|
186
|
+
.status(400)
|
|
187
|
+
.json({ error: 'not_supported', message: 'Manager not found or tap not supported' });
|
|
179
188
|
}));
|
|
180
189
|
router.post('/:udid/swipe', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
181
190
|
const { udid } = req.params;
|
|
182
191
|
const { x, y, endX, endY, duration = 1000 } = req.body;
|
|
183
192
|
const device = yield getDeviceInfo(udid);
|
|
184
193
|
if (!device)
|
|
185
|
-
return res.status(404).
|
|
194
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
186
195
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
187
196
|
if (manager && manager.swipe) {
|
|
188
197
|
try {
|
|
@@ -194,14 +203,16 @@ router.post('/:udid/swipe', (req, res) => __awaiter(void 0, void 0, void 0, func
|
|
|
194
203
|
return res.status(500).send({ error: err.message });
|
|
195
204
|
}
|
|
196
205
|
}
|
|
197
|
-
res
|
|
206
|
+
res
|
|
207
|
+
.status(400)
|
|
208
|
+
.json({ error: 'not_supported', message: 'Manager not found or swipe not supported' });
|
|
198
209
|
}));
|
|
199
210
|
router.post('/:udid/text', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
200
211
|
const { udid } = req.params;
|
|
201
212
|
const { text } = req.body;
|
|
202
213
|
const device = yield getDeviceInfo(udid);
|
|
203
214
|
if (!device)
|
|
204
|
-
return res.status(404).
|
|
215
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
205
216
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
206
217
|
if (manager && manager.typeText) {
|
|
207
218
|
try {
|
|
@@ -213,14 +224,16 @@ router.post('/:udid/text', (req, res) => __awaiter(void 0, void 0, void 0, funct
|
|
|
213
224
|
return res.status(500).send({ error: err.message });
|
|
214
225
|
}
|
|
215
226
|
}
|
|
216
|
-
res
|
|
227
|
+
res
|
|
228
|
+
.status(400)
|
|
229
|
+
.json({ error: 'not_supported', message: 'Manager not found or typeText not supported' });
|
|
217
230
|
}));
|
|
218
231
|
router.post('/:udid/keyevent', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
219
232
|
const { udid } = req.params;
|
|
220
233
|
const { keyCode } = req.body;
|
|
221
234
|
const device = yield getDeviceInfo(udid);
|
|
222
235
|
if (!device)
|
|
223
|
-
return res.status(404).
|
|
236
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
224
237
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
225
238
|
if (manager && manager.pressKey) {
|
|
226
239
|
try {
|
|
@@ -228,18 +241,24 @@ router.post('/:udid/keyevent', (req, res) => __awaiter(void 0, void 0, void 0, f
|
|
|
228
241
|
return res.status(200).send({ success: true });
|
|
229
242
|
}
|
|
230
243
|
catch (err) {
|
|
244
|
+
// A key the phone doesn't have is the caller's mistake, not a failure.
|
|
245
|
+
if (err instanceof WDAClient_1.UnsupportedKeyError) {
|
|
246
|
+
return res.status(400).send({ error: 'unsupported_key', message: err.message });
|
|
247
|
+
}
|
|
231
248
|
logger_1.default.error(`❌ pressKey failed for ${udid}: ${err.message}`);
|
|
232
249
|
return res.status(500).send({ error: err.message });
|
|
233
250
|
}
|
|
234
251
|
}
|
|
235
|
-
res
|
|
252
|
+
res
|
|
253
|
+
.status(400)
|
|
254
|
+
.json({ error: 'not_supported', message: 'Manager not found or pressKey not supported' });
|
|
236
255
|
}));
|
|
237
256
|
router.get('/:udid/screenshot', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
238
257
|
var _a, _b;
|
|
239
258
|
const { udid } = req.params;
|
|
240
259
|
const device = yield getDeviceInfo(udid);
|
|
241
260
|
if (!device)
|
|
242
|
-
return res.status(404).
|
|
261
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
243
262
|
// Principal Engineer Optimization: If a high-speed stream is already running for Android,
|
|
244
263
|
// we should grab the latest frame instead of triggering a heavy ADB screencap.
|
|
245
264
|
if (device.platform === 'android') {
|
|
@@ -270,13 +289,15 @@ router.get('/:udid/screenshot', (req, res) => __awaiter(void 0, void 0, void 0,
|
|
|
270
289
|
error: 'Screenshot capture failed. Device may be busy or WDA is unresponsive. Try again.',
|
|
271
290
|
});
|
|
272
291
|
}
|
|
273
|
-
res
|
|
292
|
+
res
|
|
293
|
+
.status(400)
|
|
294
|
+
.json({ error: 'not_supported', message: 'Manager not found or screenshot not supported' });
|
|
274
295
|
}));
|
|
275
296
|
router.get('/:udid/clipboard', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
276
297
|
const { udid } = req.params;
|
|
277
298
|
const device = yield getDeviceInfo(udid);
|
|
278
299
|
if (!device)
|
|
279
|
-
return res.status(404).
|
|
300
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
280
301
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
281
302
|
if (manager && manager.getClipboard) {
|
|
282
303
|
try {
|
|
@@ -289,14 +310,16 @@ router.get('/:udid/clipboard', (req, res) => __awaiter(void 0, void 0, void 0, f
|
|
|
289
310
|
return res.status(500).send({ error: err.message });
|
|
290
311
|
}
|
|
291
312
|
}
|
|
292
|
-
res
|
|
313
|
+
res
|
|
314
|
+
.status(400)
|
|
315
|
+
.json({ error: 'not_supported', message: 'Manager not found or getClipboard not supported' });
|
|
293
316
|
}));
|
|
294
317
|
router.post('/:udid/clipboard', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
295
318
|
const { udid } = req.params;
|
|
296
319
|
const { content } = req.body;
|
|
297
320
|
const device = yield getDeviceInfo(udid);
|
|
298
321
|
if (!device)
|
|
299
|
-
return res.status(404).
|
|
322
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
300
323
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
301
324
|
if (manager && manager.setClipboard) {
|
|
302
325
|
try {
|
|
@@ -312,14 +335,16 @@ router.post('/:udid/clipboard', (req, res) => __awaiter(void 0, void 0, void 0,
|
|
|
312
335
|
return res.status(500).send({ error: err.message });
|
|
313
336
|
}
|
|
314
337
|
}
|
|
315
|
-
res
|
|
338
|
+
res
|
|
339
|
+
.status(400)
|
|
340
|
+
.json({ error: 'not_supported', message: 'Manager not found or setClipboard not supported' });
|
|
316
341
|
}));
|
|
317
342
|
router.post('/:udid/touchAndHold', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
318
343
|
const { udid } = req.params;
|
|
319
344
|
const { x, y, duration = 1000 } = req.body;
|
|
320
345
|
const device = yield getDeviceInfo(udid);
|
|
321
346
|
if (!device)
|
|
322
|
-
return res.status(404).
|
|
347
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
323
348
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
324
349
|
if (manager && manager.touchAndHold) {
|
|
325
350
|
try {
|
|
@@ -331,13 +356,15 @@ router.post('/:udid/touchAndHold', (req, res) => __awaiter(void 0, void 0, void
|
|
|
331
356
|
return res.status(500).send({ error: err.message });
|
|
332
357
|
}
|
|
333
358
|
}
|
|
334
|
-
res
|
|
359
|
+
res
|
|
360
|
+
.status(400)
|
|
361
|
+
.json({ error: 'not_supported', message: 'Manager not found or touchAndHold not supported' });
|
|
335
362
|
}));
|
|
336
363
|
router.post('/:udid/lock', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
337
364
|
const { udid } = req.params;
|
|
338
365
|
const device = yield getDeviceInfo(udid);
|
|
339
366
|
if (!device)
|
|
340
|
-
return res.status(404).
|
|
367
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
341
368
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
342
369
|
if (manager && manager.lock) {
|
|
343
370
|
try {
|
|
@@ -349,13 +376,15 @@ router.post('/:udid/lock', (req, res) => __awaiter(void 0, void 0, void 0, funct
|
|
|
349
376
|
return res.status(500).send({ error: err.message });
|
|
350
377
|
}
|
|
351
378
|
}
|
|
352
|
-
res
|
|
379
|
+
res
|
|
380
|
+
.status(400)
|
|
381
|
+
.json({ error: 'not_supported', message: 'Manager not found or lock not supported' });
|
|
353
382
|
}));
|
|
354
383
|
router.post('/:udid/unlock', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
355
384
|
const { udid } = req.params;
|
|
356
385
|
const device = yield getDeviceInfo(udid);
|
|
357
386
|
if (!device)
|
|
358
|
-
return res.status(404).
|
|
387
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
359
388
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
360
389
|
if (manager && manager.unlock) {
|
|
361
390
|
try {
|
|
@@ -367,7 +396,9 @@ router.post('/:udid/unlock', (req, res) => __awaiter(void 0, void 0, void 0, fun
|
|
|
367
396
|
return res.status(500).send({ error: err.message });
|
|
368
397
|
}
|
|
369
398
|
}
|
|
370
|
-
res
|
|
399
|
+
res
|
|
400
|
+
.status(400)
|
|
401
|
+
.json({ error: 'not_supported', message: 'Manager not found or unlock not supported' });
|
|
371
402
|
}));
|
|
372
403
|
/**
|
|
373
404
|
* Is the device's panel lit?
|
|
@@ -386,7 +417,7 @@ router.get('/:udid/display', (req, res) => __awaiter(void 0, void 0, void 0, fun
|
|
|
386
417
|
const { udid } = req.params;
|
|
387
418
|
const device = yield getDeviceInfo(udid);
|
|
388
419
|
if (!device)
|
|
389
|
-
return res.status(404).
|
|
420
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
390
421
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
391
422
|
if (!(manager === null || manager === void 0 ? void 0 : manager.getDisplayState))
|
|
392
423
|
return res.status(200).send({ state: 'unknown' });
|
|
@@ -398,7 +429,7 @@ router.post('/:udid/install', (req, res) => __awaiter(void 0, void 0, void 0, fu
|
|
|
398
429
|
const { appPath } = req.body;
|
|
399
430
|
const device = yield getDeviceInfo(udid);
|
|
400
431
|
if (!device)
|
|
401
|
-
return res.status(404).
|
|
432
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
402
433
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
403
434
|
if (manager && manager.installApp) {
|
|
404
435
|
try {
|
|
@@ -410,22 +441,28 @@ router.post('/:udid/install', (req, res) => __awaiter(void 0, void 0, void 0, fu
|
|
|
410
441
|
return res.status(500).send({ error: err.message });
|
|
411
442
|
}
|
|
412
443
|
}
|
|
413
|
-
res
|
|
444
|
+
res
|
|
445
|
+
.status(400)
|
|
446
|
+
.json({ error: 'not_supported', message: 'Manager not found or installApp not supported' });
|
|
414
447
|
}));
|
|
415
448
|
router.post('/:udid/install-repository-app', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
416
|
-
var _a;
|
|
449
|
+
var _a, _b;
|
|
417
450
|
const { udid } = req.params;
|
|
418
|
-
const { appId } = req.body;
|
|
451
|
+
const { appId } = (_a = req.body) !== null && _a !== void 0 ? _a : {};
|
|
419
452
|
const device = yield getDeviceInfo(udid);
|
|
420
453
|
if (!device)
|
|
421
|
-
return res.status(404).
|
|
454
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
455
|
+
// Without one the lookup below threw, and the answer was a 500.
|
|
456
|
+
if (typeof appId !== 'string' || !appId) {
|
|
457
|
+
return res.status(400).json({ error: 'bad_request', message: 'appId is required' });
|
|
458
|
+
}
|
|
422
459
|
try {
|
|
423
460
|
const { APP_SERVICE } = yield Promise.resolve().then(() => __importStar(require('../../dashboard/services/app-service')));
|
|
424
461
|
const app = yield APP_SERVICE.getAppById(appId);
|
|
425
462
|
// Installing hands over the binary as a download does: another team's
|
|
426
463
|
// app answers as an unknown one.
|
|
427
|
-
if (!(0, appVisibility_1.canSeeApp)(app, (
|
|
428
|
-
return res.status(404).
|
|
464
|
+
if (!(0, appVisibility_1.canSeeApp)(app, (_b = req.auth) === null || _b === void 0 ? void 0 : _b.teamIds)) {
|
|
465
|
+
return res.status(404).json({ error: 'not_found', message: 'App not found in repository' });
|
|
429
466
|
}
|
|
430
467
|
// A node's phone: the app is in this server's library, not the node's,
|
|
431
468
|
// so the file goes to the node's own upload-install.
|
|
@@ -440,7 +477,9 @@ router.post('/:udid/install-repository-app', (req, res) => __awaiter(void 0, voi
|
|
|
440
477
|
yield manager.installApp(udid, app.filepath);
|
|
441
478
|
return res.status(200).send({ success: true, message: `Installed ${app.name}` });
|
|
442
479
|
}
|
|
443
|
-
res
|
|
480
|
+
res
|
|
481
|
+
.status(400)
|
|
482
|
+
.json({ error: 'not_supported', message: 'Manager not found or installApp not supported' });
|
|
444
483
|
}
|
|
445
484
|
catch (err) {
|
|
446
485
|
logger_1.default.error(`Installation from repository failed: ${err.message}`);
|
|
@@ -484,16 +523,20 @@ router.post('/:udid/upload-install', uploadParser, (req, res) => __awaiter(void
|
|
|
484
523
|
const answer = yield (() => __awaiter(void 0, void 0, void 0, function* () {
|
|
485
524
|
const device = yield getDeviceInfo(udid);
|
|
486
525
|
if (!device)
|
|
487
|
-
return { status: 404, body:
|
|
526
|
+
return { status: 404, body: DEVICE_NOT_FOUND };
|
|
488
527
|
if (!req.files || Object.keys(req.files).length === 0) {
|
|
489
|
-
return { status: 400, body: 'No files were uploaded.' };
|
|
528
|
+
return { status: 400, body: { error: 'bad_request', message: 'No files were uploaded.' } };
|
|
529
|
+
}
|
|
530
|
+
if (!appFile || !appPath) {
|
|
531
|
+
return { status: 400, body: { error: 'bad_request', message: 'File "app" is required' } };
|
|
490
532
|
}
|
|
491
|
-
if (!appFile || !appPath)
|
|
492
|
-
return { status: 400, body: 'File "app" is required' };
|
|
493
533
|
yield appFile.mv(appPath);
|
|
494
534
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
495
535
|
if (!(manager === null || manager === void 0 ? void 0 : manager.installApp)) {
|
|
496
|
-
return {
|
|
536
|
+
return {
|
|
537
|
+
status: 400,
|
|
538
|
+
body: { error: 'not_supported', message: 'Manager not found or installApp not supported' },
|
|
539
|
+
};
|
|
497
540
|
}
|
|
498
541
|
yield manager.installApp(udid, appPath);
|
|
499
542
|
return {
|
|
@@ -518,7 +561,7 @@ router.post('/:udid/uninstall', (req, res) => __awaiter(void 0, void 0, void 0,
|
|
|
518
561
|
const { bundleId } = req.body;
|
|
519
562
|
const device = yield getDeviceInfo(udid);
|
|
520
563
|
if (!device)
|
|
521
|
-
return res.status(404).
|
|
564
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
522
565
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
523
566
|
if (manager && manager.uninstallApp) {
|
|
524
567
|
try {
|
|
@@ -530,13 +573,15 @@ router.post('/:udid/uninstall', (req, res) => __awaiter(void 0, void 0, void 0,
|
|
|
530
573
|
return res.status(500).send({ error: err.message });
|
|
531
574
|
}
|
|
532
575
|
}
|
|
533
|
-
res
|
|
576
|
+
res
|
|
577
|
+
.status(400)
|
|
578
|
+
.json({ error: 'not_supported', message: 'Manager not found or uninstallApp not supported' });
|
|
534
579
|
}));
|
|
535
580
|
router.get('/:udid/apps', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
536
581
|
const { udid } = req.params;
|
|
537
582
|
const device = yield getDeviceInfo(udid);
|
|
538
583
|
if (!device)
|
|
539
|
-
return res.status(404).
|
|
584
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
540
585
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
541
586
|
if (manager && manager.listApps) {
|
|
542
587
|
try {
|
|
@@ -549,13 +594,15 @@ router.get('/:udid/apps', (req, res) => __awaiter(void 0, void 0, void 0, functi
|
|
|
549
594
|
return res.status(500).send({ error: err.message });
|
|
550
595
|
}
|
|
551
596
|
}
|
|
552
|
-
res
|
|
597
|
+
res
|
|
598
|
+
.status(400)
|
|
599
|
+
.json({ error: 'not_supported', message: 'Manager not found or listApps not supported' });
|
|
553
600
|
}));
|
|
554
601
|
router.get('/:udid/logs', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
555
602
|
const { udid } = req.params;
|
|
556
603
|
const device = yield getDeviceInfo(udid);
|
|
557
604
|
if (!device)
|
|
558
|
-
return res.status(404).
|
|
605
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
559
606
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
560
607
|
if (manager && manager.getLogs) {
|
|
561
608
|
try {
|
|
@@ -567,7 +614,9 @@ router.get('/:udid/logs', (req, res) => __awaiter(void 0, void 0, void 0, functi
|
|
|
567
614
|
return res.status(500).send({ error: err.message });
|
|
568
615
|
}
|
|
569
616
|
}
|
|
570
|
-
res
|
|
617
|
+
res
|
|
618
|
+
.status(400)
|
|
619
|
+
.json({ error: 'not_supported', message: 'Manager not found or getLogs not supported' });
|
|
571
620
|
}));
|
|
572
621
|
const MJPEG_PROXY_CACHE = new Map();
|
|
573
622
|
/**
|
|
@@ -581,7 +630,7 @@ router.post('/:udid/stream/start', (req, res) => __awaiter(void 0, void 0, void
|
|
|
581
630
|
exports.previewLeaves.cancel(udid);
|
|
582
631
|
const device = yield getDeviceInfo(udid);
|
|
583
632
|
if (!device)
|
|
584
|
-
return res.status(404).
|
|
633
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
585
634
|
// Ownership at stream-start time. A manual lock with no live stream is an
|
|
586
635
|
// orphan to reclaim; a live foreign stream is a conflict we must refuse.
|
|
587
636
|
// Refusing is what stops one user's start from silently rewriting another
|
|
@@ -728,7 +777,7 @@ router.post('/:udid/stream/ticket', (req, res) => __awaiter(void 0, void 0, void
|
|
|
728
777
|
return res.status(503).json((0, deviceAccessPolicy_1.ownershipUnavailableBody)());
|
|
729
778
|
}
|
|
730
779
|
if (!device)
|
|
731
|
-
return res.status(404).
|
|
780
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
732
781
|
// Carry the two other things `evaluateDeviceAccess` needs alongside the user
|
|
733
782
|
// id. A ticket consumer (the logcat WS) has no Express request to run
|
|
734
783
|
// resolveActor against, and re-deriving them from a User row at redeem time
|
|
@@ -818,7 +867,7 @@ router.post('/:udid/stream/leave', (req, res) => __awaiter(void 0, void 0, void
|
|
|
818
867
|
const { udid } = req.params;
|
|
819
868
|
const device = yield getDeviceInfo(udid);
|
|
820
869
|
if (!device)
|
|
821
|
-
return res.status(404).
|
|
870
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
822
871
|
const actor = (0, actor_1.resolveActor)(req);
|
|
823
872
|
const lockInfo = (0, manualLock_1.inspectManualLock)(device.session_id, actor.userId, udid);
|
|
824
873
|
const mayRelease = (0, deviceAccessPolicy_1.isSelfManualLock)(device.session_id, udid, actor.userId, actor.apiKeyId) ||
|
|
@@ -838,7 +887,7 @@ router.post('/:udid/stream/stop', (req, res) => __awaiter(void 0, void 0, void 0
|
|
|
838
887
|
const { udid } = req.params;
|
|
839
888
|
const device = yield getDeviceInfo(udid);
|
|
840
889
|
if (!device)
|
|
841
|
-
return res.status(404).
|
|
890
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
842
891
|
// Multi-user safety: refuse to release a manual lock owned by another user.
|
|
843
892
|
// Admin scope bypasses this so support can clear stuck sessions.
|
|
844
893
|
//
|
|
@@ -903,7 +952,7 @@ router.get('/:udid/stream/status', (req, res) => __awaiter(void 0, void 0, void
|
|
|
903
952
|
const { udid } = req.params;
|
|
904
953
|
const device = yield getDeviceInfo(udid);
|
|
905
954
|
if (!device)
|
|
906
|
-
return res.status(404).
|
|
955
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
907
956
|
// Advertise which transport the frontend player should use. A recording
|
|
908
957
|
// device keeps MJPEG (one capture pipeline per device).
|
|
909
958
|
const flagOn = (0, androidH264Config_1.resolveAndroidH264)((_a = typedi_1.Container.get(PluginContext_1.PluginContext).pluginArgs.streaming) === null || _a === void 0 ? void 0 : _a.androidH264).enabled;
|
|
@@ -973,7 +1022,7 @@ router.get('/:udid/stream', (req, res) => __awaiter(void 0, void 0, void 0, func
|
|
|
973
1022
|
});
|
|
974
1023
|
const device = yield getDeviceInfo(udid);
|
|
975
1024
|
if (!device)
|
|
976
|
-
return res.status(404).
|
|
1025
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
977
1026
|
// The MJPEG preview attaches here on a reload; see screenSizeDeps.
|
|
978
1027
|
void (0, fillMissingScreenSize_1.fillMissingScreenSize)(device, screenSizeDeps);
|
|
979
1028
|
let mjpegPort = device.mjpegServerPort;
|
|
@@ -1029,7 +1078,7 @@ router.get('/:udid/stream', (req, res) => __awaiter(void 0, void 0, void 0, func
|
|
|
1029
1078
|
if (!mjpegPort) {
|
|
1030
1079
|
return res.status(404).send({
|
|
1031
1080
|
error: 'MJPEG port not found for device',
|
|
1032
|
-
hint: '
|
|
1081
|
+
hint: 'Start the preview with POST /stream/start, then open this stream again.',
|
|
1033
1082
|
});
|
|
1034
1083
|
}
|
|
1035
1084
|
const videoUrl = `http://127.0.0.1:${mjpegPort}`;
|
|
@@ -1059,7 +1108,7 @@ router.get('/:udid/stream', (req, res) => __awaiter(void 0, void 0, void 0, func
|
|
|
1059
1108
|
proxy.proxyRequest(req, res);
|
|
1060
1109
|
}
|
|
1061
1110
|
else {
|
|
1062
|
-
res.status(404).
|
|
1111
|
+
res.status(404).json({ error: 'not_found', message: 'The preview could not be started' });
|
|
1063
1112
|
}
|
|
1064
1113
|
}
|
|
1065
1114
|
catch (err) {
|
|
@@ -1076,9 +1125,9 @@ router.post('/:udid/shell', (req, res) => __awaiter(void 0, void 0, void 0, func
|
|
|
1076
1125
|
const { command } = req.body;
|
|
1077
1126
|
const device = yield getDeviceInfo(udid);
|
|
1078
1127
|
if (!device)
|
|
1079
|
-
return res.status(404).
|
|
1128
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
1080
1129
|
if (!command)
|
|
1081
|
-
return res.status(400).
|
|
1130
|
+
return res.status(400).json({ error: 'bad_request', message: 'Command is required' });
|
|
1082
1131
|
const manager = yield getDeviceManagerForPlatform(device.platform);
|
|
1083
1132
|
if (manager && manager.executeShell) {
|
|
1084
1133
|
try {
|
|
@@ -1091,7 +1140,9 @@ router.post('/:udid/shell', (req, res) => __awaiter(void 0, void 0, void 0, func
|
|
|
1091
1140
|
return res.status(200).send({ error: err.message });
|
|
1092
1141
|
}
|
|
1093
1142
|
}
|
|
1094
|
-
res
|
|
1143
|
+
res
|
|
1144
|
+
.status(400)
|
|
1145
|
+
.json({ error: 'not_supported', message: 'Manager not found or executeShell not supported' });
|
|
1095
1146
|
}));
|
|
1096
1147
|
/**
|
|
1097
1148
|
* Where Omni-Vision reads the screen from, for the stand-in driver it is
|
|
@@ -1127,14 +1178,20 @@ router.get('/:udid/omni-scan', (req, res) => __awaiter(void 0, void 0, void 0, f
|
|
|
1127
1178
|
const { udid } = req.params;
|
|
1128
1179
|
const device = yield getDeviceInfo(udid);
|
|
1129
1180
|
if (!device)
|
|
1130
|
-
return res.status(404).
|
|
1181
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
1131
1182
|
const screen = yield omniScreen(req, udid, device);
|
|
1132
1183
|
if (!screen)
|
|
1133
|
-
return res.status(400).
|
|
1184
|
+
return res.status(400).json({ error: 'not_supported', message: 'Manager not found' });
|
|
1134
1185
|
try {
|
|
1135
1186
|
const omniService = typedi_1.Container.get(OmniVisionService_1.OmniVisionService);
|
|
1136
1187
|
const mockDriver = Object.assign({ sessionId: `manual_${udid}` }, screen);
|
|
1137
1188
|
const result = yield omniService.analyzeScreen(mockDriver);
|
|
1189
|
+
// analyzeScreen reports a failed analysis in its result rather than
|
|
1190
|
+
// throwing (the Appium execute-script path returns that object as is).
|
|
1191
|
+
if ((result === null || result === void 0 ? void 0 : result.status) === 'error') {
|
|
1192
|
+
logger_1.default.error(`Manual Omni-Scan failed for ${udid}: ${result.message}`);
|
|
1193
|
+
return res.status(500).send({ status: 'error', message: result.message });
|
|
1194
|
+
}
|
|
1138
1195
|
return res.status(200).send({ status: 'success', value: result });
|
|
1139
1196
|
}
|
|
1140
1197
|
catch (err) {
|
|
@@ -1161,7 +1218,7 @@ router.get('/:udid/inspector/snapshot', (req, res) => __awaiter(void 0, void 0,
|
|
|
1161
1218
|
return res.status(503).json((0, deviceAccessPolicy_1.ownershipUnavailableBody)());
|
|
1162
1219
|
}
|
|
1163
1220
|
if (!device)
|
|
1164
|
-
return res.status(404).
|
|
1221
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
1165
1222
|
try {
|
|
1166
1223
|
const inspectorService = typedi_1.Container.get(InspectorService_1.InspectorService);
|
|
1167
1224
|
const snapshot = yield inspectorService.getSnapshot(udid);
|
|
@@ -1193,7 +1250,7 @@ router.get('/:udid/appium-session', (req, res) => __awaiter(void 0, void 0, void
|
|
|
1193
1250
|
const { udid } = req.params;
|
|
1194
1251
|
const device = yield getDeviceInfo(udid);
|
|
1195
1252
|
if (!device)
|
|
1196
|
-
return res.status(404).
|
|
1253
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
1197
1254
|
// A manual lock is the dashboard holding the device for preview/recording,
|
|
1198
1255
|
// not an Appium session — it cannot resolve locators, so it is not one.
|
|
1199
1256
|
const sessionId = device.session_id && !(0, manualLock_1.isManualLock)(device.session_id) ? String(device.session_id) : null;
|
|
@@ -1211,19 +1268,20 @@ router.post('/:udid/test-locator', (req, res) => __awaiter(void 0, void 0, void
|
|
|
1211
1268
|
const { strategy, selector } = req.body;
|
|
1212
1269
|
const device = yield getDeviceInfo(udid);
|
|
1213
1270
|
if (!device)
|
|
1214
|
-
return res.status(404).
|
|
1271
|
+
return res.status(404).json(DEVICE_NOT_FOUND);
|
|
1215
1272
|
const screen = yield omniScreen(req, udid, device);
|
|
1216
1273
|
if (!screen)
|
|
1217
|
-
return res.status(400).
|
|
1274
|
+
return res.status(400).json({ error: 'not_supported', message: 'Manager not found' });
|
|
1218
1275
|
try {
|
|
1219
1276
|
const omniService = typedi_1.Container.get(OmniVisionService_1.OmniVisionService);
|
|
1220
1277
|
const mockDriver = { sessionId: `manual_${udid}`, getScreenshot: screen.getScreenshot };
|
|
1221
1278
|
let value = [];
|
|
1222
1279
|
if (strategy === '-custom:ai-text') {
|
|
1223
|
-
|
|
1280
|
+
// A failed OCR or AI call is a 500 here, not "no match".
|
|
1281
|
+
value = yield omniService.findByText(mockDriver, selector, { throwOnError: true });
|
|
1224
1282
|
}
|
|
1225
1283
|
else if (strategy === '-custom:ai-icon') {
|
|
1226
|
-
const match = yield omniService.findByIcon(mockDriver, selector);
|
|
1284
|
+
const match = yield omniService.findByIcon(mockDriver, selector, { throwOnError: true });
|
|
1227
1285
|
if (match)
|
|
1228
1286
|
value = [match];
|
|
1229
1287
|
}
|
|
@@ -995,8 +995,10 @@ function getMutedSelectors(request, response) {
|
|
|
995
995
|
function getSelectorStateByTuple(request, response) {
|
|
996
996
|
return __awaiter(this, void 0, void 0, function* () {
|
|
997
997
|
var _a, _b;
|
|
998
|
-
|
|
999
|
-
|
|
998
|
+
// Express has already decoded the params. Decoding them again made a
|
|
999
|
+
// selector with a `%` need encoding twice, and a lone `%` threw.
|
|
1000
|
+
const strategy = (_a = request.params.strategy) !== null && _a !== void 0 ? _a : '';
|
|
1001
|
+
const value = (_b = request.params.value) !== null && _b !== void 0 ? _b : '';
|
|
1000
1002
|
const row = yield prisma_1.prisma.selectorState.findUnique({
|
|
1001
1003
|
where: {
|
|
1002
1004
|
original_strategy_original_selector: {
|
|
@@ -1170,6 +1172,7 @@ function resetMetrics(request, response) {
|
|
|
1170
1172
|
}
|
|
1171
1173
|
});
|
|
1172
1174
|
}
|
|
1175
|
+
const changeSettingsGuard = (0, roleGuard_1.superAdminGuard)("Only a super admin can change the lab's settings.");
|
|
1173
1176
|
function register(router) {
|
|
1174
1177
|
router.use('/session/:sessionId', isValidSession);
|
|
1175
1178
|
router.use((0, roleGuard_1.roleGuard)('MEMBER'));
|
|
@@ -1207,11 +1210,14 @@ function register(router) {
|
|
|
1207
1210
|
router.post('/healing/selector/state', (0, roleGuard_1.roleGuard)('MEMBER'), (0, scopeGuard_1.scopeGuard)(['sessions']), postSelectorStateAction);
|
|
1208
1211
|
router.get('/healing/state/muted', getMutedSelectors);
|
|
1209
1212
|
router.get('/healing/state/:strategy/:value', getSelectorStateByTuple);
|
|
1210
|
-
|
|
1211
|
-
//
|
|
1212
|
-
//
|
|
1213
|
-
|
|
1214
|
-
|
|
1213
|
+
// The lab's settings. Reading them needs ADMIN, as the pages that show
|
|
1214
|
+
// them do; changing them needs SUPER_ADMIN and the admin scope. Through
|
|
1215
|
+
// 2.12 any member could read them and any admin change them: the stricter
|
|
1216
|
+
// guards in ConfigRouter were never reached, since these routes are
|
|
1217
|
+
// registered first and answer.
|
|
1218
|
+
router.get('/config', (0, roleGuard_1.roleGuard)('ADMIN'), getGlobalConfig);
|
|
1219
|
+
router.post('/config', changeSettingsGuard, (0, scopeGuard_1.scopeGuard)(['admin']), updateGlobalConfig);
|
|
1220
|
+
router.post('/config/reset-metrics', changeSettingsGuard, (0, scopeGuard_1.scopeGuard)(['admin']), resetMetrics);
|
|
1215
1221
|
}
|
|
1216
1222
|
exports.default = {
|
|
1217
1223
|
register,
|