@velor/remote-mouse 6.4.8

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.
Files changed (316) hide show
  1. package/.artifacts/screenshots/remote-mobile-config.png +0 -0
  2. package/.artifacts/screenshots/remote-mobile-home.png +0 -0
  3. package/.artifacts/screenshots/remote-mobile-preferences.png +0 -0
  4. package/.artifacts/screenshots/remote-mobile-server-info.png +0 -0
  5. package/.artifacts/screenshots/remote-mobile-vlc.png +0 -0
  6. package/.env.example +93 -0
  7. package/README.md +370 -0
  8. package/bin/remote-mouse.js +19 -0
  9. package/client/core/dom.js +148 -0
  10. package/client/core/init-socket-io.js +117 -0
  11. package/client/core/socket-emit.js +6 -0
  12. package/client/core/window-events.js +23 -0
  13. package/client/i18n/apply-page-translations.js +18 -0
  14. package/client/i18n/constants.js +3 -0
  15. package/client/i18n/core.js +40 -0
  16. package/client/i18n/locale-registry.js +77 -0
  17. package/client/i18n/locales/de.js +227 -0
  18. package/client/i18n/locales/en.js +389 -0
  19. package/client/i18n/locales/es.js +228 -0
  20. package/client/i18n/locales/fr.js +389 -0
  21. package/client/i18n/locales/it.js +227 -0
  22. package/client/i18n/locales/ja.js +227 -0
  23. package/client/i18n/locales/pt.js +227 -0
  24. package/client/i18n/locales/ru.js +240 -0
  25. package/client/i18n/locales/zh.js +227 -0
  26. package/client/preferences/constants.js +15 -0
  27. package/client/preferences/effects.js +24 -0
  28. package/client/preferences/load.js +19 -0
  29. package/client/preferences/render.js +26 -0
  30. package/client/preferences/rows.js +78 -0
  31. package/client/preferences/state.js +29 -0
  32. package/client/preview/create-preview-frame-receiver.js +13 -0
  33. package/client/preview/draw.js +77 -0
  34. package/client/preview/parse-preview-frame.js +39 -0
  35. package/client/preview/preview-stream.js +152 -0
  36. package/client/services/app-state/appStateKeys.js +30 -0
  37. package/client/services/app-state/createAppStateService.js +216 -0
  38. package/client/services/app-state/createPersistStoreService.js +122 -0
  39. package/client/services/app-state/createStateStoreService.js +64 -0
  40. package/client/services/app-state/localStorageBinding.js +26 -0
  41. package/client/services/app-state/normalizers.js +74 -0
  42. package/client/services/app-state/resolveEffectiveStateValue.js +58 -0
  43. package/client/services/app-state/stateDefinitions.js +107 -0
  44. package/client/services/backend/createBackendService.js +55 -0
  45. package/client/services/config/ConfigView.js +45 -0
  46. package/client/services/config/createClientConfigService.js +122 -0
  47. package/client/services/config/defaultClientConfig.js +22 -0
  48. package/client/services/createServicesContainer.js +48 -0
  49. package/client/services/createServicesRegistry.js +26 -0
  50. package/client/services/i18n/createI18nService.js +86 -0
  51. package/client/services/notifications/createNotificationService.js +56 -0
  52. package/client/services/pubsub/createPubSubService.js +20 -0
  53. package/client/services/remotes/createRemotesService.js +20 -0
  54. package/client/services/transport/createSocketIoTransportService.js +50 -0
  55. package/client/touch/bindTouchPassthrough.js +94 -0
  56. package/client/touch/createTouchList.js +18 -0
  57. package/client/touch/dispatchSyntheticTouch.js +20 -0
  58. package/client/touch/gesture-zone.js +28 -0
  59. package/client/touch/throttle.js +46 -0
  60. package/client/touch/touch-handlers.js +193 -0
  61. package/client/touch/touchpad.js +231 -0
  62. package/client/ui/config/page.js +840 -0
  63. package/client/ui/main/applyRemoteVisibilityState.js +60 -0
  64. package/client/ui/main/bind-client-notifications.js +3 -0
  65. package/client/ui/main/bindings/bindAccordion.js +121 -0
  66. package/client/ui/main/bindings/bindActionButtons.js +13 -0
  67. package/client/ui/main/bindings/bindAdminDrawer.js +137 -0
  68. package/client/ui/main/bindings/bindAdminRemoteButtons.js +77 -0
  69. package/client/ui/main/bindings/bindAdminVersion.js +27 -0
  70. package/client/ui/main/bindings/bindBrowserRemoteButtons.js +119 -0
  71. package/client/ui/main/bindings/bindRemoteAccordion.js +23 -0
  72. package/client/ui/main/bindings/bindSamsungRemoteButtons.js +320 -0
  73. package/client/ui/main/bindings/bindSystemRemoteButtons.js +41 -0
  74. package/client/ui/main/bindings/bindVlcRemoteButtons.js +64 -0
  75. package/client/ui/main/canvas-ui.js +33 -0
  76. package/client/ui/main/connection-overlay.js +57 -0
  77. package/client/ui/main/init-ui.js +54 -0
  78. package/client/ui/main/keyboard-panel.js +271 -0
  79. package/client/ui/main/mouse-buttons.js +94 -0
  80. package/client/ui/main/page.js +21 -0
  81. package/client/ui/notifications/show-toast.js +48 -0
  82. package/client/ui/preferences/bindPreferenceSwitchers.js +49 -0
  83. package/client/ui/preferences/page.js +61 -0
  84. package/client/ui/preferences/switchers.js +200 -0
  85. package/index.js +9 -0
  86. package/package.json +41 -0
  87. package/public/admin-config.css +447 -0
  88. package/public/admin-config.html +63 -0
  89. package/public/apple-touch-icon.png +0 -0
  90. package/public/brave-icon.svg +13 -0
  91. package/public/favicon.svg +30 -0
  92. package/public/icon-192.png +0 -0
  93. package/public/icon-512.png +0 -0
  94. package/public/index.html +246 -0
  95. package/public/manifest.webmanifest +45 -0
  96. package/public/preferences.html +55 -0
  97. package/public/server-info.html +574 -0
  98. package/public/styles.css +1042 -0
  99. package/public/sw.js +103 -0
  100. package/scripts/codex-commit.sh +112 -0
  101. package/scripts/generate-mobile-screenshots.mjs +244 -0
  102. package/scripts/install-linux.sh +633 -0
  103. package/scripts/install-windows.ps1 +369 -0
  104. package/server/application/createApplicationLifecycle.js +15 -0
  105. package/server/application/logger.js +216 -0
  106. package/server/application/shutdown.js +81 -0
  107. package/server/application/start.js +95 -0
  108. package/server/application/state.js +19 -0
  109. package/server/connection/api/admin-actions.router.js +65 -0
  110. package/server/connection/api/admin-configs.router.js +165 -0
  111. package/server/connection/api/admin-remotes.router.js +61 -0
  112. package/server/connection/api/admin-subs.router.js +49 -0
  113. package/server/connection/api/client.router.js +15 -0
  114. package/server/connection/api/configs.js +96 -0
  115. package/server/connection/api/qr-page.handler.js +17 -0
  116. package/server/connection/api/remotes.router.js +30 -0
  117. package/server/connection/api/server-info.router.js +149 -0
  118. package/server/connection/api/session.middleware.js +93 -0
  119. package/server/connection/api/unauthorized-response.js +73 -0
  120. package/server/connection/socket/createConnectionRegistrar.js +23 -0
  121. package/server/connection/socket/createSocketSessionAuthMiddleware.js +49 -0
  122. package/server/connection/socket/socket-action-responder.js +12 -0
  123. package/server/connection/socket/socket.timestamp-middleware.js +51 -0
  124. package/server/index.js +12 -0
  125. package/server/init/bootstrapApi.js +87 -0
  126. package/server/init/bootstrapSocket.js +79 -0
  127. package/server/init/createAdminApiRouter.js +22 -0
  128. package/server/init/createAdminUiRouter.js +21 -0
  129. package/server/init/createSocketActionRegistrars.js +52 -0
  130. package/server/init/observers/startConfigObserver.js +51 -0
  131. package/server/init/observers/startDisplaySizeObserver.js +55 -0
  132. package/server/init/observers/startNotificationObserver.js +49 -0
  133. package/server/init/observers/startQrOverlayHoverObserver.js +89 -0
  134. package/server/init/observers/startQrOverlayRefreshObserver.js +53 -0
  135. package/server/os/darwin/app.js +45 -0
  136. package/server/os/darwin/applescript.js +51 -0
  137. package/server/os/darwin/cliSocket.js +53 -0
  138. package/server/os/darwin/url.js +9 -0
  139. package/server/os/darwin/window.js +10 -0
  140. package/server/os/linux/app.js +120 -0
  141. package/server/os/linux/cliSocket.js +53 -0
  142. package/server/os/linux/process.js +69 -0
  143. package/server/os/linux/screen.js +58 -0
  144. package/server/os/linux/systemd.js +139 -0
  145. package/server/os/linux/url.js +17 -0
  146. package/server/os/linux/windows.js +144 -0
  147. package/server/os/win32/app.js +66 -0
  148. package/server/os/win32/cliSocket.js +15 -0
  149. package/server/os/win32/powershell.js +70 -0
  150. package/server/os/win32/process.js +20 -0
  151. package/server/os/win32/service.js +95 -0
  152. package/server/os/win32/url.js +9 -0
  153. package/server/os/win32/window.js +10 -0
  154. package/server/remotes/admin/createAdminEventGuardMiddleware.js +29 -0
  155. package/server/remotes/admin/createForceUpdateCheckAction.js +39 -0
  156. package/server/remotes/admin/createInstallUpdateAction.js +64 -0
  157. package/server/remotes/admin/createOpenQrBrowserAction.js +63 -0
  158. package/server/remotes/admin/createOpenServerInfoBrowserAction.js +58 -0
  159. package/server/remotes/admin/createRestartServiceAction.js +20 -0
  160. package/server/remotes/admin/createRotateEntryTokenAction.js +43 -0
  161. package/server/remotes/admin/createToggleQrOverlayAction.js +38 -0
  162. package/server/remotes/admin/index.js +32 -0
  163. package/server/remotes/admin/notifyIfRestarted.js +102 -0
  164. package/server/remotes/admin/registrar.js +94 -0
  165. package/server/remotes/browser/actions/createFocusOrLaunchBrowserAction.js +22 -0
  166. package/server/remotes/browser/actions/openUrlOnHost.js +3 -0
  167. package/server/remotes/browser/browserCatalog.js +70 -0
  168. package/server/remotes/browser/index.js +15 -0
  169. package/server/remotes/browser/listBrowsersOnHost.js +23 -0
  170. package/server/remotes/browser/registrar.js +29 -0
  171. package/server/remotes/browser/spec.js +18 -0
  172. package/server/remotes/browser/state.js +5 -0
  173. package/server/remotes/input/registrar.js +39 -0
  174. package/server/remotes/preview/bgraToRgbaBuffer.js +20 -0
  175. package/server/remotes/preview/captureAroundCursor.js +19 -0
  176. package/server/remotes/preview/createPreviewStreamer.js +85 -0
  177. package/server/remotes/preview/registrar.js +34 -0
  178. package/server/remotes/samsung/createSamsungCommandService.js +203 -0
  179. package/server/remotes/samsung/createSamsungRemote.js +26 -0
  180. package/server/remotes/samsung/createSamsungRemoteGetter.js +46 -0
  181. package/server/remotes/samsung/createSamsungTvGetter.js +102 -0
  182. package/server/remotes/samsung/device-config.js +122 -0
  183. package/server/remotes/samsung/registrar.js +59 -0
  184. package/server/remotes/samsung/utils.js +56 -0
  185. package/server/remotes/vlc/index.js +39 -0
  186. package/server/remotes/vlc/registrar.js +94 -0
  187. package/server/remotes/vlc/spec.js +15 -0
  188. package/server/remotes/vlc/state.js +5 -0
  189. package/server/remotes/window/index.js +6 -0
  190. package/server/remotes/window/registrar.js +28 -0
  191. package/server/services/application/createApplicationDaemonService.js +144 -0
  192. package/server/services/application/getDaemonServiceAdapter.js +56 -0
  193. package/server/services/application/utils.js +29 -0
  194. package/server/services/config/bootstrapConfig.js +38 -0
  195. package/server/services/config/configPaths.js +42 -0
  196. package/server/services/config/configSchema.js +297 -0
  197. package/server/services/config/configService.js +89 -0
  198. package/server/services/config/defaultConfig.js +137 -0
  199. package/server/services/config/envConfig.js +49 -0
  200. package/server/services/config/index.js +9 -0
  201. package/server/services/config/logConfig.js +54 -0
  202. package/server/services/config/system-config.js +44 -0
  203. package/server/services/createServicesContainer.js +94 -0
  204. package/server/services/createServicesRegistry.js +48 -0
  205. package/server/services/input/createInputController.js +17 -0
  206. package/server/services/input/createKeyboardController.js +146 -0
  207. package/server/services/input/createMouseController.js +182 -0
  208. package/server/services/notifier/createClientNotifier.js +23 -0
  209. package/server/services/notifier/createHostNotifier.js +41 -0
  210. package/server/services/notifier/createNotifier.js +13 -0
  211. package/server/services/notifier/createNotifierComposite.js +122 -0
  212. package/server/services/notifier/handleOpenQrBrowserEvent.js +40 -0
  213. package/server/services/notifier/handleOpenServerInfoBrowserEvent.js +40 -0
  214. package/server/services/notifier/handleRestartServiceEvent.js +37 -0
  215. package/server/services/notifier/handleRotateEntryTokenEvent.js +42 -0
  216. package/server/services/notifier/handleSessionEvent.js +24 -0
  217. package/server/services/notifier/handleToggleQrOverlayEvent.js +21 -0
  218. package/server/services/notifier/host-notifier/createLinuxHostNotifier.js +23 -0
  219. package/server/services/notifier/host-notifier/createNodeNotifierHostNotifier.js +24 -0
  220. package/server/services/notifier/host-notifier/index.js +11 -0
  221. package/server/services/notifier/mapper/handleForceUpdateCheckEvent.js +24 -0
  222. package/server/services/notifier/mapper/handleInstallUpdateEvent.js +53 -0
  223. package/server/services/notifier/mapper/handleSocketEvent.js +40 -0
  224. package/server/services/notifier/mapper/handleUpdateManagerEvent.js +24 -0
  225. package/server/services/notifier/mapper/notify.js +7 -0
  226. package/server/services/os/index.js +23 -0
  227. package/server/services/os/platforms/createFallbackAdapter.js +31 -0
  228. package/server/services/os/platforms/darwin/app.js +6 -0
  229. package/server/services/os/platforms/darwin/index.js +21 -0
  230. package/server/services/os/platforms/darwin/scripts.js +4 -0
  231. package/server/services/os/platforms/darwin/url.js +1 -0
  232. package/server/services/os/platforms/darwin/window.js +4 -0
  233. package/server/services/os/platforms/linux/app.js +6 -0
  234. package/server/services/os/platforms/linux/index.js +33 -0
  235. package/server/services/os/platforms/linux/url.js +1 -0
  236. package/server/services/os/platforms/linux/windows.js +11 -0
  237. package/server/services/os/platforms/win32/app.js +6 -0
  238. package/server/services/os/platforms/win32/index.js +21 -0
  239. package/server/services/os/platforms/win32/overlay.js +6 -0
  240. package/server/services/os/platforms/win32/powershell.js +4 -0
  241. package/server/services/os/platforms/win32/url.js +1 -0
  242. package/server/services/os/platforms/win32/window.js +4 -0
  243. package/server/services/os/shared.js +2 -0
  244. package/server/services/overlay/createNoopOverlay.js +13 -0
  245. package/server/services/overlay/createQrOverlay.js +27 -0
  246. package/server/services/overlay/createQrOverlayWin32.js +158 -0
  247. package/server/services/overlay/createQrOverlayYad.js +178 -0
  248. package/server/services/overlay/renderQrPage.js +82 -0
  249. package/server/services/persistence/createConfigDao.js +106 -0
  250. package/server/services/persistence/createDatabaseProvider.js +23 -0
  251. package/server/services/persistence/createEntryTokenDao.js +140 -0
  252. package/server/services/persistence/createRestartLogDao.js +210 -0
  253. package/server/services/persistence/index.js +19 -0
  254. package/server/services/pubsub/createEventStore.js +54 -0
  255. package/server/services/pubsub/createPubSub.js +89 -0
  256. package/server/services/pubsub/createServiceEvents.js +29 -0
  257. package/server/services/pubsub/serviceEventConstants.js +39 -0
  258. package/server/services/remotes/createRemotes.js +25 -0
  259. package/server/services/server/createHttpsServer.js +12 -0
  260. package/server/services/server/createServer.js +66 -0
  261. package/server/services/sse/createSseService.js +96 -0
  262. package/server/services/sse/utils.js +7 -0
  263. package/server/services/system/createSystemService.js +170 -0
  264. package/server/services/system/getNetworkInfo.js +18 -0
  265. package/server/services/system/getScreenInfo.js +32 -0
  266. package/server/services/system/listNetworkInterfaces.js +27 -0
  267. package/server/services/task-manager/createTaskManager.js +68 -0
  268. package/server/services/task-runner/createTaskRunner.js +114 -0
  269. package/server/services/token-manager/createTokenManager.js +244 -0
  270. package/server/services/token-manager/token-format.js +7 -0
  271. package/server/services/token-manager/token-store-utils.js +8 -0
  272. package/server/services/update-manager/CommandUpdateSource.js +70 -0
  273. package/server/services/update-manager/NpmUpdateSource.js +34 -0
  274. package/server/services/update-manager/buildNpmGlobalUpdateCommand.js +15 -0
  275. package/server/services/update-manager/chooseUpdateCheckSource.js +20 -0
  276. package/server/services/update-manager/chooseUpdateInstallSource.js +5 -0
  277. package/server/services/update-manager/createUpdateManager.js +109 -0
  278. package/server/services/update-manager/resolveInstallUpdateCommand.js +42 -0
  279. package/server/term/cli/executeLocalServiceCommand.js +42 -0
  280. package/server/term/cli/getCliSocketPath.js +5 -0
  281. package/server/term/cli/parseCliArgs.js +110 -0
  282. package/server/term/cli/printCliResult.js +18 -0
  283. package/server/term/cli/runCliCmd.js +50 -0
  284. package/server/term/cli/sendCliCommand.js +76 -0
  285. package/server/term/cliSocket.js +29 -0
  286. package/server/term/srv/commands/configCommand.js +84 -0
  287. package/server/term/srv/commands/helpCommand.js +6 -0
  288. package/server/term/srv/commands/infoCommand.js +7 -0
  289. package/server/term/srv/commands/openQrCommand.js +7 -0
  290. package/server/term/srv/commands/samsungDetectCommand.js +52 -0
  291. package/server/term/srv/commands/serviceCommand.js +25 -0
  292. package/server/term/srv/commands/systemConfigCommand.js +7 -0
  293. package/server/term/srv/commands/tasksCommand.js +24 -0
  294. package/server/term/srv/commands/tokensCommand.js +15 -0
  295. package/server/term/srv/executeCliCommand.js +44 -0
  296. package/server/term/srv/executeCliRequest.js +38 -0
  297. package/server/term/srv/startCliServer.js +140 -0
  298. package/server/utils/createRandomToken.js +11 -0
  299. package/server/utils/env.js +155 -0
  300. package/server/utils/git.js +13 -0
  301. package/server/utils/http.js +7 -0
  302. package/server/utils/network.js +45 -0
  303. package/server/utils/paths.js +10 -0
  304. package/server/utils/process.js +57 -0
  305. package/server/utils/robot.js +11 -0
  306. package/server/utils/semver.js +33 -0
  307. package/server/utils/truncateText.js +7 -0
  308. package/utils/createLazy.js +13 -0
  309. package/utils/functional.js +48 -0
  310. package/utils/math.js +26 -0
  311. package/utils/notificationSettings.js +61 -0
  312. package/utils/object.utils.js +79 -0
  313. package/utils/predicates.js +17 -0
  314. package/utils/remoteCommands.js +40 -0
  315. package/utils/sync.js +3 -0
  316. package/velor-remote-mouse-6.4.8.tgz +0 -0
