mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
gahew
This commit is contained in:
@@ -17,6 +17,9 @@
|
||||
- Split very large service files into smaller focused modules.
|
||||
- Keep files concise and single-purpose.
|
||||
- Add clear title comments at top of split files.
|
||||
- Every service/module file should start with a descriptive comment header containing:
|
||||
- a title line naming the service/module file
|
||||
- a longer purpose/scope description (not a one-liner)
|
||||
|
||||
## WebUI frontend
|
||||
- Every component must live in its own folder, even when it remains a single-file implementation.
|
||||
@@ -48,17 +51,20 @@
|
||||
- [ ] replay services (already partly split; reformat consistently)
|
||||
- [ ] room camera services (already partly split; reformat consistently)
|
||||
- [ ] rover manager service
|
||||
- [ ] session service
|
||||
- [ ] turn service
|
||||
- [x] session service
|
||||
- [x] turn service
|
||||
- [ ] verification service
|
||||
- [ ] video auth service
|
||||
- [ ] All remaining services: reorganize to folder structure where needed
|
||||
- [x] All remaining services: reorganize to folder structure where needed
|
||||
|
||||
### COMPLETED SERVICES
|
||||
- None yet.
|
||||
- turn service
|
||||
- session service
|
||||
|
||||
### LARGE CHANGES
|
||||
- None yet.
|
||||
- Folderized all files in `server/src/services/` into per-service folders with `index.js` entrypoints and updated internal relative imports for new path depth.
|
||||
- Split `server/src/services/turnService/index.js` by extracting constants, shared state helpers, and side-effect action helpers into `turnService/constants.js`, `turnService/state.js`, and `turnService/actions.js`.
|
||||
- Split `server/src/services/sessionService/index.js` by extracting config/timing constants, sync-throttle state storage, and visibility filter helpers into `sessionService/constants.js`, `sessionService/state.js`, and `sessionService/filters.js`.
|
||||
|
||||
## WebUI frontend
|
||||
### BIGGEST OFFENDERS
|
||||
|
||||
+6
-3
@@ -1,7 +1,10 @@
|
||||
// admin Log Service
|
||||
// Purpose: Defines the admin Log Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const io = require('../globals/io');
|
||||
const { getRole, roleEvents } = require('./roleService');
|
||||
const { getSocketIp } = require('../helpers/ipResolver');
|
||||
const io = require('../../globals/io');
|
||||
const { getRole, roleEvents } = require('../roleService');
|
||||
const { getSocketIp } = require('../../helpers/ipResolver');
|
||||
|
||||
const ADMIN_ROLES = new Set(['admin', 'lockdown', 'lockdown-admin']);
|
||||
const MAX_HISTORY = 200;
|
||||
+7
-4
@@ -1,9 +1,12 @@
|
||||
// admin Reason Service
|
||||
// Purpose: Defines the admin Reason Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('adminReasonService');
|
||||
const { isAdmin } = require('./roleService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('adminReasonService');
|
||||
const { isAdmin } = require('../roleService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
|
||||
const STORE_PATH = path.join(DATA_DIR, 'admin-reason.json');
|
||||
@@ -1,4 +1,7 @@
|
||||
const io = require('../globals/io');
|
||||
// alert Service
|
||||
// Purpose: Defines the alert Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
|
||||
function sendAlert({ color, title, message, ts = Date.now() }) {
|
||||
const payload = {
|
||||
+8
-5
@@ -1,9 +1,12 @@
|
||||
// assignment Service
|
||||
// Purpose: Defines the assignment Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('assignment');
|
||||
const { MODES, getMode, modeEvents } = require('./modeManager');
|
||||
const { roleEvents, getRole, isAdmin, isLockdownAdmin } = require('./roleService');
|
||||
const roverManager = require('./roverManager');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('assignment');
|
||||
const { MODES, getMode, modeEvents } = require('../modeManager');
|
||||
const { roleEvents, getRole, isAdmin, isLockdownAdmin } = require('../roleService');
|
||||
const roverManager = require('../roverManager');
|
||||
|
||||
const socketRefs = new Map(); // socketId -> socket
|
||||
const assignments = new Map(); // socketId -> roverId
|
||||
+10
-7
@@ -1,14 +1,17 @@
|
||||
// audio Forward Service
|
||||
// Purpose: Defines the audio Forward Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawn, spawnSync } = require('child_process');
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('audioForwardService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const roverManager = require('./roverManager');
|
||||
const turnService = require('./turnService');
|
||||
const { isVerified } = require('./verificationService');
|
||||
const videoSessions = require('./videoSessions');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('audioForwardService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const roverManager = require('../roverManager');
|
||||
const turnService = require('../turnService');
|
||||
const { isVerified } = require('../verificationService');
|
||||
const videoSessions = require('../videoSessions');
|
||||
|
||||
const audioForwardEvents = new EventEmitter();
|
||||
const config = loadConfig();
|
||||
+9
-6
@@ -1,12 +1,15 @@
|
||||
// audio Levels Service
|
||||
// Purpose: Defines the audio Levels Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('audioLevelsService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { isAdmin } = require('./roleService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { issueCommand } = require('./commandService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('audioLevelsService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { isAdmin } = require('../roleService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { issueCommand } = require('../commandService');
|
||||
|
||||
const audioLevelsEvents = new EventEmitter();
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
|
||||
@@ -1,10 +1,13 @@
|
||||
// auth Service
|
||||
// Purpose: Defines the auth Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const bcrypt = require('bcrypt');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('authService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { clearLockdownTimer } = require('./lockdownGuard');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { setRole } = require('./roleService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('authService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { clearLockdownTimer } = require('../lockdownGuard');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { setRole } = require('../roleService');
|
||||
|
||||
const config = loadConfig();
|
||||
const admins = config.admins || [];
|
||||
@@ -1,6 +1,9 @@
|
||||
const logger = require('../globals/logger').child('batteryManager');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const { managerEvents, lockRover, rovers } = require('./roverManager');
|
||||
// battery Manager
|
||||
// Purpose: Defines the battery Manager module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const logger = require('../../globals/logger').child('batteryManager');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const { managerEvents, lockRover, rovers } = require('../roverManager');
|
||||
|
||||
const STATES = {
|
||||
NORMAL: 'normal',
|
||||
+17
-14
@@ -1,24 +1,27 @@
|
||||
// button Box Service
|
||||
// Purpose: Defines the button Box Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const express = require('express');
|
||||
const { app } = require('../globals/http');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('buttonBoxService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const { getRewardById, listRewards } = require('../rewards');
|
||||
const roverManager = require('./roverManager');
|
||||
const { issueCommand } = require('./commandService');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const { sendExternalTyping, sendExternalMessage, sendSystemMessage } = require('./chatService');
|
||||
const { setMode, getMode } = require('./modeManager');
|
||||
const { getAdminReason, setAdminReason, clearAdminReason } = require('./adminReasonService');
|
||||
const assignmentService = require('./assignmentService');
|
||||
const { app } = require('../../globals/http');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('buttonBoxService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const { getRewardById, listRewards } = require('../../rewards');
|
||||
const roverManager = require('../roverManager');
|
||||
const { issueCommand } = require('../commandService');
|
||||
const { sendAlert } = require('../alertService');
|
||||
const { sendExternalTyping, sendExternalMessage, sendSystemMessage } = require('../chatService');
|
||||
const { setMode, getMode } = require('../modeManager');
|
||||
const { getAdminReason, setAdminReason, clearAdminReason } = require('../adminReasonService');
|
||||
const assignmentService = require('../assignmentService');
|
||||
const {
|
||||
getState: getHomeAssistantState,
|
||||
setEntityState: setHomeAssistantEntityState,
|
||||
setLightsLockedOn: setHomeAssistantLightsLockedOn,
|
||||
} = require('./homeAssistantService');
|
||||
const { getRequestIp, isLocalNetwork, normalizeIp } = require('../helpers/ipResolver');
|
||||
} = require('../homeAssistantService');
|
||||
const { getRequestIp, isLocalNetwork, normalizeIp } = require('../../helpers/ipResolver');
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
|
||||
const STORE_PATH = path.join(DATA_DIR, 'buttonbox-state.json');
|
||||
@@ -1,15 +1,18 @@
|
||||
// chat Service
|
||||
// Purpose: Defines the chat Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { DataSet, RegExpMatcher, englishDataset, englishRecommendedTransformers } = require('obscenity');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('chatService');
|
||||
const { publishEvent, subscribe } = require('./eventBus');
|
||||
const { getRole } = require('./roleService');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { describeAssignment } = require('./assignmentService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { getNickname } = require('./nicknameService');
|
||||
const { issueCommand } = require('./commandService');
|
||||
const { getAdminReason } = require('./adminReasonService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('chatService');
|
||||
const { publishEvent, subscribe } = require('../eventBus');
|
||||
const { getRole } = require('../roleService');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { describeAssignment } = require('../assignmentService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { issueCommand } = require('../commandService');
|
||||
const { getAdminReason } = require('../adminReasonService');
|
||||
|
||||
const RATE_LIMIT_WINDOW_MS = 8000;
|
||||
const RATE_LIMIT_MAX = 5;
|
||||
@@ -141,7 +144,7 @@ function buildRoverCtxSnapshot(roverId) {
|
||||
if (!record) return null;
|
||||
const sensors = record?.lastSensor?.decoded || {};
|
||||
const batteryState = record?.batteryState || null;
|
||||
const { getActiveDrivers } = require('./turnService');
|
||||
const { getActiveDrivers } = require('../turnService');
|
||||
const activeDrivers = getActiveDrivers();
|
||||
const driverSocketId = activeDrivers[key] || record?.drivers?.values?.().next?.().value || null;
|
||||
const charging = isChargingFromSensors(sensors);
|
||||
@@ -430,7 +433,7 @@ function maybeSpeak(socket, message, ttsOptions) {
|
||||
const audio = record?.meta?.audio || {};
|
||||
const ttsEnabled = Boolean(audio.ttsEnabled);
|
||||
if (!ttsEnabled) return;
|
||||
const { isQueuedDriver } = require('./turnService');
|
||||
const { isQueuedDriver } = require('../turnService');
|
||||
if (
|
||||
!roverManager.canDrive(message.roverId, socket) &&
|
||||
!isQueuedDriver(message.roverId, socket?.id)
|
||||
+10
-7
@@ -1,10 +1,13 @@
|
||||
// command Service
|
||||
// Purpose: Defines the command Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const io = require('../globals/io');
|
||||
const roverManager = require('./roverManager');
|
||||
const { isAdmin, isLockdownAdmin } = require('./roleService');
|
||||
const { isDeterred } = require('./verificationService');
|
||||
const logger = require('../globals/logger').child('commandService');
|
||||
const { isNightVisionBlocked } = require('../rewards/definitions/darkness');
|
||||
const io = require('../../globals/io');
|
||||
const roverManager = require('../roverManager');
|
||||
const { isAdmin, isLockdownAdmin } = require('../roleService');
|
||||
const { isDeterred } = require('../verificationService');
|
||||
const logger = require('../../globals/logger').child('commandService');
|
||||
const { isNightVisionBlocked } = require('../../rewards/definitions/darkness');
|
||||
|
||||
const pendingCommands = new Map(); // id -> { roverId }
|
||||
const lastDriveActivity = new Map(); // roverId -> { ts, socketId, direction, speed, isAdmin }
|
||||
@@ -122,7 +125,7 @@ io.on('connection', (socket) => {
|
||||
const id = issueCommand(roverId, { type, ...payload });
|
||||
logger.info('Queued command', socket.id, roverId, type);
|
||||
try {
|
||||
const { recordActivity } = require('./turnService');
|
||||
const { recordActivity } = require('../turnService');
|
||||
recordActivity(roverId, socket.id);
|
||||
} catch (err) {
|
||||
// best effort; ignore activity update errors
|
||||
+7
-4
@@ -1,9 +1,12 @@
|
||||
// community Goal Service
|
||||
// Purpose: Defines the community Goal Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('communityGoalService');
|
||||
const { isAdmin } = require('./roleService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('communityGoalService');
|
||||
const { isAdmin } = require('../roleService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
|
||||
const STORE_PATH = path.join(DATA_DIR, 'community-goal.json');
|
||||
+20
-17
@@ -1,3 +1,6 @@
|
||||
// discord Bot Service
|
||||
// Purpose: Defines the discord Bot Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const {
|
||||
Client,
|
||||
GatewayIntentBits,
|
||||
@@ -9,21 +12,21 @@ const {
|
||||
WebhookClient,
|
||||
MessageFlags,
|
||||
} = require('discord.js');
|
||||
const logger = require('../globals/logger').child('discordBot');
|
||||
const io = require('../globals/io');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { subscribe } = require('./eventBus');
|
||||
const roverManager = require('./roverManager');
|
||||
const logger = require('../../globals/logger').child('discordBot');
|
||||
const io = require('../../globals/io');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { subscribe } = require('../eventBus');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getRoster, lockRover, rovers } = roverManager;
|
||||
const { MODES, getMode, setMode } = require('./modeManager');
|
||||
const { sendExternalMessage, sendExternalTyping } = require('./chatService');
|
||||
const { buildReplayVideo } = require('./replayBuildService');
|
||||
const { getReplaySources, getDefaultDiscordSources, validateSources } = require('./replaySourceService');
|
||||
const { getActiveDrivers } = require('./turnService');
|
||||
const { getNickname } = require('./nicknameService');
|
||||
const { tryTriggerReplay } = require('./replayService');
|
||||
const { getCommunityGoal, setCommunityGoal, clearCommunityGoal } = require('./communityGoalService');
|
||||
const { getAdminReason, setAdminReason, clearAdminReason } = require('./adminReasonService');
|
||||
const { MODES, getMode, setMode } = require('../modeManager');
|
||||
const { sendExternalMessage, sendExternalTyping } = require('../chatService');
|
||||
const { buildReplayVideo } = require('../replayBuildService');
|
||||
const { getReplaySources, getDefaultDiscordSources, validateSources } = require('../replaySourceService');
|
||||
const { getActiveDrivers } = require('../turnService');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { tryTriggerReplay } = require('../replayService');
|
||||
const { getCommunityGoal, setCommunityGoal, clearCommunityGoal } = require('../communityGoalService');
|
||||
const { getAdminReason, setAdminReason, clearAdminReason } = require('../adminReasonService');
|
||||
const {
|
||||
getGuildConfig,
|
||||
listGuildConfigs,
|
||||
@@ -31,7 +34,7 @@ const {
|
||||
setGuildConfig,
|
||||
normalizeMode,
|
||||
VALID_MODES,
|
||||
} = require('./discordGuildStore');
|
||||
} = require('../discordGuildStore');
|
||||
const {
|
||||
attachDmMessage,
|
||||
getRequestByMessageId,
|
||||
@@ -42,7 +45,7 @@ const {
|
||||
listDeterredUsers,
|
||||
deterUser,
|
||||
undeterUser,
|
||||
} = require('./verificationService');
|
||||
} = require('../verificationService');
|
||||
const {
|
||||
DM_APPROVE_EMOJI: PRIVATE_ACCESS_APPROVE_EMOJI,
|
||||
DM_DENY_EMOJI: PRIVATE_ACCESS_DENY_EMOJI,
|
||||
@@ -50,7 +53,7 @@ const {
|
||||
getRequestByMessageId: getPrivateAccessRequestByMessageId,
|
||||
approveRequest: approvePrivateAccessRequest,
|
||||
denyRequest: denyPrivateAccessRequest,
|
||||
} = require('./privateRoverAccessRequestService');
|
||||
} = require('../privateRoverAccessRequestService');
|
||||
const config = loadConfig();
|
||||
const discordConfig = config.discord || {};
|
||||
const enabled = Boolean(discordConfig.token);
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
// discord Guild Store
|
||||
// Purpose: Defines the discord Guild Store module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const logger = require('../globals/logger').child('discordGuildStore');
|
||||
const logger = require('../../globals/logger').child('discordGuildStore');
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
|
||||
const STORE_PATH = path.join(DATA_DIR, 'discord-guilds.json');
|
||||
+5
-2
@@ -1,5 +1,8 @@
|
||||
const { app } = require('../globals/http');
|
||||
const { renderIndexHtml, renderOgImage } = require('./embedService');
|
||||
// embed Http Service
|
||||
// Purpose: Defines the embed Http Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { app } = require('../../globals/http');
|
||||
const { renderIndexHtml, renderOgImage } = require('../embedService');
|
||||
|
||||
app.get(['/', '/spectate', '/mini'], async (req, res) => {
|
||||
try {
|
||||
@@ -1,13 +1,16 @@
|
||||
// embed Service
|
||||
// Purpose: Defines the embed Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const path = require('path');
|
||||
const fsp = require('fs/promises');
|
||||
const sharp = require('sharp');
|
||||
|
||||
const { getMode } = require('./modeManager');
|
||||
const roverManager = require('./roverManager');
|
||||
const { getActiveDrivers, getTurnQueues } = require('./turnService');
|
||||
const { getRoomCameras } = require('./roomCameraService');
|
||||
const { getRoomCameraState } = require('./roomCameraSnapshotService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { getMode } = require('../modeManager');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getActiveDrivers, getTurnQueues } = require('../turnService');
|
||||
const { getRoomCameras } = require('../roomCameraService');
|
||||
const { getRoomCameraState } = require('../roomCameraSnapshotService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
|
||||
const INDEX_HTML_PATH = path.join(__dirname, '..', '..', 'public', 'index.html');
|
||||
const BITMAP_PATH = path.join(__dirname, '..', '..', 'public', 'bitmap.png');
|
||||
@@ -1,5 +1,8 @@
|
||||
// event Bus
|
||||
// Purpose: Defines the event Bus module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const baseLogger = require('../globals/logger');
|
||||
const baseLogger = require('../../globals/logger');
|
||||
const logger = baseLogger.child ? baseLogger.child('eventBus') : baseLogger;
|
||||
|
||||
const eventBus = new EventEmitter();
|
||||
@@ -1,9 +1,12 @@
|
||||
// health Service
|
||||
// Purpose: Defines the health Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fsp = require('fs/promises');
|
||||
const path = require('path');
|
||||
const roverManager = require('./roverManager');
|
||||
const { getRoomCameras } = require('./roomCameraService');
|
||||
const { getRoomCameraState } = require('./roomCameraSnapshotService');
|
||||
const { getReplayHealthSnapshot } = require('./replayEngineV2');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getRoomCameras } = require('../roomCameraService');
|
||||
const { getRoomCameraState } = require('../roomCameraSnapshotService');
|
||||
const { getReplayHealthSnapshot } = require('../replayEngineV2');
|
||||
|
||||
const ROVER_SNAPSHOT_DIR = process.env.ROVER_SNAPSHOT_DIR || '/var/lib/rover-snapshots';
|
||||
const HEALTH_INTERVAL_MS = 5000;
|
||||
+12
-9
@@ -1,15 +1,18 @@
|
||||
// home Assistant Service
|
||||
// Purpose: Defines the home Assistant Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const WebSocket = require('ws');
|
||||
const { createConnection, subscribeEntities, callService, Auth } = require('home-assistant-js-websocket');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('homeAssistantService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { getMode, MODES, modeEvents } = require('./modeManager');
|
||||
const { isAdmin, isLockdownAdmin } = require('./roleService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { issueCommand } = require('./commandService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const { getActiveDrivers, turnEvents } = require('./turnService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('homeAssistantService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { getMode, MODES, modeEvents } = require('../modeManager');
|
||||
const { isAdmin, isLockdownAdmin } = require('../roleService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { issueCommand } = require('../commandService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const { getActiveDrivers, turnEvents } = require('../turnService');
|
||||
|
||||
// home-assistant-js-websocket expects a global WebSocket in Node.
|
||||
if (!global.WebSocket) {
|
||||
@@ -1,7 +0,0 @@
|
||||
const { httpServer } = require('../globals/http');
|
||||
const config = require('../globals/config');
|
||||
const logger = require('../globals/logger').child('httpServer');
|
||||
|
||||
httpServer.listen(config.port, () => {
|
||||
logger.info(`Server listening on :${config.port}`);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
// http Server
|
||||
// Purpose: Defines the http Server module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { httpServer } = require('../../globals/http');
|
||||
const config = require('../../globals/config');
|
||||
const logger = require('../../globals/logger').child('httpServer');
|
||||
|
||||
httpServer.listen(config.port, () => {
|
||||
logger.info(`Server listening on :${config.port}`);
|
||||
});
|
||||
+11
-8
@@ -1,12 +1,15 @@
|
||||
// human Alert Button Service
|
||||
// Purpose: Defines the human Alert Button Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const sharp = require('sharp');
|
||||
const logger = require('../globals/logger').child('humanAlertButton');
|
||||
const { subscribe, publishEvent } = require('./eventBus');
|
||||
const { getMode, MODES, setMode } = require('./modeManager');
|
||||
const { issueCommand } = require('./commandService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { toggleLightsLockedOn } = require('./homeAssistantService');
|
||||
const { getRoomCameras } = require('./roomCameraService');
|
||||
const { getRoomCameraState } = require('./roomCameraSnapshotService');
|
||||
const logger = require('../../globals/logger').child('humanAlertButton');
|
||||
const { subscribe, publishEvent } = require('../eventBus');
|
||||
const { getMode, MODES, setMode } = require('../modeManager');
|
||||
const { issueCommand } = require('../commandService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { toggleLightsLockedOn } = require('../homeAssistantService');
|
||||
const { getRoomCameras } = require('../roomCameraService');
|
||||
const { getRoomCameraState } = require('../roomCameraSnapshotService');
|
||||
|
||||
const HA_BUTTON_EVENT_TYPE = 'ha.button.action';
|
||||
const HUMAN_ALERT_ACTION = 'humanAlert';
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
// identity Service
|
||||
// Purpose: Defines the identity Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const crypto = require('crypto');
|
||||
const { getSocketIp, normalizeIp } = require('../helpers/ipResolver');
|
||||
const { getSocketIp, normalizeIp } = require('../../helpers/ipResolver');
|
||||
|
||||
const COOKIE_USER_ID_RE = /^cu_[a-f0-9]{32}$/;
|
||||
|
||||
+11
-8
@@ -1,14 +1,17 @@
|
||||
// llm Commentary Service
|
||||
// Purpose: Defines the llm Commentary Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fsp = require('fs/promises');
|
||||
const path = require('path');
|
||||
const { Ollama } = require('ollama');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('llmCommentary');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { getRole, roleEvents } = require('./roleService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { getActiveDrivers } = require('./turnService');
|
||||
const { getNickname } = require('./nicknameService');
|
||||
const { getRecentMessages, sendSystemMessage } = require('./chatService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('llmCommentary');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { getRole, roleEvents } = require('../roleService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getActiveDrivers } = require('../turnService');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { getRecentMessages, sendSystemMessage } = require('../chatService');
|
||||
|
||||
const PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'commentary_system.txt');
|
||||
const DEFAULT_FREQUENCY_MS = 0;
|
||||
@@ -1,6 +1,9 @@
|
||||
const io = require('../globals/io');
|
||||
const { MODES, getMode, modeEvents } = require('./modeManager');
|
||||
const { isLockdownAdmin } = require('./roleService');
|
||||
// lockdown Guard
|
||||
// Purpose: Defines the lockdown Guard module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const { MODES, getMode, modeEvents } = require('../modeManager');
|
||||
const { isLockdownAdmin } = require('../roleService');
|
||||
|
||||
function disconnectForLockdown(socket) {
|
||||
socket.emit('lockdown', { message: 'Server is in lockdown mode' });
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
// log Stream Service
|
||||
// Purpose: Defines the log Stream Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const io = require('../globals/io');
|
||||
const loggerRoot = require('../globals/logger');
|
||||
const io = require('../../globals/io');
|
||||
const loggerRoot = require('../../globals/logger');
|
||||
const logger = loggerRoot.child('logStream');
|
||||
|
||||
const MAX_HISTORY = 200;
|
||||
@@ -1,9 +1,12 @@
|
||||
// mode Manager
|
||||
// Purpose: Defines the mode Manager module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const io = require('../../globals/io');
|
||||
const { sendAlert } = require('../alertService');
|
||||
const ALERT_COLOR = '#ff9800';
|
||||
const { isAdmin, isLockdownAdmin } = require('./roleService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const { isAdmin, isLockdownAdmin } = require('../roleService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
|
||||
const MODES = {
|
||||
OPEN: 'open',
|
||||
@@ -1,15 +1,18 @@
|
||||
// neato Service
|
||||
// Purpose: Defines the neato Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('neatoService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { isVerified } = require('./verificationService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('neatoService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { isVerified } = require('../verificationService');
|
||||
const {
|
||||
homeAssistantEvents,
|
||||
getRawEntitySnapshot,
|
||||
callHomeAssistantService,
|
||||
isConnected: isHomeAssistantConnected,
|
||||
enabled: homeAssistantEnabled,
|
||||
} = require('./homeAssistantService');
|
||||
} = require('../homeAssistantService');
|
||||
|
||||
const events = new EventEmitter();
|
||||
const config = loadConfig();
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
// nickname Service
|
||||
// Purpose: Defines the nickname Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('nicknameService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('nicknameService');
|
||||
|
||||
const nicknameEvents = new EventEmitter();
|
||||
|
||||
+11
-8
@@ -1,12 +1,15 @@
|
||||
// private Rover Access Request Service
|
||||
// Purpose: Defines the private Rover Access Request Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const crypto = require('crypto');
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('privateRoverAccessRequest');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const roverManager = require('./roverManager');
|
||||
const { getNickname } = require('./nicknameService');
|
||||
const { getRole, isLockdownAdmin } = require('./roleService');
|
||||
const { getSocketIp, normalizeIp } = require('../helpers/ipResolver');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('privateRoverAccessRequest');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { getRole, isLockdownAdmin } = require('../roleService');
|
||||
const { getSocketIp, normalizeIp } = require('../../helpers/ipResolver');
|
||||
|
||||
const requestEvents = new EventEmitter();
|
||||
const REQUEST_COOLDOWN_MS = 15 * 1000;
|
||||
@@ -200,7 +203,7 @@ function tryAssignClosedPrivateRover(socket, roverId) {
|
||||
});
|
||||
roverManager.managerEvents.emit('switch', { socketId: socket.id, roverId: String(roverId) });
|
||||
try {
|
||||
const assignmentService = require('./assignmentService');
|
||||
const assignmentService = require('../assignmentService');
|
||||
assignmentService.moveAssignment(socket, String(roverId), { releasePrevious: false });
|
||||
} catch (err) {
|
||||
logger.warn('Failed to move assignment after private access grant', { socketId: socket.id, error: err.message });
|
||||
@@ -1,5 +0,0 @@
|
||||
const { buildReplayVideo } = require('./replayEngineV2');
|
||||
|
||||
module.exports = {
|
||||
buildReplayVideo,
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// replay Build Service
|
||||
// Purpose: Defines the replay Build Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { buildReplayVideo } = require('../replayEngineV2');
|
||||
|
||||
module.exports = {
|
||||
buildReplayVideo,
|
||||
};
|
||||
+10
-7
@@ -1,3 +1,6 @@
|
||||
// replay Engine V2
|
||||
// Purpose: Defines the replay Engine V2 module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { spawn, execFile } = require('child_process');
|
||||
const fsp = require('fs/promises');
|
||||
const fs = require('fs');
|
||||
@@ -5,13 +8,13 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
const { promisify } = require('util');
|
||||
const EventEmitter = require('events');
|
||||
const logger = require('../globals/logger').child('replayEngineV2');
|
||||
const roverManager = require('./roverManager');
|
||||
const { getRoomCameras, roomCameraEvents } = require('./roomCameraService');
|
||||
const io = require('../globals/io');
|
||||
const { getActiveDrivers } = require('./turnService');
|
||||
const { getNickname } = require('./nicknameService');
|
||||
const { getRecentMessages } = require('./chatService');
|
||||
const logger = require('../../globals/logger').child('replayEngineV2');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getRoomCameras, roomCameraEvents } = require('../roomCameraService');
|
||||
const io = require('../../globals/io');
|
||||
const { getActiveDrivers } = require('../turnService');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { getRecentMessages } = require('../chatService');
|
||||
const sharp = require('sharp');
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
@@ -1,3 +1,6 @@
|
||||
// replay Service
|
||||
// Purpose: Defines the replay Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const COOLDOWN_MS = 10 * 1000;
|
||||
+12
-9
@@ -1,12 +1,15 @@
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('replaySocket');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const { tryTriggerReplay } = require('./replayService');
|
||||
const { validateSources, getDefaultWebSources } = require('./replaySourceService');
|
||||
const assignmentService = require('./assignmentService');
|
||||
const { getNickname } = require('./nicknameService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
// replay Socket Service
|
||||
// Purpose: Defines the replay Socket Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('replaySocket');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const { tryTriggerReplay } = require('../replayService');
|
||||
const { validateSources, getDefaultWebSources } = require('../replaySourceService');
|
||||
const assignmentService = require('../assignmentService');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
|
||||
const config = loadConfig();
|
||||
const discordConfig = config.discord || {};
|
||||
+5
-2
@@ -1,5 +1,8 @@
|
||||
const roverManager = require('./roverManager');
|
||||
const { getRoomCameras } = require('./roomCameraService');
|
||||
// replay Source Service
|
||||
// Purpose: Defines the replay Source Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const roverManager = require('../roverManager');
|
||||
const { getRoomCameras } = require('../roomCameraService');
|
||||
|
||||
function getReplaySources(socket = null) {
|
||||
const roster = socket ? roverManager.getRosterForSocket(socket) : roverManager.getRoster();
|
||||
@@ -1,3 +1,6 @@
|
||||
// role Service
|
||||
// Purpose: Defines the role Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const roleEvents = new EventEmitter();
|
||||
+5
-2
@@ -1,3 +1,6 @@
|
||||
// room Camera Replay Service
|
||||
// Purpose: Defines the room Camera Replay Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { execFile } = require('child_process');
|
||||
const EventEmitter = require('events');
|
||||
const fsp = require('fs/promises');
|
||||
@@ -5,8 +8,8 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
const { promisify } = require('util');
|
||||
|
||||
const logger = require('../globals/logger').child('roomCameraReplay');
|
||||
const { getRoomCamera, getRoomCameras } = require('./roomCameraService');
|
||||
const logger = require('../../globals/logger').child('roomCameraReplay');
|
||||
const { getRoomCamera, getRoomCameras } = require('../roomCameraService');
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
// room Camera Service
|
||||
// Purpose: Defines the room Camera Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const logger = require('../globals/logger').child('roomCameraService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const logger = require('../../globals/logger').child('roomCameraService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
|
||||
const events = new EventEmitter();
|
||||
const config = loadConfig();
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
// room Camera Snapshot Service
|
||||
// Purpose: Defines the room Camera Snapshot Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const logger = require('../globals/logger').child('roomCameraSnapshot');
|
||||
const { getRoomCameras, roomCameraEvents } = require('./roomCameraService');
|
||||
const logger = require('../../globals/logger').child('roomCameraSnapshot');
|
||||
const { getRoomCameras, roomCameraEvents } = require('../roomCameraService');
|
||||
|
||||
const POLL_INTERVAL_MS = 67;
|
||||
const FETCH_TIMEOUT_MS = 2000;
|
||||
+9
-6
@@ -1,9 +1,12 @@
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('roomCameraSocket');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('./roleService');
|
||||
const { getRoomCamera, getRoomCameras } = require('./roomCameraService');
|
||||
const { roomCameraStreamEvents, getRoomCameraState } = require('./roomCameraSnapshotService');
|
||||
// room Camera Socket Service
|
||||
// Purpose: Defines the room Camera Socket Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('roomCameraSocket');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('../roleService');
|
||||
const { getRoomCamera, getRoomCameras } = require('../roomCameraService');
|
||||
const { roomCameraStreamEvents, getRoomCameraState } = require('../roomCameraSnapshotService');
|
||||
|
||||
const SUBSCRIBE_LIMIT = 50;
|
||||
const SUBSCRIBE_WINDOW_MS = 10000;
|
||||
+8
-5
@@ -1,9 +1,12 @@
|
||||
const roverWSS = require('../globals/ws');
|
||||
const logger = require('../globals/logger').child('roverConnection');
|
||||
const roverManager = require('./roverManager');
|
||||
const { sendAlert } = require('./alertService');
|
||||
// rover Connection Service
|
||||
// Purpose: Defines the rover Connection Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const roverWSS = require('../../globals/ws');
|
||||
const logger = require('../../globals/logger').child('roverConnection');
|
||||
const roverManager = require('../roverManager');
|
||||
const { sendAlert } = require('../alertService');
|
||||
const ALERT_COLOR = '#00bcd4';
|
||||
const { handleAck } = require('./commandService');
|
||||
const { handleAck } = require('../commandService');
|
||||
|
||||
function coerceBool(value) {
|
||||
if (typeof value === 'boolean') return value;
|
||||
@@ -1,18 +1,21 @@
|
||||
// rover Manager
|
||||
// Purpose: Defines the rover Manager module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('roverManager');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('roverManager');
|
||||
const { sendAlert } = require('../alertService');
|
||||
const ALERT_COLOR = '#8bc34a';
|
||||
const { parseSensorFrame } = require('../helpers/sensorDecoder');
|
||||
const { MODES, getMode } = require('./modeManager');
|
||||
const { isAdmin, isLockdownAdmin, roleEvents } = require('./roleService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const videoSessions = require('./videoSessions');
|
||||
const { parseSensorFrame } = require('../../helpers/sensorDecoder');
|
||||
const { MODES, getMode } = require('../modeManager');
|
||||
const { isAdmin, isLockdownAdmin, roleEvents } = require('../roleService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const videoSessions = require('../videoSessions');
|
||||
|
||||
const rovers = new Map(); // roverId -> record
|
||||
const socketToRovers = new Map(); // socketId -> Set(roverId)
|
||||
const spectatorSockets = new Set();
|
||||
const turnService = require('./turnService');
|
||||
const turnService = require('../turnService');
|
||||
const managerEvents = new EventEmitter();
|
||||
const DOCK_GUARD_WINDOW_MS = 2 * 1000;
|
||||
const IDLE_UNDOCKED_MS = 2 * 60 * 1000;
|
||||
@@ -186,7 +189,7 @@ function getControlDenialReason(record, socket, options = {}) {
|
||||
if (mode === MODES.LOCKDOWN && !isLockdownAdmin(socket)) {
|
||||
return 'Server in lockdown';
|
||||
}
|
||||
const { isDeterred } = require('./verificationService');
|
||||
const { isDeterred } = require('../verificationService');
|
||||
if (!isAdmin(socket) && isDeterred(socket)) {
|
||||
return 'Not authorized';
|
||||
}
|
||||
@@ -205,7 +208,7 @@ function getControlDenialReason(record, socket, options = {}) {
|
||||
if (isLockdownAdmin(socket)) {
|
||||
return null;
|
||||
}
|
||||
const { isVerified } = require('./verificationService');
|
||||
const { isVerified } = require('../verificationService');
|
||||
if (!isVerified(socket)) {
|
||||
return 'Private rover requires verification';
|
||||
}
|
||||
@@ -306,7 +309,7 @@ function removeRover(id) {
|
||||
}
|
||||
|
||||
function sendPrivateToggleTTS(roverId, open, reason) {
|
||||
const { issueCommand } = require('./commandService');
|
||||
const { issueCommand } = require('../commandService');
|
||||
let text = open ? 'Private rover is now open.' : 'Private rover is now closed.';
|
||||
if (!open && reason === 'auto_idle') {
|
||||
text = 'Private rover closed due to inactivity.';
|
||||
@@ -600,7 +603,7 @@ function stopSafetyBackoffTimer(roverId) {
|
||||
function triggerSafetyAction(record, mode, options = {}) {
|
||||
if (!record) return;
|
||||
const roverId = record.id;
|
||||
const { issueCommand, setDriveCooldown } = require('./commandService');
|
||||
const { issueCommand, setDriveCooldown } = require('../commandService');
|
||||
const now = Date.now();
|
||||
const cooldownMs = clampInt(options.cooldownMs, 100, 10000, DEFAULT_PRIVATE_SAFETY.triggerCooldownMs);
|
||||
const backoffMs = clampInt(options.backoffMs, 50, 5000, 0);
|
||||
@@ -917,7 +920,7 @@ function attemptDockGuard(roverId) {
|
||||
stopDockGuard(roverId);
|
||||
return;
|
||||
}
|
||||
const { issueCommand } = require('./commandService');
|
||||
const { issueCommand } = require('../commandService');
|
||||
try {
|
||||
issueCommand(roverId, { type: 'sensorStream', sensorStream: { enable: true } });
|
||||
issueCommand(roverId, { type: 'raw', raw: DOCK_COMMAND_BASE64 });
|
||||
@@ -928,7 +931,7 @@ function attemptDockGuard(roverId) {
|
||||
function handleIdleUndock(undockedRecord) {
|
||||
if (!undockedRecord || undockedRecord.drivers.size > 0) return;
|
||||
const now = Date.now();
|
||||
const { getRecentDriveActivity, setDriveCooldown, issueCommand } = require('./commandService');
|
||||
const { getRecentDriveActivity, setDriveCooldown, issueCommand } = require('../commandService');
|
||||
const candidates = getRecentDriveActivity(DOCK_GUARD_WINDOW_MS, { excludeAdmins: true })
|
||||
.filter((candidate) => candidate.roverId !== undockedRecord.id);
|
||||
if (candidates.length === 0) return;
|
||||
@@ -965,7 +968,7 @@ function handleIdleUndock(undockedRecord) {
|
||||
|
||||
function nudgeRover(roverId, direction = 'backward') {
|
||||
if (!roverId) return;
|
||||
const { issueCommand } = require('./commandService');
|
||||
const { issueCommand } = require('../commandService');
|
||||
clearTimeout(backoffTimers.get(roverId));
|
||||
const speed = direction === 'forward' ? BACKOFF_SPEED : -BACKOFF_SPEED;
|
||||
try {
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
// rover Snapshot Service
|
||||
// Purpose: Defines the rover Snapshot Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
const logger = require('../globals/logger').child('roverSnapshot');
|
||||
const roverManager = require('./roverManager');
|
||||
const logger = require('../../globals/logger').child('roverSnapshot');
|
||||
const roverManager = require('../roverManager');
|
||||
|
||||
const SNAPSHOT_DIR = process.env.ROVER_SNAPSHOT_DIR || '/var/lib/rover-snapshots';
|
||||
const POLL_INTERVAL_MS = 300;
|
||||
+9
-6
@@ -1,9 +1,12 @@
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('roverSnapshotSocket');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('./roleService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { roverSnapshotEvents, getRoverSnapshotState } = require('./roverSnapshotService');
|
||||
// rover Snapshot Socket Service
|
||||
// Purpose: Defines the rover Snapshot Socket Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('roverSnapshotSocket');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('../roleService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { roverSnapshotEvents, getRoverSnapshotState } = require('../roverSnapshotService');
|
||||
|
||||
const SUBSCRIBE_LIMIT = 50;
|
||||
const SUBSCRIBE_WINDOW_MS = 10000;
|
||||
+7
-4
@@ -1,8 +1,11 @@
|
||||
// server Control Service
|
||||
// Purpose: Defines the server Control Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { spawn } = require('child_process');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('serverControlService');
|
||||
const { isAdmin } = require('./roleService');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('serverControlService');
|
||||
const { isAdmin } = require('../roleService');
|
||||
const { sendAlert } = require('../alertService');
|
||||
|
||||
const ALERT_COLOR = '#ff5722';
|
||||
let rebootPending = false;
|
||||
@@ -0,0 +1,24 @@
|
||||
// session Service constants
|
||||
// Purpose: Defines timing and static social/config constants used by session synchronization behavior.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating constants from orchestration logic.
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
|
||||
const config = loadConfig();
|
||||
const discordInvite = config.discord?.invite || null;
|
||||
const kofiLink = config.kofi?.link || null;
|
||||
const serverTimezone = config.timezone || null;
|
||||
const configuredSocials = Array.isArray(config.socials) ? config.socials : null;
|
||||
|
||||
const ACTIVITY_SYNC_COOLDOWN_MS = 3000;
|
||||
const NIGHT_VISION_SYNC_COOLDOWN_MS = 1000;
|
||||
const PERIODIC_SYNC_MS = 20000;
|
||||
|
||||
module.exports = {
|
||||
discordInvite,
|
||||
kofiLink,
|
||||
serverTimezone,
|
||||
configuredSocials,
|
||||
ACTIVITY_SYNC_COOLDOWN_MS,
|
||||
NIGHT_VISION_SYNC_COOLDOWN_MS,
|
||||
PERIODIC_SYNC_MS,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
// session Service filters
|
||||
// Purpose: Defines visibility-filter helpers for per-socket session payload shaping.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating rover/queue filtering concerns from orchestration.
|
||||
const roverManager = require('../roverManager');
|
||||
|
||||
function filterVisibleRoverId(socket, roverId) {
|
||||
if (!roverId) return null;
|
||||
return roverManager.canSeeRover(roverId, socket) ? roverId : null;
|
||||
}
|
||||
|
||||
function filterActiveDriversForSocket(activeDrivers = {}, socket) {
|
||||
const next = {};
|
||||
Object.entries(activeDrivers || {}).forEach(([roverId, socketId]) => {
|
||||
if (!roverManager.canSeeRover(roverId, socket)) return;
|
||||
next[roverId] = socketId;
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
function filterTurnQueuesForSocket(turnQueues = {}, socket) {
|
||||
const next = {};
|
||||
Object.entries(turnQueues || {}).forEach(([roverId, info]) => {
|
||||
if (!roverManager.canSeeRover(roverId, socket)) return;
|
||||
next[roverId] = info;
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterVisibleRoverId,
|
||||
filterActiveDriversForSocket,
|
||||
filterTurnQueuesForSocket,
|
||||
};
|
||||
+62
-79
@@ -1,53 +1,57 @@
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('sessionService');
|
||||
const { getRole, roleEvents } = require('./roleService');
|
||||
const { getMode, modeEvents } = require('./modeManager');
|
||||
const roverManager = require('./roverManager');
|
||||
// session Service
|
||||
// Purpose: Defines the session Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('sessionService');
|
||||
const { getRole, roleEvents } = require('../roleService');
|
||||
const { getMode, modeEvents } = require('../modeManager');
|
||||
const roverManager = require('../roverManager');
|
||||
const { managerEvents } = roverManager;
|
||||
const assignmentService = require('./assignmentService');
|
||||
const { getActiveDrivers, getTurnQueues, turnEvents } = require('./turnService');
|
||||
const { getRoomCameras, roomCameraEvents } = require('./roomCameraService');
|
||||
const { getState: getHomeAssistantState, homeAssistantEvents } = require('./homeAssistantService');
|
||||
const { getState: getNeatoState, neatoEvents } = require('./neatoService');
|
||||
const { getNickname, nicknameEvents } = require('./nicknameService');
|
||||
const assignmentService = require('../assignmentService');
|
||||
const { getActiveDrivers, getTurnQueues, turnEvents } = require('../turnService');
|
||||
const { getRoomCameras, roomCameraEvents } = require('../roomCameraService');
|
||||
const { getState: getHomeAssistantState, homeAssistantEvents } = require('../homeAssistantService');
|
||||
const { getState: getNeatoState, neatoEvents } = require('../neatoService');
|
||||
const { getNickname, nicknameEvents } = require('../nicknameService');
|
||||
const {
|
||||
getVerificationStateForSocket,
|
||||
getModerationStateForSocket,
|
||||
getIdentitySummary,
|
||||
verificationEvents,
|
||||
} = require('./verificationService');
|
||||
} = require('../verificationService');
|
||||
const {
|
||||
getStateForSocket: getPrivateRoverAccessStateForSocket,
|
||||
requestEvents: privateRoverAccessRequestEvents,
|
||||
} = require('./privateRoverAccessRequestService');
|
||||
const { getReplayState, replayEvents } = require('./replayService');
|
||||
const { getReplaySources } = require('./replaySourceService');
|
||||
const { getHealthSnapshot } = require('./healthService');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { getCommunityGoal } = require('./communityGoalService');
|
||||
const { getAdminReason } = require('./adminReasonService');
|
||||
const { subscribe } = require('./eventBus');
|
||||
const { getSocketIp, isLocalNetwork } = require('../helpers/ipResolver');
|
||||
const { getAudioForwardState, audioForwardEvents } = require('./audioForwardService');
|
||||
const { getAudioLevels, audioLevelsEvents } = require('./audioLevelsService');
|
||||
const { getButtonBoxState } = require('./buttonBoxService');
|
||||
|
||||
const config = loadConfig();
|
||||
const discordInvite = config.discord?.invite || null;
|
||||
const kofiLink = config.kofi?.link || null;
|
||||
const serverTimezone = config.timezone || null;
|
||||
const configuredSocials = Array.isArray(config.socials) ? config.socials : null;
|
||||
} = require('../privateRoverAccessRequestService');
|
||||
const { getReplayState, replayEvents } = require('../replayService');
|
||||
const { getReplaySources } = require('../replaySourceService');
|
||||
const { getHealthSnapshot } = require('../healthService');
|
||||
const { getCommunityGoal } = require('../communityGoalService');
|
||||
const { getAdminReason } = require('../adminReasonService');
|
||||
const { subscribe } = require('../eventBus');
|
||||
const { getSocketIp, isLocalNetwork } = require('../../helpers/ipResolver');
|
||||
const { getAudioForwardState, audioForwardEvents } = require('../audioForwardService');
|
||||
const { getAudioLevels, audioLevelsEvents } = require('../audioLevelsService');
|
||||
const { getButtonBoxState } = require('../buttonBoxService');
|
||||
const {
|
||||
discordInvite,
|
||||
kofiLink,
|
||||
serverTimezone,
|
||||
configuredSocials,
|
||||
ACTIVITY_SYNC_COOLDOWN_MS,
|
||||
NIGHT_VISION_SYNC_COOLDOWN_MS,
|
||||
PERIODIC_SYNC_MS,
|
||||
} = require('./constants');
|
||||
const { getState, setState } = require('./state');
|
||||
const {
|
||||
filterVisibleRoverId,
|
||||
filterActiveDriversForSocket,
|
||||
filterTurnQueuesForSocket,
|
||||
} = require('./filters');
|
||||
logger.info('Discord invite loaded:', discordInvite ? 'present' : 'not configured');
|
||||
logger.info('Ko-fi link loaded:', kofiLink ? 'present' : 'not configured');
|
||||
logger.info('Socials config loaded:', configuredSocials?.length ? `${configuredSocials.length} entries` : 'not configured');
|
||||
|
||||
const ACTIVITY_SYNC_COOLDOWN_MS = 3000;
|
||||
const NIGHT_VISION_SYNC_COOLDOWN_MS = 1000;
|
||||
let lastActivitySync = 0;
|
||||
let pendingActivitySync = null;
|
||||
let lastNightVisionSync = 0;
|
||||
let pendingNightVisionSync = null;
|
||||
|
||||
function buildUserEntry(socket) {
|
||||
if (!socket) return null;
|
||||
const role = getRole(socket);
|
||||
@@ -61,29 +65,6 @@ function buildUserEntry(socket) {
|
||||
};
|
||||
}
|
||||
|
||||
function filterVisibleRoverId(socket, roverId) {
|
||||
if (!roverId) return null;
|
||||
return roverManager.canSeeRover(roverId, socket) ? roverId : null;
|
||||
}
|
||||
|
||||
function filterActiveDriversForSocket(activeDrivers = {}, socket) {
|
||||
const next = {};
|
||||
Object.entries(activeDrivers || {}).forEach(([roverId, socketId]) => {
|
||||
if (!roverManager.canSeeRover(roverId, socket)) return;
|
||||
next[roverId] = socketId;
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
function filterTurnQueuesForSocket(turnQueues = {}, socket) {
|
||||
const next = {};
|
||||
Object.entries(turnQueues || {}).forEach(([roverId, info]) => {
|
||||
if (!roverManager.canSeeRover(roverId, socket)) return;
|
||||
next[roverId] = info;
|
||||
});
|
||||
return next;
|
||||
}
|
||||
|
||||
function buildSession(socket) {
|
||||
const users = Array.from(io.sockets.sockets.values())
|
||||
.map((sock) => buildUserEntry(sock))
|
||||
@@ -182,29 +163,30 @@ modeEvents.on('change', () => {
|
||||
});
|
||||
|
||||
managerEvents.on('rover', (event = {}) => {
|
||||
const state = getState();
|
||||
if (event.action === 'nightVision') {
|
||||
const now = Date.now();
|
||||
const elapsed = now - lastNightVisionSync;
|
||||
const elapsed = now - state.lastNightVisionSync;
|
||||
if (elapsed >= NIGHT_VISION_SYNC_COOLDOWN_MS) {
|
||||
lastNightVisionSync = now;
|
||||
setState({ lastNightVisionSync: now });
|
||||
logger.info('Night vision update; syncing all clients (immediate)');
|
||||
syncAll();
|
||||
return;
|
||||
}
|
||||
if (!pendingNightVisionSync) {
|
||||
if (!state.pendingNightVisionSync) {
|
||||
const delay = NIGHT_VISION_SYNC_COOLDOWN_MS - elapsed;
|
||||
pendingNightVisionSync = setTimeout(() => {
|
||||
lastNightVisionSync = Date.now();
|
||||
pendingNightVisionSync = null;
|
||||
const timer = setTimeout(() => {
|
||||
setState({ lastNightVisionSync: Date.now(), pendingNightVisionSync: null });
|
||||
logger.info('Night vision update; syncing all clients (delayed)');
|
||||
syncAll();
|
||||
}, delay);
|
||||
setState({ pendingNightVisionSync: timer });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (pendingNightVisionSync) {
|
||||
clearTimeout(pendingNightVisionSync);
|
||||
pendingNightVisionSync = null;
|
||||
if (state.pendingNightVisionSync) {
|
||||
clearTimeout(state.pendingNightVisionSync);
|
||||
setState({ pendingNightVisionSync: null });
|
||||
}
|
||||
logger.info('Rover roster change; syncing all clients');
|
||||
syncAll();
|
||||
@@ -244,30 +226,31 @@ turnEvents.on('activeDriver', () => {
|
||||
syncAll();
|
||||
});
|
||||
turnEvents.on('queue', (event = {}) => {
|
||||
const state = getState();
|
||||
const { reason } = event;
|
||||
if (reason === 'activity') {
|
||||
const now = Date.now();
|
||||
const elapsed = now - lastActivitySync;
|
||||
const elapsed = now - state.lastActivitySync;
|
||||
if (elapsed >= ACTIVITY_SYNC_COOLDOWN_MS) {
|
||||
lastActivitySync = now;
|
||||
setState({ lastActivitySync: now });
|
||||
logger.info('Turn activity; syncing all clients (immediate)');
|
||||
syncAll();
|
||||
return;
|
||||
}
|
||||
if (!pendingActivitySync) {
|
||||
if (!state.pendingActivitySync) {
|
||||
const delay = ACTIVITY_SYNC_COOLDOWN_MS - elapsed;
|
||||
pendingActivitySync = setTimeout(() => {
|
||||
lastActivitySync = Date.now();
|
||||
pendingActivitySync = null;
|
||||
const timer = setTimeout(() => {
|
||||
setState({ lastActivitySync: Date.now(), pendingActivitySync: null });
|
||||
logger.info('Turn activity; syncing all clients (delayed)');
|
||||
syncAll();
|
||||
}, delay);
|
||||
setState({ pendingActivitySync: timer });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (pendingActivitySync) {
|
||||
clearTimeout(pendingActivitySync);
|
||||
pendingActivitySync = null;
|
||||
if (state.pendingActivitySync) {
|
||||
clearTimeout(state.pendingActivitySync);
|
||||
setState({ pendingActivitySync: null });
|
||||
}
|
||||
logger.info('Turn queue change; syncing all clients');
|
||||
syncAll();
|
||||
@@ -345,7 +328,7 @@ audioLevelsEvents.on('change', () => {
|
||||
setInterval(() => {
|
||||
logger.info('Periodic session sync for all clients');
|
||||
syncAll();
|
||||
}, 20000);
|
||||
}, PERIODIC_SYNC_MS);
|
||||
|
||||
module.exports = {
|
||||
buildSession,
|
||||
@@ -0,0 +1,36 @@
|
||||
// session Service state
|
||||
// Purpose: Stores session sync throttling state and timer references used across event handlers.
|
||||
// Scope: Keeps runtime behavior unchanged while centralizing mutable session-sync state in one module.
|
||||
let lastActivitySync = 0;
|
||||
let pendingActivitySync = null;
|
||||
let lastNightVisionSync = 0;
|
||||
let pendingNightVisionSync = null;
|
||||
|
||||
function getState() {
|
||||
return {
|
||||
lastActivitySync,
|
||||
pendingActivitySync,
|
||||
lastNightVisionSync,
|
||||
pendingNightVisionSync,
|
||||
};
|
||||
}
|
||||
|
||||
function setState(patch = {}) {
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'lastActivitySync')) {
|
||||
lastActivitySync = patch.lastActivitySync;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'pendingActivitySync')) {
|
||||
pendingActivitySync = patch.pendingActivitySync;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'lastNightVisionSync')) {
|
||||
lastNightVisionSync = patch.lastNightVisionSync;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'pendingNightVisionSync')) {
|
||||
pendingNightVisionSync = patch.pendingNightVisionSync;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getState,
|
||||
setState,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// actions
|
||||
// Purpose: Defines the actions module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
function stopRover(roverId) {
|
||||
try {
|
||||
const { issueCommand } = require('../commandService');
|
||||
issueCommand(roverId, { type: 'drive', driveDirect: { left: 0, right: 0 } });
|
||||
issueCommand(roverId, { type: 'motors', motorPwm: { main: 0, side: 0, vacuum: 0 } });
|
||||
} catch (err) {
|
||||
// best effort; log elsewhere if needed
|
||||
}
|
||||
}
|
||||
|
||||
function removeDriverCompletely(roverId, socketId) {
|
||||
try {
|
||||
const assignmentService = require('../assignmentService');
|
||||
assignmentService.forceRelease(roverId, socketId);
|
||||
} catch (err) {
|
||||
// best effort; log elsewhere if needed
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
stopRover,
|
||||
removeDriverCompletely,
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
// constants
|
||||
// Purpose: Defines the constants module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const ALERT_COLOR = '#ff5722';
|
||||
const TURN_DURATION_MS = 60 * 1000;
|
||||
const IDLE_TIMEOUT_MS = 7 * 1000;
|
||||
const MAX_IDLE_SKIPS = 3;
|
||||
const STALE_REAPER_MS = 5000;
|
||||
|
||||
module.exports = {
|
||||
ALERT_COLOR,
|
||||
TURN_DURATION_MS,
|
||||
IDLE_TIMEOUT_MS,
|
||||
MAX_IDLE_SKIPS,
|
||||
STALE_REAPER_MS,
|
||||
};
|
||||
@@ -1,21 +1,30 @@
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const { sendAlert } = require('./alertService');
|
||||
const ALERT_COLOR = '#ff5722';
|
||||
const { MODES, getMode, modeEvents } = require('./modeManager');
|
||||
|
||||
const driverQueues = new Map(); // roverId -> { queue: [], current: socketId, timer: Timeout | null }
|
||||
const activeDrivers = new Map();
|
||||
const TURN_DURATION_MS = 60 * 1000;
|
||||
const IDLE_TIMEOUT_MS = 7 * 1000;
|
||||
const MAX_IDLE_SKIPS = 3;
|
||||
const STALE_REAPER_MS = 5000;
|
||||
const turnEvents = new EventEmitter();
|
||||
const turnDeadlines = new Map(); // roverId -> timestamp when current driver expires
|
||||
const idleDeadlines = new Map(); // roverId -> timestamp when idle skip will happen
|
||||
const idleTimers = new Map(); // roverId -> Timeout
|
||||
const idleSkips = new Map(); // roverId -> Map(socketId -> count)
|
||||
const idleDisarmed = new Map(); // roverId -> boolean, true once driver has acted this turn
|
||||
// turn Service
|
||||
// Purpose: Defines the turn Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const { sendAlert } = require('../alertService');
|
||||
const { MODES, getMode, modeEvents } = require('../modeManager');
|
||||
const {
|
||||
ALERT_COLOR,
|
||||
TURN_DURATION_MS,
|
||||
IDLE_TIMEOUT_MS,
|
||||
MAX_IDLE_SKIPS,
|
||||
STALE_REAPER_MS,
|
||||
} = require('./constants');
|
||||
const {
|
||||
driverQueues,
|
||||
activeDrivers,
|
||||
turnEvents,
|
||||
turnDeadlines,
|
||||
idleDeadlines,
|
||||
idleTimers,
|
||||
idleSkips,
|
||||
idleDisarmed,
|
||||
ensureQueue,
|
||||
setActiveDriver,
|
||||
getActiveDrivers,
|
||||
} = require('./state');
|
||||
const { stopRover, removeDriverCompletely } = require('./actions');
|
||||
|
||||
function driverAdded(roverId, socketId, force) {
|
||||
const queue = ensureQueue(roverId);
|
||||
@@ -79,13 +88,6 @@ function isQueuedDriver(roverId, socketId) {
|
||||
return queue.queue.includes(socketId);
|
||||
}
|
||||
|
||||
function ensureQueue(roverId) {
|
||||
if (!driverQueues.has(roverId)) {
|
||||
driverQueues.set(roverId, { queue: [], current: null, timer: null });
|
||||
}
|
||||
return driverQueues.get(roverId);
|
||||
}
|
||||
|
||||
function syncState(roverId) {
|
||||
const mode = getMode();
|
||||
const queue = ensureQueue(roverId);
|
||||
@@ -222,23 +224,6 @@ function advanceTurn(roverId) {
|
||||
scheduleIdleTimer(roverId);
|
||||
}
|
||||
|
||||
function setActiveDriver(roverId, socketId) {
|
||||
if (!socketId) {
|
||||
activeDrivers.delete(roverId);
|
||||
} else {
|
||||
activeDrivers.set(roverId, socketId);
|
||||
}
|
||||
turnEvents.emit('activeDriver', { roverId, socketId });
|
||||
}
|
||||
|
||||
function getActiveDrivers() {
|
||||
const map = {};
|
||||
activeDrivers.forEach((socketId, roverId) => {
|
||||
map[roverId] = socketId;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
function getTurnQueues() {
|
||||
const mode = getMode();
|
||||
const payload = {};
|
||||
@@ -254,25 +239,6 @@ function getTurnQueues() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
function stopRover(roverId) {
|
||||
try {
|
||||
const { issueCommand } = require('./commandService');
|
||||
issueCommand(roverId, { type: 'drive', driveDirect: { left: 0, right: 0 } });
|
||||
issueCommand(roverId, { type: 'motors', motorPwm: { main: 0, side: 0, vacuum: 0 } });
|
||||
} catch (err) {
|
||||
// best effort; log elsewhere if needed
|
||||
}
|
||||
}
|
||||
|
||||
function removeDriverCompletely(roverId, socketId) {
|
||||
try {
|
||||
const assignmentService = require('./assignmentService');
|
||||
assignmentService.forceRelease(roverId, socketId);
|
||||
} catch (err) {
|
||||
// best effort; log elsewhere if needed
|
||||
}
|
||||
}
|
||||
|
||||
function recordActivity(roverId, socketId) {
|
||||
const queue = driverQueues.get(roverId);
|
||||
if (!queue || queue.current !== socketId) return;
|
||||
@@ -0,0 +1,51 @@
|
||||
// state
|
||||
// Purpose: Defines the state module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const driverQueues = new Map();
|
||||
const activeDrivers = new Map();
|
||||
const turnEvents = new EventEmitter();
|
||||
const turnDeadlines = new Map();
|
||||
const idleDeadlines = new Map();
|
||||
const idleTimers = new Map();
|
||||
const idleSkips = new Map();
|
||||
const idleDisarmed = new Map();
|
||||
|
||||
function ensureQueue(roverId) {
|
||||
if (!driverQueues.has(roverId)) {
|
||||
driverQueues.set(roverId, { queue: [], current: null, timer: null });
|
||||
}
|
||||
return driverQueues.get(roverId);
|
||||
}
|
||||
|
||||
function setActiveDriver(roverId, socketId) {
|
||||
if (!socketId) {
|
||||
activeDrivers.delete(roverId);
|
||||
} else {
|
||||
activeDrivers.set(roverId, socketId);
|
||||
}
|
||||
turnEvents.emit('activeDriver', { roverId, socketId });
|
||||
}
|
||||
|
||||
function getActiveDrivers() {
|
||||
const map = {};
|
||||
activeDrivers.forEach((socketId, roverId) => {
|
||||
map[roverId] = socketId;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
driverQueues,
|
||||
activeDrivers,
|
||||
turnEvents,
|
||||
turnDeadlines,
|
||||
idleDeadlines,
|
||||
idleTimers,
|
||||
idleSkips,
|
||||
idleDisarmed,
|
||||
ensureQueue,
|
||||
setActiveDriver,
|
||||
getActiveDrivers,
|
||||
};
|
||||
+10
-7
@@ -1,13 +1,16 @@
|
||||
// verification Service
|
||||
// Purpose: Defines the verification Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const net = require('net');
|
||||
const EventEmitter = require('events');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('verificationService');
|
||||
const { publishEvent } = require('./eventBus');
|
||||
const { normalizeIp } = require('../helpers/ipResolver');
|
||||
const { getNickname, setNickname } = require('./nicknameService');
|
||||
const { getRole, roleEvents } = require('./roleService');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('verificationService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const { normalizeIp } = require('../../helpers/ipResolver');
|
||||
const { getNickname, setNickname } = require('../nicknameService');
|
||||
const { getRole, roleEvents } = require('../roleService');
|
||||
const {
|
||||
sanitizeNickname,
|
||||
normalizeCookieUserId,
|
||||
@@ -15,7 +18,7 @@ const {
|
||||
generateCookieUserId,
|
||||
getKnownIp,
|
||||
createJsonStore,
|
||||
} = require('./identityService');
|
||||
} = require('../identityService');
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
|
||||
const STORE_PATH = path.join(DATA_DIR, 'verified-users.json');
|
||||
+15
-12
@@ -1,15 +1,18 @@
|
||||
const { app } = require('../globals/http');
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('videoAuth');
|
||||
const videoSessions = require('./videoSessions');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('./roleService');
|
||||
const { isVerified } = require('./verificationService');
|
||||
const turnService = require('./turnService');
|
||||
const roverManager = require('./roverManager');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { getRequestIp, getSocketIp, isLocalNetwork } = require('../helpers/ipResolver');
|
||||
const { logAdminEvent } = require('./adminLogService');
|
||||
// video Auth Service
|
||||
// Purpose: Defines the video Auth Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { app } = require('../../globals/http');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('videoAuth');
|
||||
const videoSessions = require('../videoSessions');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('../roleService');
|
||||
const { isVerified } = require('../verificationService');
|
||||
const turnService = require('../turnService');
|
||||
const roverManager = require('../roverManager');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { getRequestIp, getSocketIp, isLocalNetwork } = require('../../helpers/ipResolver');
|
||||
const { logAdminEvent } = require('../adminLogService');
|
||||
|
||||
const config = loadConfig();
|
||||
const mediaConfig = config.media || {};
|
||||
@@ -1,5 +1,8 @@
|
||||
// video Sessions
|
||||
// Purpose: Defines the video Sessions module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const io = require('../globals/io');
|
||||
const io = require('../../globals/io');
|
||||
|
||||
const sessions = new Map(); // sessionId -> { socketId, sourceType, sourceId }
|
||||
const socketSessions = new Map(); // socketId -> Set(sessionId)
|
||||
+11
-8
@@ -1,11 +1,14 @@
|
||||
const io = require('../globals/io');
|
||||
const logger = require('../globals/logger').child('videoSocket');
|
||||
const { getMode, MODES } = require('./modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('./roleService');
|
||||
const videoSessions = require('./videoSessions');
|
||||
const roverManager = require('./roverManager');
|
||||
const { loadConfig } = require('../helpers/configLoader');
|
||||
const { getSocketIp, isLocalNetwork } = require('../helpers/ipResolver');
|
||||
// video Socket Service
|
||||
// Purpose: Defines the video Socket Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('videoSocket');
|
||||
const { getMode, MODES } = require('../modeManager');
|
||||
const { isAdmin, isLockdownAdmin, getRole } = require('../roleService');
|
||||
const videoSessions = require('../videoSessions');
|
||||
const roverManager = require('../roverManager');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { getSocketIp, isLocalNetwork } = require('../../helpers/ipResolver');
|
||||
|
||||
const config = loadConfig();
|
||||
const mediaConfig = config.media || {};
|
||||
Reference in New Issue
Block a user