@xenon-device-management/xenon 1.1.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/README.md +446 -0
- package/lib/package.json +207 -0
- package/lib/public/assets/Layouts-7IT8aFLI.js +11 -0
- package/lib/public/assets/Layouts-DPMls9vh.css +1 -0
- package/lib/public/assets/ai-settings-BbnfgdEx.js +11 -0
- package/lib/public/assets/apps-CRMrI4_p.js +16 -0
- package/lib/public/assets/apps-CcM77dgg.css +1 -0
- package/lib/public/assets/badge-B1nKs8zj.css +1 -0
- package/lib/public/assets/badge-CSvl5xIU.js +11 -0
- package/lib/public/assets/button-CJlKn4PZ.css +1 -0
- package/lib/public/assets/button-CvLaGFYj.js +26 -0
- package/lib/public/assets/calendar-6w-D6Oaw.js +6 -0
- package/lib/public/assets/clock-DcdeWBPr.js +6 -0
- package/lib/public/assets/cpu-DiSoXT9n.js +6 -0
- package/lib/public/assets/device-explorer-CajM63OJ.js +193 -0
- package/lib/public/assets/device-explorer-CxdUAoTL.css +1 -0
- package/lib/public/assets/index-ByQwMN5T.js +174 -0
- package/lib/public/assets/index-C1DBaoSh.js +1 -0
- package/lib/public/assets/index-qzCez_kk.css +1 -0
- package/lib/public/assets/lock-B23ibZmo.js +6 -0
- package/lib/public/assets/maintenance-settings-CirzA6yG.js +6 -0
- package/lib/public/assets/mouse-pointer-2-Cz76SHFb.js +6 -0
- package/lib/public/assets/plus-BBwlIevt.js +6 -0
- package/lib/public/assets/session-dashboard-C2k7FFv_.css +1 -0
- package/lib/public/assets/session-dashboard-HPDtwPOZ.js +62 -0
- package/lib/public/assets/settings-DrZsZwdc.js +1 -0
- package/lib/public/assets/trash-2-DQpvzJec.js +6 -0
- package/lib/public/assets/useSocket-Dxsqae2a.js +16 -0
- package/lib/public/assets/webhook-settings-CDPgsgkb.css +1 -0
- package/lib/public/assets/webhook-settings-Cp-B4Nrw.js +1 -0
- package/lib/public/assets/zap-DovP6iow.js +6 -0
- package/lib/public/favicon.ico +0 -0
- package/lib/public/favicon.png +0 -0
- package/lib/public/favicon.svg +9 -0
- package/lib/public/index.html +46 -0
- package/lib/public/logo.svg +17 -0
- package/lib/public/logo192.png +0 -0
- package/lib/public/logo512.png +0 -0
- package/lib/public/manifest.json +25 -0
- package/lib/public/robots.txt +3 -0
- package/lib/schema.json +348 -0
- package/lib/src/InternalHttpClient.js +212 -0
- package/lib/src/PluginContext.js +29 -0
- package/lib/src/XenonCapabilityManager.js +199 -0
- package/lib/src/app/index.js +167 -0
- package/lib/src/app/routers/apps.js +79 -0
- package/lib/src/app/routers/config.js +131 -0
- package/lib/src/app/routers/control.js +835 -0
- package/lib/src/app/routers/dashboard.js +301 -0
- package/lib/src/app/routers/grid.js +352 -0
- package/lib/src/app/routers/reservation.js +190 -0
- package/lib/src/app/routers/webhook.js +83 -0
- package/lib/src/app/swagger-docs.js +203 -0
- package/lib/src/app/swagger.js +366 -0
- package/lib/src/chromeUtils.js +148 -0
- package/lib/src/commands/handle.js +19 -0
- package/lib/src/commands/index.js +8 -0
- package/lib/src/config.js +73 -0
- package/lib/src/dashboard/asset-manager.js +84 -0
- package/lib/src/dashboard/commands.js +284 -0
- package/lib/src/dashboard/event-manager.js +699 -0
- package/lib/src/dashboard/services/app-service.js +134 -0
- package/lib/src/dashboard/services/failure-analysis-service.js +173 -0
- package/lib/src/dashboard/services/session-service.js +113 -0
- package/lib/src/data-service/CircuitBreaker.js +83 -0
- package/lib/src/data-service/config-service.js +155 -0
- package/lib/src/data-service/db.js +122 -0
- package/lib/src/data-service/device-service.js +320 -0
- package/lib/src/data-service/device-store.interface.js +2 -0
- package/lib/src/data-service/device-store.js +345 -0
- package/lib/src/data-service/pending-sessions-service.js +25 -0
- package/lib/src/data-service/pluginArgs.js +25 -0
- package/lib/src/data-service/prisma-service.js +31 -0
- package/lib/src/data-service/prisma-store.js +385 -0
- package/lib/src/data-service/queue-service.js +150 -0
- package/lib/src/data-service/web-config-service.js +130 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +1155 -0
- package/lib/src/device-managers/ChromeDriverManager.js +68 -0
- package/lib/src/device-managers/HealthMonitorService.js +325 -0
- package/lib/src/device-managers/IOSDeviceManager.js +351 -0
- package/lib/src/device-managers/NodeDevices.js +82 -0
- package/lib/src/device-managers/android/AndroidStreamService.js +370 -0
- package/lib/src/device-managers/android/DeviceLockManager.js +45 -0
- package/lib/src/device-managers/cloud/CapabilityManager.js +26 -0
- package/lib/src/device-managers/cloud/Devices.js +86 -0
- package/lib/src/device-managers/iOSTracker.js +44 -0
- package/lib/src/device-managers/index.js +89 -0
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +268 -0
- package/lib/src/device-managers/ios/IOSStreamService.js +893 -0
- package/lib/src/device-managers/ios/WDAClient.js +866 -0
- package/lib/src/device-utils.js +663 -0
- package/lib/src/enums/Capabilities.js +8 -0
- package/lib/src/enums/Cloud.js +11 -0
- package/lib/src/enums/Platform.js +9 -0
- package/lib/src/enums/SessionType.js +9 -0
- package/lib/src/enums/SocketEvents.js +15 -0
- package/lib/src/helpers/UniversalMjpegProxy.js +273 -0
- package/lib/src/helpers/index.js +229 -0
- package/lib/src/index.js +95 -0
- package/lib/src/interceptors/CommandInterceptor.js +524 -0
- package/lib/src/interfaces/ICloudManager.js +2 -0
- package/lib/src/interfaces/IDevice.js +2 -0
- package/lib/src/interfaces/IDeviceFilterOptions.js +2 -0
- package/lib/src/interfaces/IDeviceManager.js +2 -0
- package/lib/src/interfaces/IOptions.js +2 -0
- package/lib/src/interfaces/IPluginArgs.js +55 -0
- package/lib/src/interfaces/ISessionCapability.js +2 -0
- package/lib/src/logger.js +225 -0
- package/lib/src/plugin.js +244 -0
- package/lib/src/prisma.js +12 -0
- package/lib/src/profiling/AndroidAppProfiler.js +213 -0
- package/lib/src/proxy/wd-command-proxy.js +221 -0
- package/lib/src/scripts/generate-database-migration.js +59 -0
- package/lib/src/scripts/initialize-database.js +55 -0
- package/lib/src/scripts/install-go-ios.js +66 -0
- package/lib/src/scripts/prepare-prisma.js +89 -0
- package/lib/src/services/AICommandService.js +143 -0
- package/lib/src/services/AIService.js +466 -0
- package/lib/src/services/CleanupService.js +141 -0
- package/lib/src/services/EventBus.js +74 -0
- package/lib/src/services/InspectorService.js +395 -0
- package/lib/src/services/MetricsService.js +134 -0
- package/lib/src/services/NetworkConditioningService.js +173 -0
- package/lib/src/services/NotificationService.js +163 -0
- package/lib/src/services/RequestLogService.js +252 -0
- package/lib/src/services/ResourceIsolationService.js +122 -0
- package/lib/src/services/SecurityService.js +120 -0
- package/lib/src/services/ServerManager.js +284 -0
- package/lib/src/services/SessionHeartbeatService.js +158 -0
- package/lib/src/services/SessionLifecycleService.js +572 -0
- package/lib/src/services/SocketClient.js +71 -0
- package/lib/src/services/SocketServer.js +87 -0
- package/lib/src/services/TracingService.js +132 -0
- package/lib/src/services/VideoPipelineService.js +220 -0
- package/lib/src/services/healing/FuzzyXmlHealingProvider.js +333 -0
- package/lib/src/services/healing/HealEtalonService.js +98 -0
- package/lib/src/services/healing/HealedLocatorGenerator.js +132 -0
- package/lib/src/services/healing/HealingOrchestrator.js +165 -0
- package/lib/src/services/healing/LlmHealingProvider.js +77 -0
- package/lib/src/services/healing/OcrHealingProvider.js +119 -0
- package/lib/src/services/healing/ResilioTreeHealingProvider.js +100 -0
- package/lib/src/services/healing/VisualAiHealingProvider.js +90 -0
- package/lib/src/services/healing/types.js +12 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +718 -0
- package/lib/src/services/omni-vision/VisionAssertionService.js +68 -0
- package/lib/src/sessions/CloudSession.js +42 -0
- package/lib/src/sessions/LocalSession.js +313 -0
- package/lib/src/sessions/RemoteSession.js +287 -0
- package/lib/src/sessions/SessionManager.js +238 -0
- package/lib/src/sessions/XenonSession.js +44 -0
- package/lib/src/types/CLIArgs.js +2 -0
- package/lib/src/types/CloudArgs.js +2 -0
- package/lib/src/types/CloudSchema.js +131 -0
- package/lib/src/types/DeviceType.js +2 -0
- package/lib/src/types/DeviceUpdate.js +2 -0
- package/lib/src/types/IOSDevice.js +2 -0
- package/lib/src/types/Platform.js +2 -0
- package/lib/src/types/SessionStatus.js +11 -0
- package/lib/src/validators/CapabilityValidator.js +93 -0
- package/lib/test/e2e/android/conf.spec.js +43 -0
- package/lib/test/e2e/android/conf2.spec.js +44 -0
- package/lib/test/e2e/android/conf3.spec.js +44 -0
- package/lib/test/e2e/e2ehelper.js +113 -0
- package/lib/test/e2e/hubnode/forward-request.spec.js +224 -0
- package/lib/test/e2e/hubnode/hubnode.spec.js +214 -0
- package/lib/test/e2e/ios/conf1.spec.js +39 -0
- package/lib/test/e2e/ios/conf2.spec.js +39 -0
- package/lib/test/e2e/plugin-harness.js +236 -0
- package/lib/test/e2e/plugin.spec.js +97 -0
- package/lib/test/e2e/telemetry_verification.spec.js +83 -0
- package/lib/test/e2e/video-recording-test.spec.js +63 -0
- package/lib/test/helpers/test-container.js +112 -0
- package/lib/test/integration/androidDevices.spec.js +137 -0
- package/lib/test/integration/cliArgs.js +73 -0
- package/lib/test/integration/ios/01iOSSimulator.spec.js +291 -0
- package/lib/test/integration/ios/02iOSDevices.spec.js +75 -0
- package/lib/test/integration/testHelpers.js +74 -0
- package/lib/test/unit/AndroidDeviceManager.spec.js +178 -0
- package/lib/test/unit/ChromeDriverManager.spec.js +26 -0
- package/lib/test/unit/CleanupService.spec.js +21 -0
- package/lib/test/unit/DeviceModel.spec.js +157 -0
- package/lib/test/unit/FuzzyXmlHealingProvider.test.js +294 -0
- package/lib/test/unit/GetAdbOriginal.js +42 -0
- package/lib/test/unit/HealingCascade.test.js +128 -0
- package/lib/test/unit/IOSDeviceManager.spec.js +261 -0
- package/lib/test/unit/RemoteIOs.spec.js +78 -0
- package/lib/test/unit/ResilioTreeHealingProvider.test.js +96 -0
- package/lib/test/unit/commands.spec.js +27 -0
- package/lib/test/unit/config.spec.js +27 -0
- package/lib/test/unit/device-service.spec.js +307 -0
- package/lib/test/unit/device-utils.spec.js +313 -0
- package/lib/test/unit/fixtures/device.config.js +4 -0
- package/lib/test/unit/fixtures/devices.js +89 -0
- package/lib/test/unit/helpers.spec.js +62 -0
- package/lib/test/unit/omni-vision.spec.js +100 -0
- package/lib/test/unit/plugin.spec.js +133 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +207 -0
- package/prisma/data.db +0 -0
- package/prisma/dev.db +0 -0
- package/prisma/dev.db-journal +0 -0
- package/prisma/migrations/20231011074725_initial_tables/migration.sql +47 -0
- package/prisma/migrations/20231226115334_update_session_log/migration.sql +2 -0
- package/prisma/migrations/20251204113710_add_video_recording_enabled/migration.sql +29 -0
- package/prisma/migrations/20251204132449_add_log_table/migration.sql +11 -0
- package/prisma/migrations/20251205050111_add_profiling_support/migration.sql +47 -0
- package/prisma/migrations/20251205050947_add_is_error_field/migration.sql +24 -0
- package/prisma/migrations/20260126201337_add_app_model/migration.sql +18 -0
- package/prisma/migrations/20260130115722_add_performance_trace_and_xenon_sync/migration.sql +2 -0
- package/prisma/migrations/20260130135114_add_device_models/migration.sql +57 -0
- package/prisma/migrations/20260130140655_make_systemport_optional/migration.sql +45 -0
- package/prisma/migrations/20260130140932_make_device_fields_optional/migration.sql +45 -0
- package/prisma/migrations/20260130141040_final_schema_fix/migration.sql +45 -0
- package/prisma/migrations/20260130143234_add_device_health_fields/migration.sql +4 -0
- package/prisma/migrations/20260130144921_add_failure_category/migration.sql +2 -0
- package/prisma/migrations/20260131151456_add_webhook_config/migration.sql +10 -0
- package/prisma/migrations/20260201094507_add_device_tags/migration.sql +11 -0
- package/prisma/migrations/20260201103410_add_managed_process/migration.sql +15 -0
- package/prisma/migrations/20260201140637_add_web_config/migration.sql +22 -0
- package/prisma/migrations/20260201162232_add_session_progress/migration.sql +2 -0
- package/prisma/migrations/20260201174231_add_total_healed_count/migration.sql +2 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +210 -0
- package/schema.json +348 -0
- package/scripts/build-xenon.sh +32 -0
- package/scripts/dev/debug-gemini.ts +44 -0
- package/scripts/generate-types-from-schema.js +86 -0
- package/scripts/install-compatible-driver.js +39 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
42
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
43
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
44
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
45
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
46
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
47
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.NetworkConditioningService = void 0;
|
|
55
|
+
const typedi_1 = require("typedi");
|
|
56
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
57
|
+
const NETWORK_PROFILES = {
|
|
58
|
+
Normal: null,
|
|
59
|
+
'4G': { latencyMs: 20, downloadKbps: 15000, uploadKbps: 7500 },
|
|
60
|
+
'3G': { latencyMs: 100, downloadKbps: 2000, uploadKbps: 1000 },
|
|
61
|
+
Edge: { latencyMs: 400, downloadKbps: 250, uploadKbps: 150 },
|
|
62
|
+
Offline: { latencyMs: 0, downloadKbps: 0, uploadKbps: 0 },
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Service to manage network conditioning (Throttling/Latency)
|
|
66
|
+
*/
|
|
67
|
+
let NetworkConditioningService = class NetworkConditioningService {
|
|
68
|
+
constructor() {
|
|
69
|
+
this.log = logger_1.default.scope('NetworkConditioning');
|
|
70
|
+
this.activeConditions = new Map();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Applies a network profile to a session/device
|
|
74
|
+
*/
|
|
75
|
+
applyProfile(sessionId, device, profile) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
this.log.info(`Applying network profile '${profile}' to session ${sessionId} on device ${device.udid}`);
|
|
78
|
+
this.activeConditions.set(sessionId, profile);
|
|
79
|
+
// Platform specific optimizations
|
|
80
|
+
if (device.platform === 'android') {
|
|
81
|
+
yield this.applyAndroidConditioning(device.udid, profile);
|
|
82
|
+
}
|
|
83
|
+
else if (device.deviceType === 'simulator') {
|
|
84
|
+
yield this.applyIOSSimulatorConditioning(device.udid, profile);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Gets the active profile for a session
|
|
90
|
+
*/
|
|
91
|
+
getProfile(sessionId) {
|
|
92
|
+
return this.activeConditions.get(sessionId);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resets network conditions for a session
|
|
96
|
+
*/
|
|
97
|
+
reset(sessionId, device) {
|
|
98
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
if (this.activeConditions.has(sessionId)) {
|
|
100
|
+
this.log.info(`Resetting network conditions for session ${sessionId}`);
|
|
101
|
+
this.activeConditions.delete(sessionId);
|
|
102
|
+
if (device.platform === 'android') {
|
|
103
|
+
yield this.applyAndroidConditioning(device.udid, 'Normal');
|
|
104
|
+
}
|
|
105
|
+
else if (device.deviceType === 'simulator') {
|
|
106
|
+
yield this.applyIOSSimulatorConditioning(device.udid, 'Normal');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
applyAndroidConditioning(udid, profile) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
try {
|
|
114
|
+
const { exec } = yield Promise.resolve().then(() => __importStar(require('child_process')));
|
|
115
|
+
const { promisify } = yield Promise.resolve().then(() => __importStar(require('util')));
|
|
116
|
+
const execAsync = promisify(exec);
|
|
117
|
+
if (profile === 'Offline') {
|
|
118
|
+
this.log.info(`📱 Android: Disabling data for ${udid}`);
|
|
119
|
+
yield execAsync(`adb -s ${udid} shell svc data disable`);
|
|
120
|
+
yield execAsync(`adb -s ${udid} shell svc wifi disable`);
|
|
121
|
+
}
|
|
122
|
+
else if (profile === 'Normal') {
|
|
123
|
+
this.log.info(`📱 Android: Enabling data for ${udid}`);
|
|
124
|
+
yield execAsync(`adb -s ${udid} shell svc data enable`);
|
|
125
|
+
yield execAsync(`adb -s ${udid} shell svc wifi enable`);
|
|
126
|
+
}
|
|
127
|
+
// Latency is handled by the proxy
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
this.log.warn(`⚠️ Failed to apply Android conditioning for ${udid}: ${e.message}`);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
applyIOSSimulatorConditioning(udid, profile) {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
try {
|
|
137
|
+
const { exec } = yield Promise.resolve().then(() => __importStar(require('child_process')));
|
|
138
|
+
const { promisify } = yield Promise.resolve().then(() => __importStar(require('util')));
|
|
139
|
+
const execAsync = promisify(exec);
|
|
140
|
+
// simctl network <device> <status>
|
|
141
|
+
// Profiles: wifi, 3g, edge, etc. (depends on OS version)
|
|
142
|
+
let status = 'none';
|
|
143
|
+
if (profile === '3G')
|
|
144
|
+
status = '3g';
|
|
145
|
+
if (profile === 'Edge')
|
|
146
|
+
status = 'edge';
|
|
147
|
+
if (profile === 'Offline')
|
|
148
|
+
status = 'off';
|
|
149
|
+
if (profile === 'Normal')
|
|
150
|
+
status = 'none';
|
|
151
|
+
this.log.info(`🍎 iOS Sim: Setting network to ${status} for ${udid}`);
|
|
152
|
+
yield execAsync(`xcrun simctl network ${udid} status ${status}`);
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
this.log.warn(`⚠️ Failed to apply iOS Simulator conditioning for ${udid}: ${e.message}`);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Returns the latency to inject into a command proxy for the given profile
|
|
161
|
+
*/
|
|
162
|
+
getLatency(sessionId) {
|
|
163
|
+
var _a;
|
|
164
|
+
const profile = this.activeConditions.get(sessionId);
|
|
165
|
+
if (!profile)
|
|
166
|
+
return 0;
|
|
167
|
+
return ((_a = NETWORK_PROFILES[profile]) === null || _a === void 0 ? void 0 : _a.latencyMs) || 0;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
exports.NetworkConditioningService = NetworkConditioningService;
|
|
171
|
+
exports.NetworkConditioningService = NetworkConditioningService = __decorate([
|
|
172
|
+
(0, typedi_1.Service)()
|
|
173
|
+
], NetworkConditioningService);
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
13
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
14
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
15
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
16
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.NotificationService = void 0;
|
|
25
|
+
const axios_1 = __importDefault(require("axios"));
|
|
26
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
27
|
+
const prisma_service_1 = require("../data-service/prisma-service");
|
|
28
|
+
const typedi_1 = require("typedi");
|
|
29
|
+
let NotificationService = class NotificationService {
|
|
30
|
+
constructor(prisma) {
|
|
31
|
+
this.prisma = prisma;
|
|
32
|
+
}
|
|
33
|
+
getConfigs() {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
return this.prisma.webhookConfig.findMany();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
saveConfig(url_1, events_1) {
|
|
39
|
+
return __awaiter(this, arguments, void 0, function* (url, events, type = 'slack', payloadTemplate) {
|
|
40
|
+
return this.prisma.webhookConfig.create({
|
|
41
|
+
data: {
|
|
42
|
+
url,
|
|
43
|
+
events: JSON.stringify(events),
|
|
44
|
+
type,
|
|
45
|
+
payloadTemplate,
|
|
46
|
+
active: true,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
deleteConfig(id) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
yield this.prisma.webhookConfig.delete({ where: { id } });
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
dispatchEvent(eventType, payload) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
const configs = yield this.getConfigs();
|
|
59
|
+
for (const config of configs) {
|
|
60
|
+
if (!config.active)
|
|
61
|
+
continue;
|
|
62
|
+
try {
|
|
63
|
+
const events = JSON.parse(config.events);
|
|
64
|
+
if (events.includes(eventType)) {
|
|
65
|
+
yield this.sendToWebhook(config, eventType, payload);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
logger_1.default.error(`Failed to process webhook config ${config.id}: ${err}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
sendToWebhook(config, eventType, payload) {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
+
// Principal Logic: Use custom template if defined
|
|
77
|
+
if (config.payloadTemplate) {
|
|
78
|
+
const substitutedBody = this.substituteTemplate(config.payloadTemplate, Object.assign({ eventType }, payload));
|
|
79
|
+
try {
|
|
80
|
+
// Try to parse as JSON first
|
|
81
|
+
const jsonBody = JSON.parse(substitutedBody);
|
|
82
|
+
yield axios_1.default.post(config.url, jsonBody);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
// Fallback to sending as plain text or simple object
|
|
86
|
+
yield axios_1.default.post(config.url, { text: substitutedBody });
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (config.type === 'slack') {
|
|
91
|
+
yield this.sendSlackMessage(config.url, eventType, payload);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Generic webhook fallback
|
|
95
|
+
try {
|
|
96
|
+
yield axios_1.default.post(config.url, { event: eventType, payload });
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
logger_1.default.error(`Webhook failed for ${config.url}: ${err}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Principal Logic: Recursive variable substitution with {{key}} support
|
|
105
|
+
substituteTemplate(template, data) {
|
|
106
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
|
107
|
+
const keys = key.trim().split('.');
|
|
108
|
+
let value = data;
|
|
109
|
+
for (const k of keys) {
|
|
110
|
+
value = value ? value[k] : undefined;
|
|
111
|
+
}
|
|
112
|
+
return value !== undefined ? String(value) : match;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
sendSlackMessage(url, eventType, payload) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
let text = '';
|
|
118
|
+
let color = '#36a64f'; // green
|
|
119
|
+
switch (eventType) {
|
|
120
|
+
case 'device_offline':
|
|
121
|
+
text = `🚨 *Device Offline*: ${payload.udid} (${payload.host})`;
|
|
122
|
+
color = '#ff0000';
|
|
123
|
+
break;
|
|
124
|
+
case 'session_failed':
|
|
125
|
+
text = `❌ *Session Failed*: ${payload.sessionId}\nReason: ${payload.failureReason}`;
|
|
126
|
+
color = '#ff0000';
|
|
127
|
+
break;
|
|
128
|
+
case 'device_new':
|
|
129
|
+
text = `📱 *New Device Connected*: ${payload.name} (${payload.udid})`;
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
text = `Event: ${eventType}\nPayload: ${JSON.stringify(payload)}`;
|
|
133
|
+
}
|
|
134
|
+
const body = {
|
|
135
|
+
attachments: [
|
|
136
|
+
{
|
|
137
|
+
color,
|
|
138
|
+
text,
|
|
139
|
+
fields: Object.keys(payload).map((k) => ({
|
|
140
|
+
title: k,
|
|
141
|
+
value: typeof payload[k] === 'object' ? JSON.stringify(payload[k]) : String(payload[k]),
|
|
142
|
+
short: true,
|
|
143
|
+
})),
|
|
144
|
+
footer: 'Xenon Device Farm',
|
|
145
|
+
ts: Math.floor(Date.now() / 1000),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
try {
|
|
150
|
+
yield axios_1.default.post(url, body);
|
|
151
|
+
logger_1.default.info(`Slack notification sent to ${url}`);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
logger_1.default.error(`Failed to send Slack notification: ${err}`);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
exports.NotificationService = NotificationService;
|
|
160
|
+
exports.NotificationService = NotificationService = __decorate([
|
|
161
|
+
(0, typedi_1.Service)(),
|
|
162
|
+
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
|
|
163
|
+
], NotificationService);
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
45
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
46
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
47
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
48
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
49
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
50
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
54
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
55
|
+
};
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.RequestLogService = void 0;
|
|
58
|
+
const typedi_1 = require("typedi");
|
|
59
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
60
|
+
/**
|
|
61
|
+
* Sanitizes sensitive data from request payloads before logging.
|
|
62
|
+
* Redacts passwords, tokens, API keys, and other secrets.
|
|
63
|
+
*/
|
|
64
|
+
function sanitizePayload(data, depth = 0) {
|
|
65
|
+
if (depth > 5)
|
|
66
|
+
return '[MAX_DEPTH]';
|
|
67
|
+
if (data === null || data === undefined)
|
|
68
|
+
return data;
|
|
69
|
+
if (typeof data === 'string') {
|
|
70
|
+
// Truncate very long strings (e.g., base64 images)
|
|
71
|
+
if (data.length > 500) {
|
|
72
|
+
return `[STRING_TRUNCATED: ${data.length} chars]`;
|
|
73
|
+
}
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
if (Array.isArray(data)) {
|
|
77
|
+
if (data.length > 10) {
|
|
78
|
+
return `[ARRAY_TRUNCATED: ${data.length} items]`;
|
|
79
|
+
}
|
|
80
|
+
return data.map((item) => sanitizePayload(item, depth + 1));
|
|
81
|
+
}
|
|
82
|
+
if (typeof data === 'object') {
|
|
83
|
+
const sanitized = {};
|
|
84
|
+
const sensitiveKeys = [
|
|
85
|
+
'password',
|
|
86
|
+
'token',
|
|
87
|
+
'apikey',
|
|
88
|
+
'api_key',
|
|
89
|
+
'secret',
|
|
90
|
+
'authorization',
|
|
91
|
+
'auth',
|
|
92
|
+
'credential',
|
|
93
|
+
'key',
|
|
94
|
+
'private',
|
|
95
|
+
'accesstoken',
|
|
96
|
+
'access_token',
|
|
97
|
+
'refreshtoken',
|
|
98
|
+
'refresh_token',
|
|
99
|
+
'bearer',
|
|
100
|
+
'jwt',
|
|
101
|
+
];
|
|
102
|
+
for (const [key, value] of Object.entries(data)) {
|
|
103
|
+
const lowerKey = key.toLowerCase();
|
|
104
|
+
if (sensitiveKeys.some((s) => lowerKey.includes(s))) {
|
|
105
|
+
sanitized[key] = '[REDACTED]';
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
sanitized[key] = sanitizePayload(value, depth + 1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return sanitized;
|
|
112
|
+
}
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* RequestLogService provides centralized logging for HTTP requests.
|
|
117
|
+
*
|
|
118
|
+
* Features:
|
|
119
|
+
* - In-memory ring buffer for recent requests (fast access)
|
|
120
|
+
* - Database persistence for audit trail (optional)
|
|
121
|
+
* - Automatic payload sanitization (no secrets in logs)
|
|
122
|
+
* - Correlation ID tracking for distributed tracing
|
|
123
|
+
*/
|
|
124
|
+
let RequestLogService = class RequestLogService {
|
|
125
|
+
constructor() {
|
|
126
|
+
this.ringBuffer = [];
|
|
127
|
+
this.bufferSize = 500; // Keep last 500 requests in memory
|
|
128
|
+
this.persistToDb = false; // Enable for full audit trail
|
|
129
|
+
logger_1.default.info('[RequestLogService] Initialized with buffer size', this.bufferSize);
|
|
130
|
+
// Principal Decoupling: Subscribe to global events
|
|
131
|
+
Promise.resolve().then(() => __importStar(require('./EventBus'))).then(({ EVENT_BUS }) => {
|
|
132
|
+
EVENT_BUS.on('http:outgoing', (data) => this.logRequest(data));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Enable/disable database persistence
|
|
137
|
+
*/
|
|
138
|
+
setPersistence(enabled) {
|
|
139
|
+
this.persistToDb = enabled;
|
|
140
|
+
logger_1.default.info(`[RequestLogService] Database persistence: ${enabled ? 'enabled' : 'disabled'}`);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Log an outgoing HTTP request
|
|
144
|
+
*/
|
|
145
|
+
logRequest(entry) {
|
|
146
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
const fullEntry = Object.assign(Object.assign({}, entry), { timestamp: new Date(), requestBody: entry.requestBody
|
|
148
|
+
? JSON.stringify(sanitizePayload(JSON.parse(entry.requestBody || '{}')))
|
|
149
|
+
: undefined, responseBody: entry.responseBody
|
|
150
|
+
? JSON.stringify(sanitizePayload(JSON.parse(entry.responseBody || '{}')))
|
|
151
|
+
: undefined });
|
|
152
|
+
// Add to ring buffer
|
|
153
|
+
this.ringBuffer.push(fullEntry);
|
|
154
|
+
if (this.ringBuffer.length > this.bufferSize) {
|
|
155
|
+
this.ringBuffer.shift();
|
|
156
|
+
}
|
|
157
|
+
// Log to console with appropriate level
|
|
158
|
+
if (entry.error) {
|
|
159
|
+
logger_1.default.error(`[HTTP ${entry.direction.toUpperCase()}] ${entry.method} ${entry.url} ` +
|
|
160
|
+
`[${entry.statusCode || 'ERR'}] ${entry.durationMs}ms - ${entry.error}`);
|
|
161
|
+
}
|
|
162
|
+
else if ((entry.statusCode || 0) >= 400) {
|
|
163
|
+
logger_1.default.warn(`[HTTP ${entry.direction.toUpperCase()}] ${entry.method} ${entry.url} ` +
|
|
164
|
+
`[${entry.statusCode}] ${entry.durationMs}ms`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
logger_1.default.debug(`[HTTP ${entry.direction.toUpperCase()}] ${entry.method} ${entry.url} ` +
|
|
168
|
+
`[${entry.statusCode}] ${entry.durationMs}ms`);
|
|
169
|
+
}
|
|
170
|
+
// Persist to database if enabled
|
|
171
|
+
if (this.persistToDb) {
|
|
172
|
+
yield this.persistEntry(fullEntry);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get recent request logs from the ring buffer
|
|
178
|
+
*/
|
|
179
|
+
getRecentLogs(limit = 50, filter) {
|
|
180
|
+
let logs = [...this.ringBuffer].reverse();
|
|
181
|
+
if (filter) {
|
|
182
|
+
if (filter.method) {
|
|
183
|
+
logs = logs.filter((l) => l.method === filter.method);
|
|
184
|
+
}
|
|
185
|
+
if (filter.urlPattern) {
|
|
186
|
+
const pattern = filter.urlPattern;
|
|
187
|
+
logs = logs.filter((l) => l.url.includes(pattern));
|
|
188
|
+
}
|
|
189
|
+
if (filter.direction) {
|
|
190
|
+
logs = logs.filter((l) => l.direction === filter.direction);
|
|
191
|
+
}
|
|
192
|
+
if (filter.hasError !== undefined) {
|
|
193
|
+
logs = logs.filter((l) => (filter.hasError ? !!l.error : !l.error));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return logs.slice(0, limit);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get summary statistics
|
|
200
|
+
*/
|
|
201
|
+
getStats() {
|
|
202
|
+
const logs = this.ringBuffer;
|
|
203
|
+
const byMethod = {};
|
|
204
|
+
const byStatusCode = {};
|
|
205
|
+
let errorCount = 0;
|
|
206
|
+
let totalDuration = 0;
|
|
207
|
+
for (const entry of logs) {
|
|
208
|
+
byMethod[entry.method] = (byMethod[entry.method] || 0) + 1;
|
|
209
|
+
if (entry.statusCode) {
|
|
210
|
+
byStatusCode[entry.statusCode] = (byStatusCode[entry.statusCode] || 0) + 1;
|
|
211
|
+
}
|
|
212
|
+
if (entry.error)
|
|
213
|
+
errorCount++;
|
|
214
|
+
if (entry.durationMs)
|
|
215
|
+
totalDuration += entry.durationMs;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
totalLogged: logs.length,
|
|
219
|
+
errorCount,
|
|
220
|
+
avgDurationMs: logs.length > 0 ? Math.round(totalDuration / logs.length) : 0,
|
|
221
|
+
byMethod,
|
|
222
|
+
byStatusCode,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Clear the ring buffer
|
|
227
|
+
*/
|
|
228
|
+
clear() {
|
|
229
|
+
this.ringBuffer = [];
|
|
230
|
+
logger_1.default.info('[RequestLogService] Buffer cleared');
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Persist entry to database (for audit trail)
|
|
234
|
+
*/
|
|
235
|
+
persistEntry(entry) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
try {
|
|
238
|
+
// Using a generic Log table or you could create a dedicated RequestLog table
|
|
239
|
+
// For now, we'll just log - you can add Prisma model later
|
|
240
|
+
// await prisma.requestLog.create({ data: { ... } });
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
logger_1.default.warn(`[RequestLogService] Failed to persist log: ${err.message}`);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
exports.RequestLogService = RequestLogService;
|
|
249
|
+
exports.RequestLogService = RequestLogService = __decorate([
|
|
250
|
+
(0, typedi_1.Service)(),
|
|
251
|
+
__metadata("design:paramtypes", [])
|
|
252
|
+
], RequestLogService);
|