@@ -0,0 +1,149 @@
1
+ import express from 'express';
2
+ import {createHash} from 'node:crypto';
3
+ import path from 'node:path';
4
+ import {computeTokenTtlMs} from '../../services/token-manager/token-store-utils.js';
5
+ import {publicDir, projectRoot} from '../../utils/paths.js';
6
+ import {getRecentLogs} from '../../application/logger.js';
7
+ import {readPackageVersion} from '../../utils/env.js';
8
+
9
+ const packageJsonPath = path.join(projectRoot, 'package.json');
10
+
11
+ function redactSecrets(value, key = '') {
12
+ const keyLower = String(key || '').toLowerCase();
13
+ if (keyLower.includes('secret') || keyLower.includes('password')) {
14
+ return '[redacted]';
15
+ }
16
+
17
+ if (Array.isArray(value)) {
18
+ return value.map((item) => redactSecrets(item));
19
+ }
20
+
21
+ if (value && typeof value === 'object') {
22
+ const out = {};
23
+ for (const [childKey, childValue] of Object.entries(value)) {
24
+ out[childKey] = redactSecrets(childValue, childKey);
25
+ }
26
+ return out;
27
+ }
28
+
29
+ return value;
30
+ }
31
+
32
+ function getConnectedClients(io) {
33
+ return Array.from(io.of('/').sockets.values()).map((socket) => ({
34
+ id: socket.id,
35
+ connectedAt: Number.isFinite(socket?.handshake?.issued)
36
+ ? new Date(socket.handshake.issued).toISOString()
37
+ : null,
38
+ address: socket?.handshake?.address || socket?.conn?.remoteAddress || null,
39
+ transport: socket?.conn?.transport?.name || 'unknown',
40
+ }));
41
+ }
42
+
43
+ function maskToken(token) {
44
+ const normalized = String(token || '').trim();
45
+ if (!normalized) {
46
+ return '[masque]';
47
+ }
48
+
49
+ const fingerprint = createHash('sha256')
50
+ .update(normalized)
51
+ .digest('hex')
52
+ .slice(0, 8);
53
+
54
+ return `[masque:${fingerprint}]`;
55
+ }
56
+
57
+ function buildTokenEntries({entries, currentToken, entryPathConfig}) {
58
+ const entryPath = entryPathConfig || {};
59
+ const graceTtlMs = computeTokenTtlMs(entryPath.graceMin);
60
+ const rotateTtlMs = Math.max(60_000, Number(entryPath.rotateMin || 0) * 60_000);
61
+ const normalizedCurrentToken = String(currentToken || '').trim();
62
+ let currentTokenCreatedAt = null;
63
+ const tokenEntries = Array.from(entries || [], ([token, createdAt]) => {
64
+ if (token === normalizedCurrentToken) {
65
+ currentTokenCreatedAt = createdAt;
66
+ return null;
67
+ }
68
+
69
+ const createdAtMs = Math.floor(Number(createdAt));
70
+
71
+ return {
72
+ token: maskToken(token),
73
+ isCurrent: false,
74
+ createdAt: Number.isFinite(createdAtMs) ? new Date(createdAtMs).toISOString() : null,
75
+ expiresAt: Number.isFinite(createdAtMs) ? new Date(createdAtMs + graceTtlMs).toISOString() : null,
76
+ };
77
+ }).filter(Boolean);
78
+
79
+ if (!normalizedCurrentToken) {
80
+ return tokenEntries;
81
+ }
82
+
83
+ const currentTokenCreatedAtMs = Math.floor(Number(currentTokenCreatedAt));
84
+ const currentTokenExpiresAtMs = Number.isFinite(currentTokenCreatedAtMs)
85
+ ? currentTokenCreatedAtMs + Math.max(graceTtlMs, rotateTtlMs)
86
+ : NaN;
87
+
88
+ tokenEntries.push({
89
+ token: maskToken(normalizedCurrentToken),
90
+ isCurrent: true,
91
+ createdAt: Number.isFinite(currentTokenCreatedAtMs) ? new Date(currentTokenCreatedAtMs).toISOString() : null,
92
+ expiresAt: Number.isFinite(currentTokenExpiresAtMs) ? new Date(currentTokenExpiresAtMs).toISOString() : null,
93
+ });
94
+
95
+ return tokenEntries;
96
+ }
97
+
98
+ export function createServerInfoRouter(services) {
99
+ const router = express.Router();
100
+
101
+ router.get('/', (_req, res) => {
102
+ res.sendFile(path.join(publicDir, 'server-info.html'));
103
+ });
104
+
105
+ router.get('/data', async (_req, res) => {
106
+ const clients = getConnectedClients(services.getServer().io);
107
+ const rawConfig = services.getConfig();
108
+ const rawSystemConfig = services.getSystemConfig();
109
+ const config = redactSecrets(rawConfig);
110
+ const sysConfig = redactSecrets(rawSystemConfig);
111
+ const logs = getRecentLogs(250);
112
+ const version = readPackageVersion(packageJsonPath);
113
+ const tasks = services.getTaskManager().getTasksSnapshot();
114
+ const tokenEntries = services.getPersistence().entryTokenDao.loadEntryTokens();
115
+ const restarts = services.getPersistence().restartLogDao.listRecentRestartRecords(20);
116
+ const currentToken = services.getTokenManager().getToken();
117
+ const entryPathConfig = services.getSystemConfig().entryPath;
118
+ const daemon = await services.getApplicationDaemonService().getInfo();
119
+ const system = await services.getSystem().getInfo();
120
+ const tokens = buildTokenEntries({
121
+ entries: tokenEntries,
122
+ currentToken,
123
+ entryPathConfig,
124
+ });
125
+
126
+ res.json({
127
+ version,
128
+ now: new Date().toISOString(),
129
+ onlineSince: new Date(services.getServer().serverStartedAt).toISOString(),
130
+ uptimeSec: Math.floor(process.uptime()),
131
+ clientsConnected: clients.length,
132
+ clients,
133
+ tasks,
134
+ tokens,
135
+ daemon,
136
+ system,
137
+ restarts,
138
+ config,
139
+ sysConfig,
140
+ logs,
141
+ });
142
+ });
143
+ return router;
144
+ }
145
+
146
+ export const __testables = {
147
+ buildTokenEntries,
148
+ redactSecrets,
149
+ };
@@ -0,0 +1,93 @@
1
+ import express from 'express';
2
+ import {sendUnauthorizedResponse} from './unauthorized-response.js';
3
+ import {
4
+ PUBSUB_EVENT_SESSION_CREATED,
5
+ PUBSUB_SERVICE_SESSION
6
+ } from '../../services/pubsub/serviceEventConstants.js';
7
+
8
+ function isLocalAddress(value) {
9
+ const address = String(value || '').toLowerCase();
10
+ return (
11
+ address === '127.0.0.1'
12
+ || address === '::1'
13
+ || address === '::ffff:127.0.0.1'
14
+ );
15
+ }
16
+
17
+ function getForwardedFor(req) {
18
+ const raw = String(req.headers?.['x-forwarded-for'] || '').split(',')[0].trim();
19
+ return raw || null;
20
+ }
21
+
22
+ export const createSessionCreationMiddleware = ({
23
+ cookieName,
24
+ cookieMaxAgeMs,
25
+ secureCookies = true,
26
+ }) => (req, res, next) => {
27
+ res.createSession = (token) => {
28
+ res.cookie(cookieName, token,
29
+ {
30
+ signed: true,
31
+ httpOnly: true,
32
+ secure: secureCookies,
33
+ sameSite: 'lax',
34
+ maxAge: cookieMaxAgeMs,
35
+ path: '/',
36
+ });
37
+ }
38
+ next();
39
+ }
40
+
41
+ export function createSessionGuard(services, {
42
+ onUnauthorized = sendUnauthorizedResponse,
43
+ } = {}) {
44
+ return (req, res, next) => {
45
+ const tokenManager = services.getTokenManager();
46
+ const cookieName = services.getSystemConfig().session.cookieName;
47
+ const forwarded = getForwardedFor(req);
48
+ const clientIp = forwarded || req.ip || req.socket?.remoteAddress;
49
+ const allowBypass = isLocalAddress(clientIp);
50
+
51
+ const token = req.signedCookies && req.signedCookies[cookieName];
52
+ if (!allowBypass && !tokenManager.isValid(token)) {
53
+ onUnauthorized(req, res);
54
+ return;
55
+ }
56
+ req.sessionToken = token;
57
+ next();
58
+ }
59
+ }
60
+
61
+ export function createSessionRouter(services) {
62
+ const router = express.Router();
63
+
64
+ router.get('/:token', (req, res) => {
65
+ const tokenManager = services.getTokenManager();
66
+ const token = req.params.token;
67
+ if (!tokenManager.isValid(token)) {
68
+ sendUnauthorizedResponse(req, res);
69
+ return;
70
+ }
71
+ const forwarded = getForwardedFor(req);
72
+ const clientIp = forwarded || req.ip || req.socket?.remoteAddress || '';
73
+ services.getEvents().publishEvent(PUBSUB_SERVICE_SESSION, PUBSUB_EVENT_SESSION_CREATED, {
74
+ address: String(clientIp || '').trim(),
75
+ token,
76
+ });
77
+ res.createSession(token);
78
+ res.redirect('/');
79
+ });
80
+
81
+ // router.post('/:token', (req, res, next) => {
82
+ // req.signedCookies = {
83
+ // ...req.signedCookies,
84
+ // [services.getSystemConfig().session.cookieName]: req.params.token,
85
+ // };
86
+ // next();
87
+ // }, postSessionGuard, (req, res) => {
88
+ // res.createSession(req.params.token);
89
+ // res.status(204).end();
90
+ // });
91
+
92
+ return router;
93
+ }
@@ -0,0 +1,73 @@
1
+ function getUnauthorizedPageHtml() {
2
+ return `<!doctype html>
3
+ <html lang="fr">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <title>Acces expire</title>
8
+ <style>
9
+ :root {
10
+ color-scheme: dark;
11
+ --bg: #101417;
12
+ --panel: rgba(19, 27, 33, 0.92);
13
+ --fg: #f0f4f7;
14
+ --muted: rgba(240, 244, 247, 0.78);
15
+ --accent: #4bd4ff;
16
+ --danger: #ff8f8f;
17
+ }
18
+ * { box-sizing: border-box; }
19
+ body {
20
+ margin: 0;
21
+ min-height: 100vh;
22
+ display: grid;
23
+ place-items: center;
24
+ padding: 24px;
25
+ background: radial-gradient(circle at 20% 10%, #1f2e3a, var(--bg));
26
+ color: var(--fg);
27
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
28
+ }
29
+ main {
30
+ width: min(100%, 460px);
31
+ padding: 28px 24px;
32
+ border-radius: 18px;
33
+ border: 1px solid rgba(255, 255, 255, 0.14);
34
+ background: var(--panel);
35
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
36
+ }
37
+ .eyebrow {
38
+ margin: 0 0 12px;
39
+ color: var(--danger);
40
+ font-size: 0.82rem;
41
+ font-weight: 700;
42
+ letter-spacing: 0.08em;
43
+ text-transform: uppercase;
44
+ }
45
+ h1 {
46
+ margin: 0 0 10px;
47
+ font-size: clamp(1.6rem, 5vw, 2rem);
48
+ line-height: 1.1;
49
+ }
50
+ p {
51
+ margin: 0 0 12px;
52
+ color: var(--muted);
53
+ line-height: 1.5;
54
+ }
55
+ </style>
56
+ </head>
57
+ <body>
58
+ <main>
59
+ <h1>Connexion expiree</h1>
60
+ <p>Rescannez le code QR du serveur.</p>
61
+ </main>
62
+ </body>
63
+ </html>`;
64
+ }
65
+
66
+ export function sendUnauthorizedResponse(req, res) {
67
+ if (typeof req?.accepts === 'function' && req.accepts('html')) {
68
+ res.status(401).type('text/html').send(getUnauthorizedPageHtml());
69
+ return;
70
+ }
71
+
72
+ res.status(401).type('text/plain').send('Unauthorized');
73
+ }
@@ -0,0 +1,23 @@
1
+ import {createLogger} from "../../application/logger.js";
2
+ import {
3
+ PUBSUB_EVENT_SOCKET_CLIENT_DISCONNECTED,
4
+ PUBSUB_SERVICE_SOCKET
5
+ } from "../../services/pubsub/serviceEventConstants.js";
6
+
7
+ let log;
8
+ function getModuleLog() {
9
+ log ??= createLogger('socket');
10
+ return log;
11
+ }
12
+
13
+ export function createConnectionRegistrar({events}) {
14
+ const log = getModuleLog();
15
+ return (socket) => {
16
+ socket.on('disconnect', () => {
17
+ log.info({socketId: socket.id}, 'Client déconnecté');
18
+ events.publishEvent(PUBSUB_SERVICE_SOCKET, PUBSUB_EVENT_SOCKET_CLIENT_DISCONNECTED, {
19
+ clientId: socket.id,
20
+ });
21
+ })
22
+ }
23
+ }
@@ -0,0 +1,49 @@
1
+ function isLocalAddress(value) {
2
+ const address = String(value || '').toLowerCase();
3
+ return (
4
+ address === '127.0.0.1'
5
+ || address === '::1'
6
+ || address === '::ffff:127.0.0.1'
7
+ );
8
+ }
9
+
10
+ function getForwardedFor(request) {
11
+ const raw = String(request?.headers?.['x-forwarded-for'] || '').split(',')[0].trim();
12
+ return raw || null;
13
+ }
14
+
15
+ export function createSocketSessionAuthMiddleware(services) {
16
+ function createUnauthorizedError() {
17
+ const error = new Error('unauthorized');
18
+ error.data = {
19
+ code: 'ENTRY_TOKEN_INVALID',
20
+ message: 'Rescannez le code QR du serveur.',
21
+ };
22
+ return error;
23
+ }
24
+
25
+ function authorizeSocket(socket, next) {
26
+ const forwarded = getForwardedFor(socket.request);
27
+ const remoteAddress = forwarded || socket.request?.socket?.remoteAddress;
28
+ if (isLocalAddress(remoteAddress)) {
29
+ next();
30
+ return;
31
+ }
32
+
33
+ const systemConfig = services.getSystemConfig();
34
+ const tokenManager = services.getTokenManager();
35
+ const cookieName = systemConfig.session.cookieName;
36
+
37
+ const token = socket.request?.signedCookies?.[cookieName];
38
+ if (!tokenManager?.isValid?.(token)) {
39
+ next(createUnauthorizedError());
40
+ return;
41
+ }
42
+
43
+ socket.sessionToken = token;
44
+ next();
45
+ }
46
+
47
+
48
+ return authorizeSocket;
49
+ }
@@ -0,0 +1,12 @@
1
+ import { REMOTE_EVENT_ADMIN_RESULT } from '../../../utils/remoteCommands.js';
2
+
3
+ export function createSocketActionResponder({ socket, eventName = REMOTE_EVENT_ADMIN_RESULT }) {
4
+ return function respondAction(action, result = {}) {
5
+ socket.emit(eventName, {
6
+ action,
7
+ ok: Boolean(result.ok),
8
+ message: result.message,
9
+ openUrl: result.openUrl,
10
+ });
11
+ };
12
+ }
@@ -0,0 +1,51 @@
1
+ import {createLogger} from '../../application/logger.js';
2
+
3
+ let log;
4
+ function getModuleLog() {
5
+ log ??= createLogger('socket:timestamp');
6
+ return log;
7
+ }
8
+
9
+ export function socketTimestampGuardMiddleware({
10
+ maxEventAgeMs = 1200,
11
+ maxClockSkewMs = 5 * 60 * 1000,
12
+ socketId,
13
+ } = {}) {
14
+ const log = getModuleLog();
15
+ let observedClockOffsetMs = null;
16
+
17
+ return function enforceSocketEventTimestamp(packet, next) {
18
+ const [, payload] = packet;
19
+ const ts = payload && typeof payload === 'object'
20
+ ? Number(payload.ts)
21
+ : NaN;
22
+
23
+ if (!Number.isFinite(ts)) {
24
+ log.warn({ socketId, event: packet[0] }, 'Message socket sans timestamp');
25
+ next(new Error('missing_timestamp'));
26
+ return;
27
+ }
28
+
29
+ const rawDeltaMs = Date.now() - ts;
30
+ if (observedClockOffsetMs == null && Math.abs(rawDeltaMs) <= maxClockSkewMs) {
31
+ observedClockOffsetMs = rawDeltaMs;
32
+ log.info({ socketId, event: packet[0], observedClockOffsetMs }, 'Calibration horloge socket');
33
+ }
34
+
35
+ const ageMs = rawDeltaMs - (observedClockOffsetMs || 0);
36
+ if (ageMs > maxEventAgeMs) {
37
+ log.warn({
38
+ socketId,
39
+ event: packet[0],
40
+ ageMs,
41
+ rawDeltaMs,
42
+ observedClockOffsetMs,
43
+ maxEventAgeMs,
44
+ }, 'Message socket expiré');
45
+ next(new Error('stale_event'));
46
+ return;
47
+ }
48
+
49
+ next();
50
+ };
51
+ }
@@ -0,0 +1,12 @@
1
+ import {createServicesRegistry} from './services/createServicesRegistry.js';
2
+ import {
3
+ bootstrapLogger,
4
+ } from './application/logger.js';
5
+ import {createApplicationLifecycle} from './application/createApplicationLifecycle.js';
6
+
7
+ export async function startServer() {
8
+ const services = await createServicesRegistry();
9
+ bootstrapLogger();
10
+ const lifecycle = createApplicationLifecycle(services);
11
+ return lifecycle.start();
12
+ }
@@ -0,0 +1,87 @@
1
+ import {createStaticShareRouter} from '../connection/api/client.router.js';
2
+ import path from 'node:path';
3
+ import {
4
+ clientDir,
5
+ projectRoot,
6
+ publicDir,
7
+ sharedUtilsDir
8
+ } from '../utils/paths.js';
9
+ import {
10
+ createSessionCreationMiddleware,
11
+ createSessionGuard,
12
+ createSessionRouter,
13
+ } from '../connection/api/session.middleware.js';
14
+ import {createQrPageHandler} from '../connection/api/qr-page.handler.js';
15
+ import {createAdminUiRouter} from "./createAdminUiRouter.js";
16
+ import {createAdminApiRouter} from "./createAdminApiRouter.js";
17
+ import { createRemotesRouter } from '../connection/api/remotes.router.js';
18
+ import {readPackageVersion} from '../utils/env.js';
19
+ import {createLogger} from '../application/logger.js';
20
+
21
+ const packageJsonPath = path.join(projectRoot, 'package.json');
22
+
23
+ export function bootstrapApi(services) {
24
+ const {
25
+ getTokenManager,
26
+ getSystemConfig,
27
+ getServer,
28
+ } = services;
29
+
30
+ const {
31
+ app,
32
+ cookieParser
33
+ } = getServer();
34
+
35
+ const systemConfig = getSystemConfig();
36
+ const log = createLogger('createApp');
37
+
38
+ log.debug({
39
+ httpsEnabled: Boolean(systemConfig.https.enabled),
40
+ cookieName: systemConfig.session.cookieName,
41
+ entryPathEnabled: Boolean(systemConfig.entryPath.enabled),
42
+ }, 'Initialisation API Express');
43
+
44
+ if (!systemConfig.https.enabled) {
45
+ log.warn('HTTPS=false: cookie session envoyé sans attribut Secure (moins sécuritaire).');
46
+ }
47
+ app.set('trust proxy', 'loopback');
48
+
49
+ app.use(cookieParser);
50
+
51
+ app.use(createSessionCreationMiddleware({
52
+ cookieName: systemConfig.session.cookieName,
53
+ cookieMaxAgeMs: Math.max(1, systemConfig.session.cookieMaxAgeDays) * 24 * 60 * 60 * 1000,
54
+ secureCookies: systemConfig.https.enabled,
55
+ }));
56
+
57
+ app.use('/api/sessions', createSessionRouter(services));
58
+
59
+
60
+ app.use(createSessionGuard(services));
61
+
62
+ app.get('/', (req, res, next) => {
63
+ if (req.sessionToken) {
64
+ res.createSession(getTokenManager().getToken());
65
+ }
66
+ next();
67
+ });
68
+
69
+ app.use(createStaticShareRouter({
70
+ publicDir,
71
+ clientDir,
72
+ sharedUtilsDir
73
+ }));
74
+
75
+ app.get('/qr', createQrPageHandler(services));
76
+ app.use('/api/remotes', createRemotesRouter(services));
77
+ app.use('/api/admin', createAdminApiRouter(services));
78
+ app.use('/ui/admin', createAdminUiRouter(services));
79
+ log.trace('Routes API enregistrees');
80
+
81
+ app.get('/health', (_req, res) => {
82
+ res.json({
83
+ ok: true,
84
+ version: readPackageVersion(packageJsonPath),
85
+ });
86
+ });
87
+ }
@@ -0,0 +1,79 @@
1
+ import {createSocketSessionAuthMiddleware} from '../connection/socket/createSocketSessionAuthMiddleware.js';
2
+ import {createSocketActionRegistrars} from './createSocketActionRegistrars.js';
3
+ import {socketTimestampGuardMiddleware} from "../connection/socket/socket.timestamp-middleware.js";
4
+ import { hasRecentRestart } from '../remotes/admin/notifyIfRestarted.js';
5
+ import {
6
+ PUBSUB_EVENT_SOCKET_CLIENT_CONNECTED,
7
+ PUBSUB_SERVICE_SOCKET
8
+ } from "../services/pubsub/serviceEventConstants.js";
9
+ import { REMOTE_EVENT_SYSTEM_RELOAD } from '../../utils/remoteCommands.js';
10
+ import {createLogger} from '../application/logger.js';
11
+
12
+ function broadcast(...functions) {
13
+ return (...args) => functions.flatMap(f => f).map(f => f(...args));
14
+ }
15
+
16
+ function createNotificationHandler(services) {
17
+ const events = services.getEvents();
18
+ const log = createLogger('socket:timestamp');
19
+
20
+ return socket => {
21
+ log.info({socketId: socket.id}, 'Client connecté');
22
+ events.publishEvent(PUBSUB_SERVICE_SOCKET, PUBSUB_EVENT_SOCKET_CLIENT_CONNECTED, {
23
+ clientId: socket.id,
24
+ });
25
+ if (hasRecentRestart()) {
26
+ socket.emit(REMOTE_EVENT_SYSTEM_RELOAD, {
27
+ reason: 'service-restarted',
28
+ });
29
+ }
30
+ }
31
+ }
32
+
33
+ function createSocketGuardMiddleware(services) {
34
+ const {
35
+ getSystemConfig,
36
+ } = services;
37
+
38
+ const maxEventAgeMs = getSystemConfig().session.socketEventMaxAgeMs;
39
+
40
+ return (socket, next) => {
41
+ socket.use(socketTimestampGuardMiddleware({
42
+ maxEventAgeMs,
43
+ socketId: socket.id,
44
+ }));
45
+
46
+ // ... other guards here
47
+
48
+ next();
49
+ }
50
+ }
51
+
52
+ function createActionHandlers(services) {
53
+ const socketActionRegistrars = createSocketActionRegistrars(services);
54
+ return socket => {
55
+ for (let register of socketActionRegistrars) {
56
+ register(socket);
57
+ }
58
+ };
59
+ }
60
+
61
+ export function bootstrapSocket(services) {
62
+ const {getServer} = services;
63
+ const log = createLogger('socket:bootstrap');
64
+
65
+ const {io, cookieParser} = getServer();
66
+ log.debug('Initialisation Socket.IO');
67
+
68
+ io.engine.use((...args) => cookieParser(...args));
69
+
70
+ io.use(createSocketSessionAuthMiddleware(services));
71
+ io.use(createSocketGuardMiddleware(services));
72
+ log.trace('Middlewares Socket.IO enregistres');
73
+
74
+ io.on('connection', broadcast(
75
+ createNotificationHandler(services),
76
+ createActionHandlers(services)
77
+ ));
78
+ log.debug('Handlers Socket.IO enregistres');
79
+ }
@@ -0,0 +1,22 @@
1
+ import express from "express";
2
+ import {createServerInfoRouter} from "../connection/api/server-info.router.js";
3
+ import {createAdminConfigsRouter} from "../connection/api/admin-configs.router.js";
4
+ import {createAdminConfigActionsRouter} from "../connection/api/admin-actions.router.js";
5
+ import {createAdminSubsRouter} from "../connection/api/admin-subs.router.js";
6
+ import {createAdminRemotesRouter} from '../connection/api/admin-remotes.router.js';
7
+
8
+ export function createAdminApiRouter(services) {
9
+ const router = express.Router();
10
+
11
+ router.use('/server-info', createServerInfoRouter(services));
12
+
13
+ router.use('/configs', createAdminConfigsRouter(services));
14
+
15
+ router.use('/remotes', createAdminRemotesRouter(services));
16
+
17
+ router.use('/subs', createAdminSubsRouter(services));
18
+
19
+ router.use('/', createAdminConfigActionsRouter(services));
20
+
21
+ return router;
22
+ }
@@ -0,0 +1,21 @@
1
+ import express from "express";
2
+ import path from "node:path";
3
+ import {publicDir} from "../utils/paths.js";
4
+
5
+ export function createAdminUiRouter(_services) {
6
+ const router = express.Router();
7
+
8
+ router.get('/config', (_req, res) => {
9
+ res.sendFile(path.join(publicDir, 'admin-config.html'));
10
+ });
11
+
12
+ router.get('/server-info', (_req, res) => {
13
+ res.sendFile(path.join(publicDir, 'server-info.html'));
14
+ });
15
+
16
+ router.get('/preferences', (_req, res) => {
17
+ res.sendFile(path.join(publicDir, 'preferences.html'));
18
+ });
19
+
20
+ return router;
21
+ }