Files
api/services/nginx/app/routes/departmentSelfserveStudioRoute.php
T
Jeppe Bundgaard acdff75311 Add requestBooleanFlag helper and enhance session synchronization logic
- Introduce `requestBooleanFlag` method for consistent boolean parameter handling with default values.
- Add `activate_machine` and `sync_relay_state` parameters to `synchronizeSession` for more flexible relay and machine activation control.
- Update methods, routes, and tests to integrate the new session synchronization parameters effectively.
- Enhance debugging support with additional metadata in simulation and payload captures.
2026-04-28 16:54:57 +02:00

226 lines
11 KiB
PHP

<?php
namespace routes;
use classes\authentication;
use modules\selfserve\classes\selfserve_config_versioning;
use modules\selfserve\classes\selfserve_studio_graph;
use modules\selfserve\classes\selfserve_wash_flow;
use objects\logs_o;
use traits\route_t;
class departmentSelfserveStudioRoute
{
use route_t;
public function run(): void
{
$this->get('/department/selfserve/studio/graph', function (): void {
global $response;
$user = $this->requireStudioUser('list_department_selfserve_config_versions');
self::requireParameters(['department']);
$departmentId = (int)self::getParameter('department');
$this->assertDepartmentAccess($user, $departmentId, ['view_all_department_selfserve_config_versions']);
$service = new selfserve_studio_graph();
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'GET_STUDIO_GRAPH', 'Fetched self-serve studio graph');
$response->success($service->buildGraph($departmentId, (int)$user->id, $this->studioPermissions()));
}, [
'list_department_selfserve_config_versions' => 'View the all-in-one self-serve studio graph',
'view_all_department_selfserve_config_versions' => 'View the all-in-one self-serve studio graph across departments',
]);
$this->put('/department/selfserve/studio/graph', function (): void {
global $response;
$user = $this->requireStudioUser('edit_department_selfserve_config_versions');
self::requireParameters(['department']);
$departmentId = (int)self::getParameter('department');
$this->assertDepartmentAccess($user, $departmentId);
try {
$payload = self::getParametersAsArray();
$service = new selfserve_studio_graph();
$graph = $service->applyGraphSave($departmentId, $payload, (int)$user->id, $this->studioPermissions());
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'SAVE_STUDIO_GRAPH', 'Saved self-serve studio graph');
$response->success($graph);
} catch (\RuntimeException $exception) {
$response->error($exception->getMessage(), 422);
}
}, [
'edit_department_selfserve_config_versions' => 'Create, update, delete, connect, and reorder self-serve studio graph objects',
]);
$this->put('/department/selfserve/studio/layout', function (): void {
global $response;
$user = $this->requireStudioUser('edit_department_selfserve_config_versions');
self::requireParameters(['department', 'layout']);
self::requireType(self::getParameter('layout'), self::TYPE_ARRAY());
$departmentId = (int)self::getParameter('department');
$this->assertDepartmentAccess($user, $departmentId);
try {
$layout = (new selfserve_studio_graph())->saveLayout($departmentId, (int)$user->id, (array)self::getParameter('layout'));
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'SAVE_STUDIO_LAYOUT', 'Saved self-serve studio layout');
$response->success($layout);
} catch (\RuntimeException $exception) {
$response->error($exception->getMessage(), 422);
}
}, [
'edit_department_selfserve_config_versions' => 'Save canvas-only self-serve studio layout',
]);
$this->post('/department/selfserve/studio/validate', function (): void {
global $response;
$user = $this->requireStudioUser('edit_department_selfserve_config_versions');
self::requireParameters(['department']);
$departmentId = (int)self::getParameter('department');
$this->assertDepartmentAccess($user, $departmentId);
$validation = (new selfserve_studio_graph())->validatePayload($departmentId, self::getParametersAsArray());
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'VALIDATE_STUDIO_GRAPH', 'Validated self-serve studio graph');
$response->success($validation);
}, [
'edit_department_selfserve_config_versions' => 'Validate the self-serve studio graph',
]);
$this->post('/department/selfserve/studio/simulate', function (): void {
global $response;
$user = $this->requireStudioUser('list_department_selfserve_vehicle_conditions');
self::requireParameters(['department', 'lane_id', 'reg']);
$departmentId = (int)self::getParameter('department');
$laneId = (int)self::getParameter('lane_id');
self::requireParameterIntPositive($laneId, 'lane_id');
$this->assertDepartmentAccess($user, $departmentId);
try {
$result = (new selfserve_studio_graph())->simulateGraph(
$departmentId,
self::getParametersAsArray(),
(int)$user->id,
$this->studioPermissions()
);
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'SIMULATE_STUDIO_GRAPH', 'Simulated self-serve studio graph');
$response->success($result);
} catch (\Throwable $exception) {
$response->error($exception->getMessage(), 422);
}
}, [
'list_department_selfserve_vehicle_conditions' => 'Run the self-serve studio simulator',
]);
$this->post('/department/selfserve/studio/publish', function (): void {
global $response;
$user = $this->requireStudioUser('publish_department_selfserve_config_versions');
self::requireParameters(['department']);
$departmentId = (int)self::getParameter('department');
$this->assertDepartmentAccess($user, $departmentId);
try {
$published = (new selfserve_config_versioning())->publishDraft($departmentId, (int)$user->id);
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'PUBLISH_STUDIO_GRAPH', 'Published self-serve studio graph');
$response->success($published);
} catch (\RuntimeException $exception) {
$response->error($exception->getMessage(), 422);
}
}, [
'publish_department_selfserve_config_versions' => 'Publish the self-serve studio draft',
]);
$this->post('/department/selfserve/studio/rollback', function (): void {
global $response;
$user = $this->requireStudioUser('rollback_department_selfserve_config_versions');
self::requireParameters(['department', 'target_version_id']);
$departmentId = (int)self::getParameter('department');
$targetVersionId = (int)self::getParameter('target_version_id');
self::requireParameterIntPositive($targetVersionId, 'target_version_id');
$this->assertDepartmentAccess($user, $departmentId);
try {
$rolledBack = (new selfserve_config_versioning())->rollbackToVersion($departmentId, $targetVersionId, (int)$user->id);
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'ROLLBACK_STUDIO_GRAPH', 'Rolled back self-serve studio graph');
$response->success($rolledBack);
} catch (\RuntimeException $exception) {
$response->error($exception->getMessage(), 422);
}
}, [
'rollback_department_selfserve_config_versions' => 'Rollback the self-serve studio draft to an earlier version',
]);
$this->post('/department/selfserve/studio/gateway-action', function (): void {
global $response;
$user = $this->requireStudioUser('modules_shelly_config');
self::requireParameters(['department', 'gateway_id', 'action']);
$departmentId = (int)self::getParameter('department');
$gatewayId = (int)self::getParameter('gateway_id');
self::requireParameterIntPositive($gatewayId, 'gateway_id');
$this->assertDepartmentAccess($user, $departmentId);
$action = strtolower((string)self::getParameter('action'));
$confirmed = filter_var(self::getParameter('confirm'), FILTER_VALIDATE_BOOLEAN);
if (in_array($action, ['uninstall', 'rotate_credentials'], true) && $confirmed !== true) {
$response->error('This gateway action requires explicit confirmation.', 428);
}
try {
$payload = self::getParametersAsArray();
$result = (new selfserve_studio_graph())->runGatewayAction($departmentId, $gatewayId, $action, $payload, (int)$user->id);
(new logs_o())->add('selfserve_studio', $departmentId, 1, $user->id, 'GATEWAY_STUDIO_ACTION', 'Ran self-serve studio gateway action: ' . $action);
$response->success($result);
} catch (\Throwable $exception) {
$response->error($exception->getMessage(), 422);
}
}, [
'modules_shelly_config' => 'Run permission-gated self-serve studio edge gateway actions',
]);
}
private function requireStudioUser(string $permission): object
{
global $response;
$this->requirePermission($permission);
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
}
return $user;
}
/**
* @param array<int,string> $bypassPermissions
*/
private function assertDepartmentAccess(object $user, int $departmentId, array $bypassPermissions = []): void
{
$authorizedDepartmentIds = $user->getGroup()->getDepartments();
if (in_array($departmentId, $authorizedDepartmentIds, true)) {
return;
}
foreach ($bypassPermissions as $permission) {
if ($this->hasPermission($permission)) {
return;
}
}
$this->forbidDepartmentAccess($departmentId, $bypassPermissions);
}
/**
* @return array<string,bool>
*/
private function studioPermissions(): array
{
return [
'can_view' => $this->hasPermission('list_department_selfserve_config_versions'),
'can_edit' => $this->hasPermission('edit_department_selfserve_config_versions'),
'can_publish' => $this->hasPermission('publish_department_selfserve_config_versions'),
'can_rollback' => $this->hasPermission('rollback_department_selfserve_config_versions'),
'can_simulate' => $this->hasPermission('list_department_selfserve_vehicle_conditions'),
'modules_shelly_config' => $this->hasPermission('modules_shelly_config'),
'can_manage_gateways' => $this->hasPermission('modules_shelly_config'),
'can_run_gateway_destructive_actions' => $this->hasPermission('modules_shelly_config'),
'can_run_live_lane_actions' => $this->hasPermission('modules_selfserve_sessions_force_stop'),
];
}
}