mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
this is a big slop that might backfire lol... new config system and UI!
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
// Complete Configuration Definition
|
||||
// Purpose: Assembles service-owned configuration fragments into the one ordered document used by storage, validation, and the admin UI.
|
||||
// Scope: Controls top-level order and composition only; each owning service defines the meaning, defaults, and schema of its own values.
|
||||
const { strictObject } = require('./schemaHelpers');
|
||||
const sessionConfiguration = require('../services/sessionService/configuration');
|
||||
const interInstance = require('../services/interInstanceService/configuration');
|
||||
const llmCommentary = require('../services/llmCommentaryService/configuration');
|
||||
const overseerControl = require('../services/overseerControlService/configuration');
|
||||
const barcodeGames = require('../services/barcodeGameService/configuration');
|
||||
const media = require('../services/mediaMtxService/configuration');
|
||||
const bandwidthSavings = require('../helpers/bandwidthSavings.configuration');
|
||||
const audioForward = require('../services/audioForwardService/configuration');
|
||||
const audioLevels = require('../services/audioLevelsService/configuration');
|
||||
const homeAssistant = require('../services/homeAssistantService/configuration');
|
||||
const roomCameras = require('../services/roomCameraService/configuration');
|
||||
const ptzCamera = require('../services/ptzCameraService/configuration');
|
||||
const kinect = require('../services/kinectService/configuration');
|
||||
const balanceBoard = require('../services/balanceBoardService/configuration');
|
||||
const buttonBox = require('../services/buttonBoxService/configuration');
|
||||
const barcodeScanner = require('../services/barcodeScannerService/configuration');
|
||||
const commands = require('../services/operatorCommandService/configuration');
|
||||
const discord = require('../services/discordBotService/configuration');
|
||||
const fleetReports = require('../services/fleetReportService/configuration');
|
||||
|
||||
/*
|
||||
Object property order is preserved by JSON serialization and JSON Schema
|
||||
consumers. Keeping this explicit list in legacy-YAML order makes the generic
|
||||
admin form predictable without creating a second frontend ordering system.
|
||||
The session service owns three non-adjacent public-presentation values, so
|
||||
those fragments are placed independently at their historical positions.
|
||||
*/
|
||||
const definitions = [
|
||||
sessionConfiguration.timezone,
|
||||
interInstance,
|
||||
llmCommentary,
|
||||
overseerControl,
|
||||
barcodeGames,
|
||||
media,
|
||||
bandwidthSavings,
|
||||
audioForward,
|
||||
audioLevels,
|
||||
homeAssistant,
|
||||
roomCameras,
|
||||
ptzCamera,
|
||||
kinect,
|
||||
balanceBoard,
|
||||
buttonBox,
|
||||
barcodeScanner,
|
||||
commands,
|
||||
discord,
|
||||
sessionConfiguration.socials,
|
||||
sessionConfiguration.driverAd,
|
||||
fleetReports,
|
||||
];
|
||||
|
||||
const defaultConfig = Object.fromEntries(
|
||||
definitions.map(({ key, defaultValue }) => [key, defaultValue]),
|
||||
);
|
||||
const properties = Object.fromEntries(
|
||||
definitions.map(({ key, schema }) => [key, schema]),
|
||||
);
|
||||
const rootSchema = strictObject(properties, {
|
||||
title: 'Configuration',
|
||||
required: definitions.map(({ key }) => key),
|
||||
});
|
||||
|
||||
function collectFeatureDefinitions(definition, parentPath = []) {
|
||||
const configPath = [...parentPath, definition.key];
|
||||
const features = [];
|
||||
|
||||
if (definition.feature === true) {
|
||||
/*
|
||||
A feature declaration is intentionally only a boolean marker. Its public
|
||||
name is the configuration item's key and its value is that item's own
|
||||
enabled field, so a service cannot introduce a second enablement rule in
|
||||
metadata. Failing during definition assembly catches an invalid marker at
|
||||
startup instead of publishing an undefined capability to browsers.
|
||||
*/
|
||||
if (definition.schema?.properties?.enabled?.type !== 'boolean') {
|
||||
throw new Error(`Configuration feature ${definition.key} must define a boolean enabled field.`);
|
||||
}
|
||||
features.push({ key: definition.key, path: [...configPath, 'enabled'] });
|
||||
}
|
||||
|
||||
const nestedDefinitions = Array.isArray(definition.nestedDefinitions)
|
||||
? definition.nestedDefinitions
|
||||
: [];
|
||||
nestedDefinitions.forEach((nestedDefinition) => {
|
||||
features.push(...collectFeatureDefinitions(nestedDefinition, configPath));
|
||||
});
|
||||
return features;
|
||||
}
|
||||
|
||||
/*
|
||||
This derived list replaces the old hand-maintained feature registry. Top-level
|
||||
and nested configuration owners opt in beside their schema, while this module
|
||||
only preserves their already-declared document paths.
|
||||
*/
|
||||
const featureDefinitions = definitions.flatMap((definition) => collectFeatureDefinitions(definition));
|
||||
|
||||
function collectWriteOnlyPaths(schema, prefix = '') {
|
||||
/*
|
||||
Secrets are declared once, beside the service field that consumes them.
|
||||
Walking object properties produces the dotted paths needed for redaction
|
||||
and update handling without maintaining a parallel secret registry.
|
||||
*/
|
||||
if (!schema || typeof schema !== 'object') return [];
|
||||
if (schema.writeOnly === true) return prefix ? [prefix] : [];
|
||||
if (schema.type !== 'object' || !schema.properties) return [];
|
||||
return Object.entries(schema.properties).flatMap(([key, childSchema]) => (
|
||||
collectWriteOnlyPaths(childSchema, prefix ? `${prefix}.${key}` : key)
|
||||
));
|
||||
}
|
||||
|
||||
const secretPaths = collectWriteOnlyPaths(rootSchema);
|
||||
|
||||
module.exports = {
|
||||
definitions,
|
||||
defaultConfig,
|
||||
rootSchema,
|
||||
secretPaths,
|
||||
featureDefinitions,
|
||||
};
|
||||
Reference in New Issue
Block a user