@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.
- package/.artifacts/screenshots/remote-mobile-config.png +0 -0
- package/.artifacts/screenshots/remote-mobile-home.png +0 -0
- package/.artifacts/screenshots/remote-mobile-preferences.png +0 -0
- package/.artifacts/screenshots/remote-mobile-server-info.png +0 -0
- package/.artifacts/screenshots/remote-mobile-vlc.png +0 -0
- package/.env.example +93 -0
- package/README.md +370 -0
- package/bin/remote-mouse.js +19 -0
- package/client/core/dom.js +148 -0
- package/client/core/init-socket-io.js +117 -0
- package/client/core/socket-emit.js +6 -0
- package/client/core/window-events.js +23 -0
- package/client/i18n/apply-page-translations.js +18 -0
- package/client/i18n/constants.js +3 -0
- package/client/i18n/core.js +40 -0
- package/client/i18n/locale-registry.js +77 -0
- package/client/i18n/locales/de.js +227 -0
- package/client/i18n/locales/en.js +389 -0
- package/client/i18n/locales/es.js +228 -0
- package/client/i18n/locales/fr.js +389 -0
- package/client/i18n/locales/it.js +227 -0
- package/client/i18n/locales/ja.js +227 -0
- package/client/i18n/locales/pt.js +227 -0
- package/client/i18n/locales/ru.js +240 -0
- package/client/i18n/locales/zh.js +227 -0
- package/client/preferences/constants.js +15 -0
- package/client/preferences/effects.js +24 -0
- package/client/preferences/load.js +19 -0
- package/client/preferences/render.js +26 -0
- package/client/preferences/rows.js +78 -0
- package/client/preferences/state.js +29 -0
- package/client/preview/create-preview-frame-receiver.js +13 -0
- package/client/preview/draw.js +77 -0
- package/client/preview/parse-preview-frame.js +39 -0
- package/client/preview/preview-stream.js +152 -0
- package/client/services/app-state/appStateKeys.js +30 -0
- package/client/services/app-state/createAppStateService.js +216 -0
- package/client/services/app-state/createPersistStoreService.js +122 -0
- package/client/services/app-state/createStateStoreService.js +64 -0
- package/client/services/app-state/localStorageBinding.js +26 -0
- package/client/services/app-state/normalizers.js +74 -0
- package/client/services/app-state/resolveEffectiveStateValue.js +58 -0
- package/client/services/app-state/stateDefinitions.js +107 -0
- package/client/services/backend/createBackendService.js +55 -0
- package/client/services/config/ConfigView.js +45 -0
- package/client/services/config/createClientConfigService.js +122 -0
- package/client/services/config/defaultClientConfig.js +22 -0
- package/client/services/createServicesContainer.js +48 -0
- package/client/services/createServicesRegistry.js +26 -0
- package/client/services/i18n/createI18nService.js +86 -0
- package/client/services/notifications/createNotificationService.js +56 -0
- package/client/services/pubsub/createPubSubService.js +20 -0
- package/client/services/remotes/createRemotesService.js +20 -0
- package/client/services/transport/createSocketIoTransportService.js +50 -0
- package/client/touch/bindTouchPassthrough.js +94 -0
- package/client/touch/createTouchList.js +18 -0
- package/client/touch/dispatchSyntheticTouch.js +20 -0
- package/client/touch/gesture-zone.js +28 -0
- package/client/touch/throttle.js +46 -0
- package/client/touch/touch-handlers.js +193 -0
- package/client/touch/touchpad.js +231 -0
- package/client/ui/config/page.js +840 -0
- package/client/ui/main/applyRemoteVisibilityState.js +60 -0
- package/client/ui/main/bind-client-notifications.js +3 -0
- package/client/ui/main/bindings/bindAccordion.js +121 -0
- package/client/ui/main/bindings/bindActionButtons.js +13 -0
- package/client/ui/main/bindings/bindAdminDrawer.js +137 -0
- package/client/ui/main/bindings/bindAdminRemoteButtons.js +77 -0
- package/client/ui/main/bindings/bindAdminVersion.js +27 -0
- package/client/ui/main/bindings/bindBrowserRemoteButtons.js +119 -0
- package/client/ui/main/bindings/bindRemoteAccordion.js +23 -0
- package/client/ui/main/bindings/bindSamsungRemoteButtons.js +320 -0
- package/client/ui/main/bindings/bindSystemRemoteButtons.js +41 -0
- package/client/ui/main/bindings/bindVlcRemoteButtons.js +64 -0
- package/client/ui/main/canvas-ui.js +33 -0
- package/client/ui/main/connection-overlay.js +57 -0
- package/client/ui/main/init-ui.js +54 -0
- package/client/ui/main/keyboard-panel.js +271 -0
- package/client/ui/main/mouse-buttons.js +94 -0
- package/client/ui/main/page.js +21 -0
- package/client/ui/notifications/show-toast.js +48 -0
- package/client/ui/preferences/bindPreferenceSwitchers.js +49 -0
- package/client/ui/preferences/page.js +61 -0
- package/client/ui/preferences/switchers.js +200 -0
- package/index.js +9 -0
- package/package.json +41 -0
- package/public/admin-config.css +447 -0
- package/public/admin-config.html +63 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/brave-icon.svg +13 -0
- package/public/favicon.svg +30 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/index.html +246 -0
- package/public/manifest.webmanifest +45 -0
- package/public/preferences.html +55 -0
- package/public/server-info.html +574 -0
- package/public/styles.css +1042 -0
- package/public/sw.js +103 -0
- package/scripts/codex-commit.sh +112 -0
- package/scripts/generate-mobile-screenshots.mjs +244 -0
- package/scripts/install-linux.sh +633 -0
- package/scripts/install-windows.ps1 +369 -0
- package/server/application/createApplicationLifecycle.js +15 -0
- package/server/application/logger.js +216 -0
- package/server/application/shutdown.js +81 -0
- package/server/application/start.js +95 -0
- package/server/application/state.js +19 -0
- package/server/connection/api/admin-actions.router.js +65 -0
- package/server/connection/api/admin-configs.router.js +165 -0
- package/server/connection/api/admin-remotes.router.js +61 -0
- package/server/connection/api/admin-subs.router.js +49 -0
- package/server/connection/api/client.router.js +15 -0
- package/server/connection/api/configs.js +96 -0
- package/server/connection/api/qr-page.handler.js +17 -0
- package/server/connection/api/remotes.router.js +30 -0
- package/server/connection/api/server-info.router.js +149 -0
- package/server/connection/api/session.middleware.js +93 -0
- package/server/connection/api/unauthorized-response.js +73 -0
- package/server/connection/socket/createConnectionRegistrar.js +23 -0
- package/server/connection/socket/createSocketSessionAuthMiddleware.js +49 -0
- package/server/connection/socket/socket-action-responder.js +12 -0
- package/server/connection/socket/socket.timestamp-middleware.js +51 -0
- package/server/index.js +12 -0
- package/server/init/bootstrapApi.js +87 -0
- package/server/init/bootstrapSocket.js +79 -0
- package/server/init/createAdminApiRouter.js +22 -0
- package/server/init/createAdminUiRouter.js +21 -0
- package/server/init/createSocketActionRegistrars.js +52 -0
- package/server/init/observers/startConfigObserver.js +51 -0
- package/server/init/observers/startDisplaySizeObserver.js +55 -0
- package/server/init/observers/startNotificationObserver.js +49 -0
- package/server/init/observers/startQrOverlayHoverObserver.js +89 -0
- package/server/init/observers/startQrOverlayRefreshObserver.js +53 -0
- package/server/os/darwin/app.js +45 -0
- package/server/os/darwin/applescript.js +51 -0
- package/server/os/darwin/cliSocket.js +53 -0
- package/server/os/darwin/url.js +9 -0
- package/server/os/darwin/window.js +10 -0
- package/server/os/linux/app.js +120 -0
- package/server/os/linux/cliSocket.js +53 -0
- package/server/os/linux/process.js +69 -0
- package/server/os/linux/screen.js +58 -0
- package/server/os/linux/systemd.js +139 -0
- package/server/os/linux/url.js +17 -0
- package/server/os/linux/windows.js +144 -0
- package/server/os/win32/app.js +66 -0
- package/server/os/win32/cliSocket.js +15 -0
- package/server/os/win32/powershell.js +70 -0
- package/server/os/win32/process.js +20 -0
- package/server/os/win32/service.js +95 -0
- package/server/os/win32/url.js +9 -0
- package/server/os/win32/window.js +10 -0
- package/server/remotes/admin/createAdminEventGuardMiddleware.js +29 -0
- package/server/remotes/admin/createForceUpdateCheckAction.js +39 -0
- package/server/remotes/admin/createInstallUpdateAction.js +64 -0
- package/server/remotes/admin/createOpenQrBrowserAction.js +63 -0
- package/server/remotes/admin/createOpenServerInfoBrowserAction.js +58 -0
- package/server/remotes/admin/createRestartServiceAction.js +20 -0
- package/server/remotes/admin/createRotateEntryTokenAction.js +43 -0
- package/server/remotes/admin/createToggleQrOverlayAction.js +38 -0
- package/server/remotes/admin/index.js +32 -0
- package/server/remotes/admin/notifyIfRestarted.js +102 -0
- package/server/remotes/admin/registrar.js +94 -0
- package/server/remotes/browser/actions/createFocusOrLaunchBrowserAction.js +22 -0
- package/server/remotes/browser/actions/openUrlOnHost.js +3 -0
- package/server/remotes/browser/browserCatalog.js +70 -0
- package/server/remotes/browser/index.js +15 -0
- package/server/remotes/browser/listBrowsersOnHost.js +23 -0
- package/server/remotes/browser/registrar.js +29 -0
- package/server/remotes/browser/spec.js +18 -0
- package/server/remotes/browser/state.js +5 -0
- package/server/remotes/input/registrar.js +39 -0
- package/server/remotes/preview/bgraToRgbaBuffer.js +20 -0
- package/server/remotes/preview/captureAroundCursor.js +19 -0
- package/server/remotes/preview/createPreviewStreamer.js +85 -0
- package/server/remotes/preview/registrar.js +34 -0
- package/server/remotes/samsung/createSamsungCommandService.js +203 -0
- package/server/remotes/samsung/createSamsungRemote.js +26 -0
- package/server/remotes/samsung/createSamsungRemoteGetter.js +46 -0
- package/server/remotes/samsung/createSamsungTvGetter.js +102 -0
- package/server/remotes/samsung/device-config.js +122 -0
- package/server/remotes/samsung/registrar.js +59 -0
- package/server/remotes/samsung/utils.js +56 -0
- package/server/remotes/vlc/index.js +39 -0
- package/server/remotes/vlc/registrar.js +94 -0
- package/server/remotes/vlc/spec.js +15 -0
- package/server/remotes/vlc/state.js +5 -0
- package/server/remotes/window/index.js +6 -0
- package/server/remotes/window/registrar.js +28 -0
- package/server/services/application/createApplicationDaemonService.js +144 -0
- package/server/services/application/getDaemonServiceAdapter.js +56 -0
- package/server/services/application/utils.js +29 -0
- package/server/services/config/bootstrapConfig.js +38 -0
- package/server/services/config/configPaths.js +42 -0
- package/server/services/config/configSchema.js +297 -0
- package/server/services/config/configService.js +89 -0
- package/server/services/config/defaultConfig.js +137 -0
- package/server/services/config/envConfig.js +49 -0
- package/server/services/config/index.js +9 -0
- package/server/services/config/logConfig.js +54 -0
- package/server/services/config/system-config.js +44 -0
- package/server/services/createServicesContainer.js +94 -0
- package/server/services/createServicesRegistry.js +48 -0
- package/server/services/input/createInputController.js +17 -0
- package/server/services/input/createKeyboardController.js +146 -0
- package/server/services/input/createMouseController.js +182 -0
- package/server/services/notifier/createClientNotifier.js +23 -0
- package/server/services/notifier/createHostNotifier.js +41 -0
- package/server/services/notifier/createNotifier.js +13 -0
- package/server/services/notifier/createNotifierComposite.js +122 -0
- package/server/services/notifier/handleOpenQrBrowserEvent.js +40 -0
- package/server/services/notifier/handleOpenServerInfoBrowserEvent.js +40 -0
- package/server/services/notifier/handleRestartServiceEvent.js +37 -0
- package/server/services/notifier/handleRotateEntryTokenEvent.js +42 -0
- package/server/services/notifier/handleSessionEvent.js +24 -0
- package/server/services/notifier/handleToggleQrOverlayEvent.js +21 -0
- package/server/services/notifier/host-notifier/createLinuxHostNotifier.js +23 -0
- package/server/services/notifier/host-notifier/createNodeNotifierHostNotifier.js +24 -0
- package/server/services/notifier/host-notifier/index.js +11 -0
- package/server/services/notifier/mapper/handleForceUpdateCheckEvent.js +24 -0
- package/server/services/notifier/mapper/handleInstallUpdateEvent.js +53 -0
- package/server/services/notifier/mapper/handleSocketEvent.js +40 -0
- package/server/services/notifier/mapper/handleUpdateManagerEvent.js +24 -0
- package/server/services/notifier/mapper/notify.js +7 -0
- package/server/services/os/index.js +23 -0
- package/server/services/os/platforms/createFallbackAdapter.js +31 -0
- package/server/services/os/platforms/darwin/app.js +6 -0
- package/server/services/os/platforms/darwin/index.js +21 -0
- package/server/services/os/platforms/darwin/scripts.js +4 -0
- package/server/services/os/platforms/darwin/url.js +1 -0
- package/server/services/os/platforms/darwin/window.js +4 -0
- package/server/services/os/platforms/linux/app.js +6 -0
- package/server/services/os/platforms/linux/index.js +33 -0
- package/server/services/os/platforms/linux/url.js +1 -0
- package/server/services/os/platforms/linux/windows.js +11 -0
- package/server/services/os/platforms/win32/app.js +6 -0
- package/server/services/os/platforms/win32/index.js +21 -0
- package/server/services/os/platforms/win32/overlay.js +6 -0
- package/server/services/os/platforms/win32/powershell.js +4 -0
- package/server/services/os/platforms/win32/url.js +1 -0
- package/server/services/os/platforms/win32/window.js +4 -0
- package/server/services/os/shared.js +2 -0
- package/server/services/overlay/createNoopOverlay.js +13 -0
- package/server/services/overlay/createQrOverlay.js +27 -0
- package/server/services/overlay/createQrOverlayWin32.js +158 -0
- package/server/services/overlay/createQrOverlayYad.js +178 -0
- package/server/services/overlay/renderQrPage.js +82 -0
- package/server/services/persistence/createConfigDao.js +106 -0
- package/server/services/persistence/createDatabaseProvider.js +23 -0
- package/server/services/persistence/createEntryTokenDao.js +140 -0
- package/server/services/persistence/createRestartLogDao.js +210 -0
- package/server/services/persistence/index.js +19 -0
- package/server/services/pubsub/createEventStore.js +54 -0
- package/server/services/pubsub/createPubSub.js +89 -0
- package/server/services/pubsub/createServiceEvents.js +29 -0
- package/server/services/pubsub/serviceEventConstants.js +39 -0
- package/server/services/remotes/createRemotes.js +25 -0
- package/server/services/server/createHttpsServer.js +12 -0
- package/server/services/server/createServer.js +66 -0
- package/server/services/sse/createSseService.js +96 -0
- package/server/services/sse/utils.js +7 -0
- package/server/services/system/createSystemService.js +170 -0
- package/server/services/system/getNetworkInfo.js +18 -0
- package/server/services/system/getScreenInfo.js +32 -0
- package/server/services/system/listNetworkInterfaces.js +27 -0
- package/server/services/task-manager/createTaskManager.js +68 -0
- package/server/services/task-runner/createTaskRunner.js +114 -0
- package/server/services/token-manager/createTokenManager.js +244 -0
- package/server/services/token-manager/token-format.js +7 -0
- package/server/services/token-manager/token-store-utils.js +8 -0
- package/server/services/update-manager/CommandUpdateSource.js +70 -0
- package/server/services/update-manager/NpmUpdateSource.js +34 -0
- package/server/services/update-manager/buildNpmGlobalUpdateCommand.js +15 -0
- package/server/services/update-manager/chooseUpdateCheckSource.js +20 -0
- package/server/services/update-manager/chooseUpdateInstallSource.js +5 -0
- package/server/services/update-manager/createUpdateManager.js +109 -0
- package/server/services/update-manager/resolveInstallUpdateCommand.js +42 -0
- package/server/term/cli/executeLocalServiceCommand.js +42 -0
- package/server/term/cli/getCliSocketPath.js +5 -0
- package/server/term/cli/parseCliArgs.js +110 -0
- package/server/term/cli/printCliResult.js +18 -0
- package/server/term/cli/runCliCmd.js +50 -0
- package/server/term/cli/sendCliCommand.js +76 -0
- package/server/term/cliSocket.js +29 -0
- package/server/term/srv/commands/configCommand.js +84 -0
- package/server/term/srv/commands/helpCommand.js +6 -0
- package/server/term/srv/commands/infoCommand.js +7 -0
- package/server/term/srv/commands/openQrCommand.js +7 -0
- package/server/term/srv/commands/samsungDetectCommand.js +52 -0
- package/server/term/srv/commands/serviceCommand.js +25 -0
- package/server/term/srv/commands/systemConfigCommand.js +7 -0
- package/server/term/srv/commands/tasksCommand.js +24 -0
- package/server/term/srv/commands/tokensCommand.js +15 -0
- package/server/term/srv/executeCliCommand.js +44 -0
- package/server/term/srv/executeCliRequest.js +38 -0
- package/server/term/srv/startCliServer.js +140 -0
- package/server/utils/createRandomToken.js +11 -0
- package/server/utils/env.js +155 -0
- package/server/utils/git.js +13 -0
- package/server/utils/http.js +7 -0
- package/server/utils/network.js +45 -0
- package/server/utils/paths.js +10 -0
- package/server/utils/process.js +57 -0
- package/server/utils/robot.js +11 -0
- package/server/utils/semver.js +33 -0
- package/server/utils/truncateText.js +7 -0
- package/utils/createLazy.js +13 -0
- package/utils/functional.js +48 -0
- package/utils/math.js +26 -0
- package/utils/notificationSettings.js +61 -0
- package/utils/object.utils.js +79 -0
- package/utils/predicates.js +17 -0
- package/utils/remoteCommands.js +40 -0
- package/utils/sync.js +3 -0
- package/velor-remote-mouse-6.4.8.tgz +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import {createLogger} from '../../application/logger.js';
|
|
3
|
+
import {createNoopOverlay} from './createNoopOverlay.js';
|
|
4
|
+
import {createQrOverlayYad} from './createQrOverlayYad.js';
|
|
5
|
+
import {createQrOverlayWin32} from './createQrOverlayWin32.js';
|
|
6
|
+
|
|
7
|
+
let log;
|
|
8
|
+
function getModuleLog() {
|
|
9
|
+
log ??= createLogger('qr-overlay');
|
|
10
|
+
return log;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function createQrOverlay(services) {
|
|
14
|
+
const log = getModuleLog();
|
|
15
|
+
const platform = os.platform();
|
|
16
|
+
|
|
17
|
+
if (platform === 'linux') {
|
|
18
|
+
return createQrOverlayYad(services);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (platform === 'win32') {
|
|
22
|
+
return createQrOverlayWin32(services);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
log.info({ platform }, 'QR overlay non supporté sur cette plateforme');
|
|
26
|
+
return createNoopOverlay();
|
|
27
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import QRCode from 'qrcode';
|
|
5
|
+
import {DEFAULT_PERSISTED_CONFIG} from '../config/defaultConfig.js';
|
|
6
|
+
import {createLogger} from '../../application/logger.js';
|
|
7
|
+
import {createNoopOverlay} from './createNoopOverlay.js';
|
|
8
|
+
import {
|
|
9
|
+
buildQrOverlayPowerShellScript,
|
|
10
|
+
spawnPowerShellFile,
|
|
11
|
+
} from '../os/platforms/win32/overlay.js';
|
|
12
|
+
|
|
13
|
+
let log;
|
|
14
|
+
function getModuleLog() {
|
|
15
|
+
log ??= createLogger('qr-overlay:win32');
|
|
16
|
+
return log;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function createQrOverlayWin32(services) {
|
|
20
|
+
const log = getModuleLog();
|
|
21
|
+
const getUrl = () => services.getUrls().entryUrl;
|
|
22
|
+
const getConfig = () => services.getConfig();
|
|
23
|
+
|
|
24
|
+
function getOverlayContext() {
|
|
25
|
+
const config = getConfig?.() || {};
|
|
26
|
+
const qrOverlayConfig = {
|
|
27
|
+
...DEFAULT_PERSISTED_CONFIG.qrOverlay,
|
|
28
|
+
...config.qrOverlay,
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
qrOverlayConfig,
|
|
32
|
+
startsVisible: Boolean(qrOverlayConfig.enabled),
|
|
33
|
+
isSupported: os.platform() === 'win32',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!getOverlayContext().isSupported) {
|
|
38
|
+
return createNoopOverlay();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const qrPath = path.join(os.tmpdir(), 'remote-mouse-qr-overlay.png');
|
|
42
|
+
const scriptPath = path.join(os.tmpdir(), 'remote-mouse-qr-overlay.ps1');
|
|
43
|
+
let child = null;
|
|
44
|
+
let refreshChain = Promise.resolve();
|
|
45
|
+
let visible = getOverlayContext().startsVisible;
|
|
46
|
+
let suppressed = false;
|
|
47
|
+
let overlayBounds = null;
|
|
48
|
+
|
|
49
|
+
const close = () => {
|
|
50
|
+
if (child && !child.killed) {
|
|
51
|
+
child.kill('SIGTERM');
|
|
52
|
+
}
|
|
53
|
+
child = null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
async function spawnOverlay() {
|
|
57
|
+
const {qrOverlayConfig, isSupported} = getOverlayContext();
|
|
58
|
+
if (!isSupported) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const size = qrOverlayConfig.size;
|
|
63
|
+
const margin = qrOverlayConfig.margin;
|
|
64
|
+
await QRCode.toFile(qrPath, getUrl(), { width: size, margin: 1 });
|
|
65
|
+
|
|
66
|
+
const screen = await services.getSystem().getScreenInfo() || {width: size + margin, height: size + margin};
|
|
67
|
+
const posX = Math.max(0, screen.width - size - margin);
|
|
68
|
+
const posY = Math.max(0, margin);
|
|
69
|
+
overlayBounds = {
|
|
70
|
+
x: posX,
|
|
71
|
+
y: posY,
|
|
72
|
+
width: size,
|
|
73
|
+
height: size,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
fs.writeFileSync(
|
|
77
|
+
scriptPath,
|
|
78
|
+
buildQrOverlayPowerShellScript({qrPath, size, posX, posY}),
|
|
79
|
+
'utf8',
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
child = spawnPowerShellFile(scriptPath);
|
|
83
|
+
child.once('error', (error) => {
|
|
84
|
+
log.warn({ err: error }, 'Impossible de lancer PowerShell pour QR overlay');
|
|
85
|
+
});
|
|
86
|
+
log.debug({ url: getUrl() }, 'QR overlay Windows rafraîchi');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const update = async () => {
|
|
90
|
+
if (!visible || suppressed || !getOverlayContext().isSupported) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const previousRefresh = refreshChain;
|
|
95
|
+
refreshChain = (async () => {
|
|
96
|
+
try {
|
|
97
|
+
await previousRefresh;
|
|
98
|
+
close();
|
|
99
|
+
await spawnOverlay();
|
|
100
|
+
} catch (_error) {}
|
|
101
|
+
})();
|
|
102
|
+
|
|
103
|
+
await refreshChain;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const hide = () => {
|
|
107
|
+
visible = false;
|
|
108
|
+
close();
|
|
109
|
+
return visible;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const show = async () => {
|
|
113
|
+
if (!getOverlayContext().isSupported) {
|
|
114
|
+
visible = false;
|
|
115
|
+
return visible;
|
|
116
|
+
}
|
|
117
|
+
visible = true;
|
|
118
|
+
await update();
|
|
119
|
+
return visible;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const setSuppressed = (nextSuppressed) => {
|
|
123
|
+
const normalized = Boolean(nextSuppressed);
|
|
124
|
+
if (suppressed === normalized) {
|
|
125
|
+
return suppressed;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
suppressed = normalized;
|
|
129
|
+
if (suppressed) {
|
|
130
|
+
close();
|
|
131
|
+
return suppressed;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
void update();
|
|
135
|
+
return suppressed;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const toggle = async () => {
|
|
139
|
+
if (visible) {
|
|
140
|
+
hide();
|
|
141
|
+
return visible;
|
|
142
|
+
}
|
|
143
|
+
await show();
|
|
144
|
+
return visible;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
close,
|
|
149
|
+
show,
|
|
150
|
+
hide,
|
|
151
|
+
update,
|
|
152
|
+
setSuppressed,
|
|
153
|
+
toggle,
|
|
154
|
+
isVisible: () => visible,
|
|
155
|
+
isSuppressed: () => suppressed,
|
|
156
|
+
getBounds: () => overlayBounds,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import QRCode from 'qrcode';
|
|
5
|
+
import {DEFAULT_PERSISTED_CONFIG} from '../config/defaultConfig.js';
|
|
6
|
+
import {createLogger} from '../../application/logger.js';
|
|
7
|
+
import {createNoopOverlay} from './createNoopOverlay.js';
|
|
8
|
+
import {commandExists} from '../../os/linux/process.js';
|
|
9
|
+
|
|
10
|
+
let log;
|
|
11
|
+
function getModuleLog() {
|
|
12
|
+
log ??= createLogger('qr-overlay:yad');
|
|
13
|
+
return log;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildQrOverlayYadArgs({qrPath, size, posX, posY}) {
|
|
17
|
+
return [
|
|
18
|
+
'--picture',
|
|
19
|
+
'--class=remote-mouse-qr-overlay',
|
|
20
|
+
'--undecorated',
|
|
21
|
+
'--skip-taskbar',
|
|
22
|
+
'--sticky',
|
|
23
|
+
'--on-top',
|
|
24
|
+
'--no-buttons',
|
|
25
|
+
'--fixed',
|
|
26
|
+
'--borders=0',
|
|
27
|
+
`--width=${size}`,
|
|
28
|
+
`--height=${size}`,
|
|
29
|
+
`--posx=${posX}`,
|
|
30
|
+
`--posy=${posY}`,
|
|
31
|
+
'--size=orig',
|
|
32
|
+
`--filename=${qrPath}`,
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function createQrOverlayYad(services) {
|
|
37
|
+
const log = getModuleLog();
|
|
38
|
+
const getUrl = () => services.getUrls().entryUrl;
|
|
39
|
+
const getConfig = () => services.getConfig();
|
|
40
|
+
const getSystemConfig = () => services.getSystemConfig();
|
|
41
|
+
|
|
42
|
+
function getOverlayContext() {
|
|
43
|
+
const config = getConfig?.() || {};
|
|
44
|
+
const systemConfig = getSystemConfig?.() || {};
|
|
45
|
+
const qrOverlayConfig = {
|
|
46
|
+
...DEFAULT_PERSISTED_CONFIG.qrOverlay,
|
|
47
|
+
...config.qrOverlay,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
qrOverlayConfig,
|
|
52
|
+
startsVisible: Boolean(qrOverlayConfig.enabled),
|
|
53
|
+
isSupported: os.platform() === 'linux'
|
|
54
|
+
&& Boolean(systemConfig.graphicalDisplay),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!getOverlayContext().isSupported) {
|
|
59
|
+
return createNoopOverlay();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const hasYad = await commandExists('yad');
|
|
63
|
+
if (!hasYad) {
|
|
64
|
+
log.warn('Overlay QR non lancé: "yad" est introuvable.');
|
|
65
|
+
return createNoopOverlay();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const qrPath = path.join(os.tmpdir(), 'remote-mouse-qr-overlay.png');
|
|
69
|
+
let child = null;
|
|
70
|
+
let refreshChain = Promise.resolve();
|
|
71
|
+
let visible = getOverlayContext().startsVisible;
|
|
72
|
+
let suppressed = false;
|
|
73
|
+
let overlayBounds = null;
|
|
74
|
+
|
|
75
|
+
const close = () => {
|
|
76
|
+
if (child && !child.killed) {
|
|
77
|
+
child.kill('SIGTERM');
|
|
78
|
+
}
|
|
79
|
+
child = null;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
async function spawnOverlay() {
|
|
83
|
+
const {qrOverlayConfig, isSupported} = getOverlayContext();
|
|
84
|
+
if (!isSupported) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const size = qrOverlayConfig.size;
|
|
89
|
+
const margin = qrOverlayConfig.margin;
|
|
90
|
+
const topBarOffset = qrOverlayConfig.topOffsetPx;
|
|
91
|
+
await QRCode.toFile(qrPath, getUrl(), { width: size, margin: 1 });
|
|
92
|
+
|
|
93
|
+
const screen = await services.getSystem().getScreenInfo() || {width: size + margin, height: size + margin};
|
|
94
|
+
const posX = Math.max(0, screen.width - size - margin);
|
|
95
|
+
const posY = Math.max(0, margin + topBarOffset);
|
|
96
|
+
overlayBounds = {
|
|
97
|
+
x: posX,
|
|
98
|
+
y: posY,
|
|
99
|
+
width: size,
|
|
100
|
+
height: size,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const args = buildQrOverlayYadArgs({qrPath, size, posX, posY});
|
|
104
|
+
|
|
105
|
+
child = spawn('yad', args, { stdio: 'ignore' });
|
|
106
|
+
log.debug({ url: getUrl() }, 'QR overlay rafraîchi');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const update = async () => {
|
|
110
|
+
if (!visible || suppressed || !getOverlayContext().isSupported) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const previousRefresh = refreshChain;
|
|
115
|
+
refreshChain = (async () => {
|
|
116
|
+
try {
|
|
117
|
+
await previousRefresh;
|
|
118
|
+
close();
|
|
119
|
+
await spawnOverlay();
|
|
120
|
+
} catch (_error) {}
|
|
121
|
+
})();
|
|
122
|
+
|
|
123
|
+
await refreshChain;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const hide = () => {
|
|
127
|
+
visible = false;
|
|
128
|
+
close();
|
|
129
|
+
return visible;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const show = async () => {
|
|
133
|
+
if (!getOverlayContext().isSupported) {
|
|
134
|
+
visible = false;
|
|
135
|
+
return visible;
|
|
136
|
+
}
|
|
137
|
+
visible = true;
|
|
138
|
+
await update();
|
|
139
|
+
return visible;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const setSuppressed = (nextSuppressed) => {
|
|
143
|
+
const normalized = Boolean(nextSuppressed);
|
|
144
|
+
if (suppressed === normalized) {
|
|
145
|
+
return suppressed;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
suppressed = normalized;
|
|
149
|
+
if (suppressed) {
|
|
150
|
+
close();
|
|
151
|
+
return suppressed;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
void update();
|
|
155
|
+
return suppressed;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const toggle = async () => {
|
|
159
|
+
if (visible) {
|
|
160
|
+
hide();
|
|
161
|
+
return visible;
|
|
162
|
+
}
|
|
163
|
+
await show();
|
|
164
|
+
return visible;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
close,
|
|
169
|
+
show,
|
|
170
|
+
hide,
|
|
171
|
+
update,
|
|
172
|
+
setSuppressed,
|
|
173
|
+
toggle,
|
|
174
|
+
isVisible: () => visible,
|
|
175
|
+
isSuppressed: () => suppressed,
|
|
176
|
+
getBounds: () => overlayBounds,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export function renderQrPage({ qrDataUrl, publicUrl }) {
|
|
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>Remote Mouse - QR</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
color-scheme: dark;
|
|
11
|
+
--bg: #101417;
|
|
12
|
+
--panel: rgba(19, 27, 33, 0.92);
|
|
13
|
+
--text: #f0f4f7;
|
|
14
|
+
--muted: rgba(240, 244, 247, 0.8);
|
|
15
|
+
--line: rgba(255,255,255,.12);
|
|
16
|
+
}
|
|
17
|
+
* { box-sizing: border-box; }
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
display: grid;
|
|
22
|
+
place-items: center;
|
|
23
|
+
padding: 20px;
|
|
24
|
+
font-family: system-ui, sans-serif;
|
|
25
|
+
background: radial-gradient(circle at 20% 10%, #1f2e3a, var(--bg));
|
|
26
|
+
color: var(--text);
|
|
27
|
+
}
|
|
28
|
+
.card {
|
|
29
|
+
width: min(100%, 860px);
|
|
30
|
+
display: grid;
|
|
31
|
+
grid-template-columns: minmax(220px, 360px) minmax(0, 1fr);
|
|
32
|
+
gap: 22px;
|
|
33
|
+
align-items: center;
|
|
34
|
+
padding: 22px;
|
|
35
|
+
background: var(--panel);
|
|
36
|
+
border: 1px solid var(--line);
|
|
37
|
+
border-radius: 20px;
|
|
38
|
+
box-shadow: 0 18px 48px rgba(0,0,0,.35);
|
|
39
|
+
}
|
|
40
|
+
.copy h1 { margin: 0 0 10px; font-size: clamp(1.4rem, 4vw, 2rem); }
|
|
41
|
+
.copy p { margin: 0 0 14px; color: var(--muted); line-height: 1.5; }
|
|
42
|
+
.qr-wrap {
|
|
43
|
+
display: grid;
|
|
44
|
+
place-items: center;
|
|
45
|
+
padding: 14px;
|
|
46
|
+
border-radius: 18px;
|
|
47
|
+
background: rgba(255,255,255,.04);
|
|
48
|
+
}
|
|
49
|
+
img { width: min(100%, 320px); height: auto; background: white; padding: 12px; border-radius: 14px; }
|
|
50
|
+
code {
|
|
51
|
+
display: block;
|
|
52
|
+
width: 100%;
|
|
53
|
+
padding: 12px 14px;
|
|
54
|
+
border-radius: 12px;
|
|
55
|
+
background: rgba(255,255,255,.04);
|
|
56
|
+
color: var(--text);
|
|
57
|
+
font-size: .92rem;
|
|
58
|
+
word-break: break-word;
|
|
59
|
+
}
|
|
60
|
+
@media (max-width: 720px) {
|
|
61
|
+
.card {
|
|
62
|
+
grid-template-columns: 1fr;
|
|
63
|
+
text-align: center;
|
|
64
|
+
padding: 18px;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
68
|
+
</head>
|
|
69
|
+
<body>
|
|
70
|
+
<div class="card">
|
|
71
|
+
<div class="qr-wrap">
|
|
72
|
+
<img src="${qrDataUrl}" alt="QR code" />
|
|
73
|
+
</div>
|
|
74
|
+
<div class="copy">
|
|
75
|
+
<h1>Scanner pour connecter</h1>
|
|
76
|
+
<p>Ouvrez cette page sur mobile ou scannez le code QR pour acceder directement a la telecommande.</p>
|
|
77
|
+
<code>${publicUrl}</code>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</body>
|
|
81
|
+
</html>`;
|
|
82
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const CONFIG_TABLE = 'config_entries';
|
|
2
|
+
|
|
3
|
+
export function createConfigDao({getDatabase} = {}) {
|
|
4
|
+
let bootstrapped = false;
|
|
5
|
+
|
|
6
|
+
function bootstrapConfigDatabase() {
|
|
7
|
+
const db = getDatabase();
|
|
8
|
+
db.exec(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS ${CONFIG_TABLE}
|
|
10
|
+
(
|
|
11
|
+
key TEXT PRIMARY KEY,
|
|
12
|
+
value_json TEXT NOT NULL
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function saveOne(key, value) {
|
|
18
|
+
const db = getDatabase();
|
|
19
|
+
if (!bootstrapped) {
|
|
20
|
+
bootstrapConfigDatabase();
|
|
21
|
+
bootstrapped = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const normalizedKey = String(key || '').trim();
|
|
25
|
+
if (!normalizedKey) {
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const statement = db.prepare(`
|
|
30
|
+
INSERT INTO ${CONFIG_TABLE} (key, value_json)
|
|
31
|
+
VALUES (?, ?)
|
|
32
|
+
ON CONFLICT(key) DO UPDATE SET value_json = excluded.value_json
|
|
33
|
+
`);
|
|
34
|
+
return statement.run(normalizedKey, JSON.stringify(value)).changes;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getOne(key) {
|
|
38
|
+
const db = getDatabase();
|
|
39
|
+
if (!bootstrapped) {
|
|
40
|
+
bootstrapConfigDatabase();
|
|
41
|
+
bootstrapped = true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const normalizedKey = String(key || '').trim();
|
|
45
|
+
if (!normalizedKey) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const row = db.prepare(`SELECT value_json FROM ${CONFIG_TABLE} WHERE key = ?`).get(normalizedKey);
|
|
50
|
+
if (!row) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(row.value_json);
|
|
56
|
+
} catch (_error) {
|
|
57
|
+
return row.value_json;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getAll() {
|
|
62
|
+
const db = getDatabase();
|
|
63
|
+
if (!bootstrapped) {
|
|
64
|
+
bootstrapConfigDatabase();
|
|
65
|
+
bootstrapped = true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const rows = db.prepare(`SELECT key, value_json FROM ${CONFIG_TABLE}`).all();
|
|
69
|
+
return rows.map((row) => {
|
|
70
|
+
let value;
|
|
71
|
+
try {
|
|
72
|
+
value = JSON.parse(row.value_json);
|
|
73
|
+
} catch (_error) {
|
|
74
|
+
value = row.value_json;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
key: row.key,
|
|
79
|
+
value,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function deleteOne(key) {
|
|
85
|
+
const db = getDatabase();
|
|
86
|
+
if (!bootstrapped) {
|
|
87
|
+
bootstrapConfigDatabase();
|
|
88
|
+
bootstrapped = true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const normalizedKey = String(key || '').trim();
|
|
92
|
+
if (!normalizedKey) {
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return db.prepare(`DELETE FROM ${CONFIG_TABLE} WHERE key = ?`).run(normalizedKey).changes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
bootstrapConfigDatabase,
|
|
101
|
+
saveOne,
|
|
102
|
+
getOne,
|
|
103
|
+
getAll,
|
|
104
|
+
deleteOne,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
|
|
5
|
+
export function createDatabaseProvider({getDatabasePath}) {
|
|
6
|
+
let database = null;
|
|
7
|
+
|
|
8
|
+
return function getDatabase() {
|
|
9
|
+
const configuredDatabasePath = String(getDatabasePath?.() || '').trim();
|
|
10
|
+
if (!configuredDatabasePath) {
|
|
11
|
+
throw new Error('Persistence database path is not configured');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (database) {
|
|
15
|
+
return database;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fs.mkdirSync(path.dirname(configuredDatabasePath), {recursive: true});
|
|
19
|
+
database = new Database(configuredDatabasePath);
|
|
20
|
+
database.pragma('journal_mode = WAL');
|
|
21
|
+
return database;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const ENTRY_TOKEN_TABLE = 'entry_tokens';
|
|
2
|
+
|
|
3
|
+
export function createEntryTokenDao({getDatabase}) {
|
|
4
|
+
let bootstrapped = false;
|
|
5
|
+
|
|
6
|
+
function bootstrapEntryTokenTable() {
|
|
7
|
+
if (bootstrapped) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const db = getDatabase();
|
|
12
|
+
db.exec(`
|
|
13
|
+
CREATE TABLE IF NOT EXISTS ${ENTRY_TOKEN_TABLE} (
|
|
14
|
+
token TEXT PRIMARY KEY,
|
|
15
|
+
created_at INTEGER NOT NULL
|
|
16
|
+
)
|
|
17
|
+
`);
|
|
18
|
+
bootstrapped = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadEntryTokens() {
|
|
22
|
+
bootstrapEntryTokenTable();
|
|
23
|
+
|
|
24
|
+
const db = getDatabase();
|
|
25
|
+
const rows = db.prepare(`
|
|
26
|
+
SELECT token, created_at
|
|
27
|
+
FROM ${ENTRY_TOKEN_TABLE}
|
|
28
|
+
ORDER BY created_at ASC
|
|
29
|
+
`).all();
|
|
30
|
+
|
|
31
|
+
return new Map(rows.map((row) => [row.token, row.created_at]));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function countEntryTokens() {
|
|
35
|
+
bootstrapEntryTokenTable();
|
|
36
|
+
|
|
37
|
+
const db = getDatabase();
|
|
38
|
+
const row = db.prepare(`
|
|
39
|
+
SELECT COUNT(*) AS count
|
|
40
|
+
FROM ${ENTRY_TOKEN_TABLE}
|
|
41
|
+
`).get();
|
|
42
|
+
|
|
43
|
+
return Number(row?.count || 0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getLatestEntryToken() {
|
|
47
|
+
bootstrapEntryTokenTable();
|
|
48
|
+
|
|
49
|
+
const db = getDatabase();
|
|
50
|
+
const row = db.prepare(`
|
|
51
|
+
SELECT token
|
|
52
|
+
FROM ${ENTRY_TOKEN_TABLE}
|
|
53
|
+
ORDER BY created_at DESC
|
|
54
|
+
LIMIT 1
|
|
55
|
+
`).get();
|
|
56
|
+
|
|
57
|
+
return String(row?.token || '');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getLatestEntryTokenRecord() {
|
|
61
|
+
bootstrapEntryTokenTable();
|
|
62
|
+
|
|
63
|
+
const db = getDatabase();
|
|
64
|
+
const row = db.prepare(`
|
|
65
|
+
SELECT token, created_at
|
|
66
|
+
FROM ${ENTRY_TOKEN_TABLE}
|
|
67
|
+
ORDER BY created_at DESC
|
|
68
|
+
LIMIT 1
|
|
69
|
+
`).get();
|
|
70
|
+
|
|
71
|
+
if (!row) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
token: String(row.token || ''),
|
|
77
|
+
createdAt: Number(row.created_at),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function deleteExpiredEntryTokens({olderThan, keepToken = ''} = {}) {
|
|
82
|
+
bootstrapEntryTokenTable();
|
|
83
|
+
|
|
84
|
+
const db = getDatabase();
|
|
85
|
+
const safeOlderThan = Math.floor(Number(olderThan));
|
|
86
|
+
if (!Number.isFinite(safeOlderThan)) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (keepToken) {
|
|
91
|
+
const result = db.prepare(`
|
|
92
|
+
DELETE FROM ${ENTRY_TOKEN_TABLE}
|
|
93
|
+
WHERE token != ?
|
|
94
|
+
AND created_at < ?
|
|
95
|
+
`).run(keepToken, safeOlderThan);
|
|
96
|
+
return result.changes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const result = db.prepare(`
|
|
100
|
+
DELETE FROM ${ENTRY_TOKEN_TABLE}
|
|
101
|
+
WHERE created_at < ?
|
|
102
|
+
`).run(safeOlderThan);
|
|
103
|
+
return result.changes;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function hasEntryToken(token) {
|
|
107
|
+
bootstrapEntryTokenTable();
|
|
108
|
+
|
|
109
|
+
const db = getDatabase();
|
|
110
|
+
const row = db.prepare(`
|
|
111
|
+
SELECT 1
|
|
112
|
+
FROM ${ENTRY_TOKEN_TABLE}
|
|
113
|
+
WHERE token = ?
|
|
114
|
+
LIMIT 1
|
|
115
|
+
`).get(token);
|
|
116
|
+
|
|
117
|
+
return Boolean(row);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function createEntryToken(token, createdAt = Date.now()) {
|
|
121
|
+
bootstrapEntryTokenTable();
|
|
122
|
+
|
|
123
|
+
const db = getDatabase();
|
|
124
|
+
db.prepare(`
|
|
125
|
+
INSERT INTO ${ENTRY_TOKEN_TABLE} (token, created_at)
|
|
126
|
+
VALUES (?, ?)
|
|
127
|
+
ON CONFLICT(token) DO UPDATE SET created_at = excluded.created_at
|
|
128
|
+
`).run(token, Math.floor(createdAt));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
loadEntryTokens,
|
|
133
|
+
countEntryTokens,
|
|
134
|
+
getLatestEntryToken,
|
|
135
|
+
getLatestEntryTokenRecord,
|
|
136
|
+
deleteExpiredEntryTokens,
|
|
137
|
+
hasEntryToken,
|
|
138
|
+
createEntryToken,
|
|
139
|
+
};
|
|
140
|
+
}
|