Add batch processing for lane hardware commands and status retrieval

This commit is contained in:
Jeppe Bundgaard
2026-06-30 09:30:43 +02:00
parent 5f13242cfa
commit 0da02dfeb5
10 changed files with 1914 additions and 2 deletions
+58
View File
@@ -754,6 +754,60 @@ export async function setRelayState(payload, fetchImpl = fetch) {
}
}
async function mapWithConcurrency(items, limit, mapper) {
const results = new Array(items.length);
let nextIndex = 0;
const workerCount = Math.max(1, Math.min(Number(limit) || 1, items.length || 1));
await Promise.all(Array.from({ length: workerCount }, async () => {
while (nextIndex < items.length) {
const index = nextIndex;
nextIndex += 1;
results[index] = await mapper(items[index], index);
}
}));
return results;
}
async function executeRelayBatch(command, handler, fetchImpl = fetch) {
const commands = Array.isArray(command?.payload?.commands)
? command.payload.commands
: Array.isArray(command?.commands)
? command.commands
: [];
const concurrency = Math.max(1, Math.min(Number(command?.payload?.concurrency || command?.concurrency || 5), 5));
const results = await mapWithConcurrency(commands, concurrency, async (entry = {}) => {
const target = String(entry.target || entry.relay || "");
const relayId = String(entry.relayId || entry.relay_id || "");
try {
const payload = await handler(entry, fetchImpl);
return {
target,
relayId,
relay_id: relayId,
ok: true,
payload,
};
} catch (error) {
return {
target,
relayId,
relay_id: relayId,
ok: false,
error: error instanceof Error ? error.message : String(error),
};
}
});
return {
batchId: command?.payload?.batchId || command?.payload?.batch_id || command?.batchId || command?.batch_id || null,
batch_id: command?.payload?.batch_id || command?.payload?.batchId || command?.batch_id || command?.batchId || null,
results,
};
}
async function fetchArtifactBuffer(url, expectedSha256, label, fetchImpl = fetch) {
if (!url) {
return null;
@@ -1416,6 +1470,10 @@ export async function handleAgentCommand(command, deps = {}) {
return await getRelayStatus(command.payload || {}, fetchImpl);
case "SET_RELAY_STATE":
return await setRelayState(command.payload || {}, fetchImpl);
case "BATCH_RELAY_STATUS":
return await executeRelayBatch(command, getRelayStatus, fetchImpl);
case "BATCH_SET_RELAY_STATE":
return await executeRelayBatch(command, setRelayState, fetchImpl);
case "RUN_UPDATE":
return await runUpdate(command.payload || {}, fetchImpl, deps);
case "UNINSTALL_AGENT":
+33
View File
@@ -127,6 +127,39 @@ test("relay status and switch commands support both Shelly RPC and legacy endpoi
assert.equal(switched.on, false);
});
test("batch relay commands return per-relay results without failing the whole batch", async () => {
const fakeFetch = async (url) => {
const value = String(url);
if (value.includes("10.1.0.31")) {
return {
ok: true,
async json() {
return { output: true };
},
};
}
throw new Error("relay offline");
};
const result = await handleAgentCommand({
commandType: "BATCH_SET_RELAY_STATE",
payload: {
batch_id: "batch-1",
commands: [
{ target: "MACHINE", relayId: "relay-machine", localIp: "10.1.0.31", channel: 0, on: true },
{ target: "EXIT", relayId: "relay-out", localIp: "10.1.0.32", channel: 0, on: true },
],
},
}, { fetchImpl: fakeFetch });
assert.equal(result.batch_id, "batch-1");
assert.equal(result.results.length, 2);
assert.equal(result.results[0].ok, true);
assert.equal(result.results[0].target, "MACHINE");
assert.equal(result.results[1].ok, false);
assert.match(result.results[1].error, /relay offline/);
});
test("Shelly discovery infers Gen3 from S3 relay model codes when generation is omitted", async () => {
const inventory = await discoverShellyDevices({ candidateIps: ["192.168.1.2"] }, async (url) => {
assert.equal(String(url), "http://192.168.1.2/shelly");
File diff suppressed because one or more lines are too long
@@ -12452,3 +12452,875 @@
[Tue Jun 2 14:16:35 2026] 127.0.0.1:55074 Closing
[Tue Jun 2 14:17:06 2026] 127.0.0.1:34490 Accepted
[Tue Jun 2 14:17:06 2026] 127.0.0.1:34490 Closing
[Thu Jun 4 05:49:23 2026] PHP 8.2.15 Development Server (http://127.0.0.1:41145) started
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56234 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56234 Closing
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56242 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56242 Closing
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56250 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56250 Closing
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56258 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56258 Closing
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56272 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56272 Closing
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56278 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56278 Closing
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56288 Accepted
[Thu Jun 4 05:49:24 2026] 127.0.0.1:56288 Closing
[Thu Jun 4 05:49:46 2026] PHP 8.2.15 Development Server (http://127.0.0.1:45671) started
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37442 Accepted
[Thu Jun 4 05:49:47 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37442 Closing
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37444 Accepted
[Thu Jun 4 05:49:47 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37444 Closing
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37448 Accepted
[Thu Jun 4 05:49:47 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37448 Closing
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37462 Accepted
[Thu Jun 4 05:49:47 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:47 2026] 127.0.0.1:37462 Closing
[Thu Jun 4 05:49:57 2026] PHP 8.2.15 Development Server (http://127.0.0.1:33783) started
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59700 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59700 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59702 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59702 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59708 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59708 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59722 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59722 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59732 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 05:49:58 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59732 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59738 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59738 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59752 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59752 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59756 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 05:49:58 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59756 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59762 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59762 Closing
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59772 Accepted
[Thu Jun 4 05:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:58 2026] 127.0.0.1:59772 Closing
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59784 Accepted
[Thu Jun 4 05:49:59 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59784 Closing
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59790 Accepted
[Thu Jun 4 05:49:59 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59790 Closing
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59806 Accepted
[Thu Jun 4 05:49:59 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59806 Closing
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59810 Accepted
[Thu Jun 4 05:49:59 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 05:49:59 2026] 127.0.0.1:59810 Closing
[Thu Jun 4 06:06:51 2026] PHP 8.2.15 Development Server (http://127.0.0.1:36081) started
[Thu Jun 4 06:06:51 2026] 127.0.0.1:34670 Accepted
[Thu Jun 4 06:06:51 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:51 2026] 127.0.0.1:34670 Closing
[Thu Jun 4 06:06:51 2026] 127.0.0.1:34674 Accepted
[Thu Jun 4 06:06:51 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:51 2026] 127.0.0.1:34674 Closing
[Thu Jun 4 06:06:51 2026] 127.0.0.1:58440 Accepted
[Thu Jun 4 06:06:51 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:51 2026] 127.0.0.1:58440 Closing
[Thu Jun 4 06:06:51 2026] 127.0.0.1:58454 Accepted
[Thu Jun 4 06:06:51 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58454 Closing
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58466 Accepted
[Thu Jun 4 06:06:52 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:06:52 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58466 Closing
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58478 Accepted
[Thu Jun 4 06:06:52 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58478 Closing
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58492 Accepted
[Thu Jun 4 06:06:52 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58492 Closing
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58502 Accepted
[Thu Jun 4 06:06:52 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:06:52 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58502 Closing
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58512 Accepted
[Thu Jun 4 06:06:52 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58512 Closing
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58522 Accepted
[Thu Jun 4 06:06:52 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:06:52 2026] 127.0.0.1:58522 Closing
[Thu Jun 4 06:07:21 2026] 127.0.0.1:56322 Accepted
[Thu Jun 4 06:07:21 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:07:21 2026] 127.0.0.1:56322 Closing
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56336 Accepted
[Thu Jun 4 06:07:22 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56336 Closing
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56352 Accepted
[Thu Jun 4 06:07:22 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56352 Closing
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56362 Accepted
[Thu Jun 4 06:07:22 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56362 Closing
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56374 Accepted
[Thu Jun 4 06:07:22 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:07:22 2026] 127.0.0.1:56374 Closing
[Thu Jun 4 06:18:18 2026] PHP 8.2.15 Development Server (http://127.0.0.1:45139) started
[Thu Jun 4 06:18:18 2026] 127.0.0.1:40632 Accepted
[Thu Jun 4 06:18:18 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:18 2026] 127.0.0.1:40632 Closing
[Thu Jun 4 06:18:18 2026] 127.0.0.1:40646 Accepted
[Thu Jun 4 06:18:18 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40646 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40658 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40658 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40672 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40672 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40674 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:18:19 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40674 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40684 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40684 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40686 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40686 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40700 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:18:19 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40700 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40702 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40702 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40714 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40714 Closing
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40722 Accepted
[Thu Jun 4 06:18:19 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:19 2026] 127.0.0.1:40722 Closing
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40736 Accepted
[Thu Jun 4 06:18:20 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40736 Closing
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40752 Accepted
[Thu Jun 4 06:18:20 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40752 Closing
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40760 Accepted
[Thu Jun 4 06:18:20 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40760 Closing
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40764 Accepted
[Thu Jun 4 06:18:20 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40764 Closing
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40768 Accepted
[Thu Jun 4 06:18:20 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:20 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:18:20 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:18:20 2026] 127.0.0.1:40768 Closing
[Thu Jun 4 06:18:21 2026] 127.0.0.1:40774 Accepted
[Thu Jun 4 06:18:21 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:18:21 2026] 127.0.0.1:40774 Closing
[Thu Jun 4 06:31:38 2026] PHP 8.2.15 Development Server (http://127.0.0.1:40389) started
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60850 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60850 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60856 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60856 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60858 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60858 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60872 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60872 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60874 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:31:38 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60874 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60890 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60890 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60904 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60904 Closing
[Thu Jun 4 06:31:38 2026] 127.0.0.1:60916 Accepted
[Thu Jun 4 06:31:38 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:38 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:31:38 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60916 Closing
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60926 Accepted
[Thu Jun 4 06:31:39 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60926 Closing
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60936 Accepted
[Thu Jun 4 06:31:39 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60936 Closing
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60946 Accepted
[Thu Jun 4 06:31:39 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60946 Closing
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60954 Accepted
[Thu Jun 4 06:31:39 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60954 Closing
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60960 Accepted
[Thu Jun 4 06:31:39 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60960 Closing
[Thu Jun 4 06:31:39 2026] 127.0.0.1:60964 Accepted
[Thu Jun 4 06:31:39 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60964 Closing
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60966 Accepted
[Thu Jun 4 06:31:40 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60966 Closing
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60982 Accepted
[Thu Jun 4 06:31:40 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:40 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:31:40 2026] Failed to log lane action: Table 'nnks_db_test_clone.module_usage_logs' doesn't exist
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60982 Closing
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60990 Accepted
[Thu Jun 4 06:31:40 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:40 2026] 127.0.0.1:60990 Closing
[Thu Jun 4 06:31:40 2026] 127.0.0.1:32770 Accepted
[Thu Jun 4 06:31:40 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Thu Jun 4 06:31:40 2026] 127.0.0.1:32770 Closing
[Fri Jun 12 12:04:15 2026] PHP 8.2.15 Development Server (http://127.0.0.1:44515) started
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34204 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34204 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34210 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34210 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34214 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34214 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34226 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34226 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34228 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34228 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34238 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34238 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34244 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34244 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34260 Accepted
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34260 Closing
[Fri Jun 12 12:04:15 2026] 127.0.0.1:34274 Accepted
[Fri Jun 12 12:04:16 2026] 127.0.0.1:34274 Closing
[Fri Jun 12 12:04:16 2026] 127.0.0.1:34280 Accepted
[Fri Jun 12 12:04:16 2026] 127.0.0.1:34280 Closing
[Fri Jun 12 12:04:16 2026] 127.0.0.1:34288 Accepted
[Fri Jun 12 12:04:16 2026] 127.0.0.1:34288 Closing
[Fri Jun 12 12:04:16 2026] 127.0.0.1:34290 Accepted
[Fri Jun 12 12:04:17 2026] 127.0.0.1:34290 Closing
[Fri Jun 12 12:04:17 2026] 127.0.0.1:34296 Accepted
[Fri Jun 12 12:04:18 2026] 127.0.0.1:34296 Closing
[Fri Jun 12 12:04:18 2026] 127.0.0.1:34304 Accepted
[Fri Jun 12 12:04:19 2026] 127.0.0.1:34304 Closing
[Fri Jun 12 12:04:19 2026] 127.0.0.1:34314 Accepted
[Fri Jun 12 12:04:20 2026] 127.0.0.1:34314 Closing
[Fri Jun 12 12:19:47 2026] PHP 8.2.15 Development Server (http://127.0.0.1:35009) started
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57422 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57422 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57428 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57428 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57444 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57444 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57454 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57454 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57470 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57470 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57472 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57472 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57478 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57478 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57488 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57488 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57496 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57496 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57510 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57510 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57520 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57520 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57522 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57522 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57536 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57536 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57548 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57548 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57550 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57550 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57566 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57566 Closing
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57582 Accepted
[Fri Jun 12 12:19:47 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:19:47 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:19:47 2026] 127.0.0.1:57582 Closing
[Fri Jun 12 12:23:27 2026] PHP 8.2.15 Development Server (http://127.0.0.1:33981) started
[Fri Jun 12 12:23:27 2026] 127.0.0.1:56426 Accepted
[Fri Jun 12 12:23:27 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:23:27 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:23:27 2026] 127.0.0.1:56426 Closing
[Fri Jun 12 12:23:27 2026] 127.0.0.1:56432 Accepted
[Fri Jun 12 12:23:27 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:23:27 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:23:27 2026] 127.0.0.1:56432 Closing
[Fri Jun 12 12:23:27 2026] 127.0.0.1:56438 Accepted
[Fri Jun 12 12:23:27 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:23:27 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:23:27 2026] 127.0.0.1:56438 Closing
[Fri Jun 12 12:39:31 2026] PHP 8.2.15 Development Server (http://127.0.0.1:43643) started
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58744 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58744 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58758 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58758 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58764 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58764 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58774 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58774 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58782 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58782 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58788 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58788 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58804 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58804 Closing
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58818 Accepted
[Fri Jun 12 12:39:31 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:31 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:31 2026] 127.0.0.1:58818 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58830 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58830 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58834 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58834 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58838 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58838 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58846 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58846 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58862 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58862 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58872 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58872 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58884 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58884 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58890 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58890 Closing
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58896 Accepted
[Fri Jun 12 12:39:32 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:32 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:32 2026] 127.0.0.1:58896 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58898 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58898 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58912 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58912 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58918 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58918 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58932 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58932 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58944 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58944 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58956 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58956 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58962 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58962 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58976 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58976 Closing
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58982 Accepted
[Fri Jun 12 12:39:33 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:33 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:33 2026] 127.0.0.1:58982 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:58998 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:58998 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59004 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59004 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59012 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59012 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59028 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59028 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59040 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59040 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59046 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59046 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59052 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59052 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59062 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59062 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59074 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59074 Closing
[Fri Jun 12 12:39:34 2026] 127.0.0.1:59078 Accepted
[Fri Jun 12 12:39:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59078 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59080 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59080 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59090 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59090 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59100 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59100 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59102 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59102 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59106 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59106 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59120 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59120 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59122 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59122 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59136 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59136 Closing
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59144 Accepted
[Fri Jun 12 12:39:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:35 2026] 127.0.0.1:59144 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59158 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59158 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59162 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59162 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59176 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59176 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59190 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59190 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59202 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59202 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59204 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59204 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59212 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59212 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59222 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59222 Closing
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59224 Accepted
[Fri Jun 12 12:39:36 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:36 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:36 2026] 127.0.0.1:59224 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59226 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59226 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59230 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59230 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59232 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59232 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59248 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59248 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59254 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59254 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59270 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59270 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59276 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59276 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59290 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59290 Closing
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59300 Accepted
[Fri Jun 12 12:39:37 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:37 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:37 2026] 127.0.0.1:59300 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59316 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59316 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59326 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59326 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59330 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59330 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59332 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59332 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59338 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59338 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59350 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59350 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59356 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59356 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59366 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59366 Closing
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59382 Accepted
[Fri Jun 12 12:39:38 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:38 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:38 2026] 127.0.0.1:59382 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59392 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59392 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59408 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59408 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59410 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59410 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59426 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59426 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59430 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59430 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59444 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59444 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59452 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59452 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59458 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59458 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59462 Accepted
[Fri Jun 12 12:39:39 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:39 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59462 Closing
[Fri Jun 12 12:39:39 2026] 127.0.0.1:59470 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59470 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59484 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59484 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59488 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59488 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59496 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59496 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59512 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59512 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59516 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59516 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59524 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59524 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59526 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59526 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59534 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59534 Closing
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59536 Accepted
[Fri Jun 12 12:39:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:40 2026] 127.0.0.1:59536 Closing
[Fri Jun 12 12:39:41 2026] 127.0.0.1:42172 Accepted
[Fri Jun 12 12:39:41 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:39:41 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:39:41 2026] 127.0.0.1:42172 Closing
[Fri Jun 12 12:41:48 2026] PHP 8.2.15 Development Server (http://127.0.0.1:45327) started
[Fri Jun 12 12:41:48 2026] 127.0.0.1:49112 Accepted
[Fri Jun 12 12:41:48 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:41:48 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:41:48 2026] 127.0.0.1:49112 Closing
[Fri Jun 12 12:42:48 2026] PHP 8.2.15 Development Server (http://127.0.0.1:44757) started
[Fri Jun 12 12:42:48 2026] 127.0.0.1:44444 Accepted
[Fri Jun 12 12:42:48 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:42:48 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:42:48 2026] 127.0.0.1:44444 Closing
[Fri Jun 12 12:43:43 2026] PHP 8.2.15 Development Server (http://127.0.0.1:43731) started
[Fri Jun 12 12:43:43 2026] 127.0.0.1:40338 Accepted
[Fri Jun 12 12:43:43 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:43:43 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:43:43 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 12:43:43 2026] 127.0.0.1:40338 Closing
[Fri Jun 12 12:43:43 2026] 127.0.0.1:40354 Accepted
[Fri Jun 12 12:43:43 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:43:43 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:43:43 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 12:43:43 2026] 127.0.0.1:40354 Closing
[Fri Jun 12 12:43:43 2026] 127.0.0.1:40358 Accepted
[Fri Jun 12 12:43:43 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:43:43 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:43:43 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 12:43:43 2026] 127.0.0.1:40358 Closing
[Fri Jun 12 12:53:28 2026] PHP 8.2.15 Development Server (http://127.0.0.1:41773) started
[Fri Jun 12 12:53:29 2026] 127.0.0.1:59496 Accepted
[Fri Jun 12 12:53:29 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:53:29 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:53:29 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 12:53:29 2026] 127.0.0.1:59496 Closing
[Fri Jun 12 12:53:29 2026] 127.0.0.1:59506 Accepted
[Fri Jun 12 12:53:29 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 12:53:29 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 12:53:29 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 12:53:29 2026] 127.0.0.1:59506 Closing
[Fri Jun 12 13:42:40 2026] PHP 8.2.15 Development Server (http://127.0.0.1:45259) started
[Fri Jun 12 13:42:40 2026] 127.0.0.1:50638 Accepted
[Fri Jun 12 13:42:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:42:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:42:40 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:42:40 2026] 127.0.0.1:50638 Closing
[Fri Jun 12 13:42:40 2026] 127.0.0.1:50644 Accepted
[Fri Jun 12 13:42:40 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:42:40 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:42:40 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:42:40 2026] 127.0.0.1:50644 Closing
[Fri Jun 12 13:45:11 2026] PHP 8.2.15 Development Server (http://127.0.0.1:43693) started
[Fri Jun 12 13:45:12 2026] 127.0.0.1:59572 Accepted
[Fri Jun 12 13:45:12 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:45:12 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:45:12 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:45:12 2026] 127.0.0.1:59572 Closing
[Fri Jun 12 13:45:12 2026] 127.0.0.1:59578 Accepted
[Fri Jun 12 13:45:12 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:45:12 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:45:12 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:45:12 2026] 127.0.0.1:59578 Closing
[Fri Jun 12 13:45:12 2026] PHP 8.2.15 Development Server (http://127.0.0.1:37353) started
[Fri Jun 12 13:45:12 2026] 127.0.0.1:37666 Accepted
[Fri Jun 12 13:45:12 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:45:12 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:45:12 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:45:12 2026] 127.0.0.1:37666 Closing
[Fri Jun 12 13:45:12 2026] 127.0.0.1:37676 Accepted
[Fri Jun 12 13:45:12 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:45:12 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:45:12 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:45:12 2026] 127.0.0.1:37676 Closing
[Fri Jun 12 13:49:21 2026] PHP 8.2.15 Development Server (http://127.0.0.1:32781) started
[Fri Jun 12 13:49:21 2026] 127.0.0.1:57936 Accepted
[Fri Jun 12 13:49:21 2026] 127.0.0.1:57936 Closing
[Fri Jun 12 13:49:21 2026] 127.0.0.1:57940 Accepted
[Fri Jun 12 13:49:21 2026] 127.0.0.1:57940 Closing
[Fri Jun 12 13:49:22 2026] PHP 8.2.15 Development Server (http://127.0.0.1:33723) started
[Fri Jun 12 13:49:22 2026] 127.0.0.1:60016 Accepted
[Fri Jun 12 13:49:22 2026] 127.0.0.1:60016 Closing
[Fri Jun 12 13:49:22 2026] 127.0.0.1:60018 Accepted
[Fri Jun 12 13:49:22 2026] 127.0.0.1:60018 Closing
[Fri Jun 12 13:49:57 2026] PHP 8.2.15 Development Server (http://127.0.0.1:34549) started
[Fri Jun 12 13:49:57 2026] 127.0.0.1:59336 Accepted
[Fri Jun 12 13:49:57 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:49:57 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:49:57 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:49:57 2026] 127.0.0.1:59336 Closing
[Fri Jun 12 13:49:57 2026] 127.0.0.1:59342 Accepted
[Fri Jun 12 13:49:57 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:49:57 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:49:57 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:49:58 2026] 127.0.0.1:59342 Closing
[Fri Jun 12 13:49:58 2026] PHP 8.2.15 Development Server (http://127.0.0.1:36181) started
[Fri Jun 12 13:49:58 2026] 127.0.0.1:39290 Accepted
[Fri Jun 12 13:49:58 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:49:58 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:49:58 2026] 127.0.0.1:39290 Closing
[Fri Jun 12 13:49:58 2026] 127.0.0.1:39302 Accepted
[Fri Jun 12 13:49:58 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 13:49:58 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 13:49:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 13:49:58 2026] 127.0.0.1:39302 Closing
[Fri Jun 12 14:03:34 2026] PHP 8.2.15 Development Server (http://127.0.0.1:40101) started
[Fri Jun 12 14:03:34 2026] 127.0.0.1:59406 Accepted
[Fri Jun 12 14:03:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:03:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:03:34 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:03:34 2026] 127.0.0.1:59406 Closing
[Fri Jun 12 14:03:34 2026] 127.0.0.1:59414 Accepted
[Fri Jun 12 14:03:34 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:03:34 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:03:34 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:03:34 2026] 127.0.0.1:59414 Closing
[Fri Jun 12 14:03:35 2026] PHP 8.2.15 Development Server (http://127.0.0.1:33543) started
[Fri Jun 12 14:03:35 2026] 127.0.0.1:60992 Accepted
[Fri Jun 12 14:03:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:03:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:03:35 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:03:35 2026] 127.0.0.1:60992 Closing
[Fri Jun 12 14:03:35 2026] 127.0.0.1:60994 Accepted
[Fri Jun 12 14:03:35 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:03:35 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:03:35 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:03:35 2026] 127.0.0.1:60994 Closing
[Fri Jun 12 14:05:57 2026] PHP 8.2.15 Development Server (http://127.0.0.1:46061) started
[Fri Jun 12 14:05:57 2026] 127.0.0.1:38780 Accepted
[Fri Jun 12 14:05:57 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:05:57 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:05:57 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:05:57 2026] 127.0.0.1:38780 Closing
[Fri Jun 12 14:05:57 2026] 127.0.0.1:38792 Accepted
[Fri Jun 12 14:05:57 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:05:57 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:05:57 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:05:57 2026] 127.0.0.1:38792 Closing
[Fri Jun 12 14:05:57 2026] PHP 8.2.15 Development Server (http://127.0.0.1:34023) started
[Fri Jun 12 14:05:58 2026] 127.0.0.1:59736 Accepted
[Fri Jun 12 14:05:58 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:05:58 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:05:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:05:58 2026] 127.0.0.1:59736 Closing
[Fri Jun 12 14:05:58 2026] 127.0.0.1:59748 Accepted
[Fri Jun 12 14:05:58 2026] [config] Economic API is missing required credentials. Set ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN, then recreate php containers.
[Fri Jun 12 14:05:58 2026] [config] ECONOMIC_API_APP_ACCESS_GRANT2 is not set. Secondary e-conomic token requests will fall back to ECONOMIC_API_APP_ACCESS_GRANT.
[Fri Jun 12 14:05:58 2026] [replication-bootstrap] Could not sync startup failover metadata: Class "replication_bootstrap_config" not found
[Fri Jun 12 14:05:58 2026] 127.0.0.1:59748 Closing
@@ -38,6 +38,8 @@ class gateway_shelly_transport implements shelly_transport_i
return match ($endpoint) {
'/v2/devices/api/get' => $this->handleGetStates($department_id, $data),
'/v2/devices/api/set/switch' => $this->handleSetSwitch($department_id, $data),
'/v2/devices/api/batch/get' => $this->handleBatchGetStates($department_id, $data),
'/v2/devices/api/batch/set/switch' => $this->handleBatchSetSwitch($department_id, $data),
default => throw new Exception('Unsupported gateway Shelly transport endpoint: ' . $endpoint),
};
}
@@ -95,6 +97,52 @@ class gateway_shelly_transport implements shelly_transport_i
return [$this->normalizeRelayPayload($logicalRelayId, $status)];
}
/**
* @throws Exception
*/
private function handleBatchGetStates(int $departmentId, array $data): array
{
$requests = [];
foreach ((array)($data['commands'] ?? $data['targets'] ?? []) as $entry) {
$command = is_array($entry) ? $entry : ['relay_id' => $entry];
$relayId = trim((string)($command['relay_id'] ?? $command['relayId'] ?? $command['id'] ?? ''));
if ($relayId === '') {
continue;
}
$requests[] = [
'target' => strtoupper(trim((string)($command['target'] ?? $relayId))),
'relay_id' => $relayId,
];
}
return $this->manager()->queueRelayStatusBatch($departmentId, $requests, null, $this->localOnly);
}
/**
* @throws Exception
*/
private function handleBatchSetSwitch(int $departmentId, array $data): array
{
$requests = [];
foreach ((array)($data['commands'] ?? []) as $entry) {
if (!is_array($entry)) {
continue;
}
$relayId = trim((string)($entry['relay_id'] ?? $entry['relayId'] ?? $entry['id'] ?? ''));
if ($relayId === '') {
continue;
}
$requests[] = [
'target' => strtoupper(trim((string)($entry['target'] ?? $relayId))),
'relay_id' => $relayId,
'on' => (bool)($entry['on'] ?? false),
'toggle_after' => $entry['toggle_after'] ?? $entry['toggleAfter'] ?? $entry['timer'] ?? null,
];
}
return $this->manager()->queueRelaySwitchBatch($departmentId, $requests, null, $this->localOnly);
}
/**
* @param array<string,mixed> $status
* @return array<string,mixed>
@@ -23,6 +23,8 @@ class edge_gateway_manager
public const DELIVERY_CHANNEL_BROKER = 'BROKER_FAST_PATH';
public const DELIVERY_CHANNEL_API = 'API_POLLING';
public const DELIVERY_CHANNEL_CLOUD = 'CLOUD';
public const COMMAND_BATCH_RELAY_STATUS = 'BATCH_RELAY_STATUS';
public const COMMAND_BATCH_SET_RELAY_STATE = 'BATCH_SET_RELAY_STATE';
public const RELAY_FALLBACK_PREFER_LOCAL = 'PREFER_LOCAL';
public const RELAY_FALLBACK_LOCAL_ONLY = 'LOCAL_ONLY';
public const RELAY_FALLBACK_CLOUD_ONLY = 'CLOUD_ONLY';
@@ -68,6 +70,11 @@ class edge_gateway_manager
public const COMMAND_POLL_INTERVAL_MICROSECONDS = 250000;
public const COMMAND_DISPATCH_STALE_AFTER_SECONDS = 30;
public const COMMAND_EXPIRES_AFTER_SECONDS = 90;
public const RELAY_BATCH_DEDUPE_SECONDS = 30;
public const RELAY_STATUS_RATE_LIMIT_PER_MINUTE = 60;
public const RELAY_SET_RATE_LIMIT_PER_MINUTE = 30;
public const RELAY_GATE_RATE_LIMIT_PER_MINUTE = 4;
public const RELAY_OPERATIONAL_WINDOW_SECONDS = 900;
public const BROKER_PRESENCE_TTL_SECONDS = 90;
public const BROKER_CONNECTIVITY_DEGRADED_AFTER_SECONDS = 45;
public const BROKER_HTTP_TIMEOUT_SECONDS = 5;
@@ -1127,6 +1134,179 @@ class edge_gateway_manager
);
}
/**
* @param array<int,array<string,mixed>> $requests
* @throws Exception
*/
public function queueRelayStatusBatch(
int $departmentId,
array $requests,
?int $userId = null,
bool $requireFastLocalPath = false
): array {
return $this->queueRelayBatch(
$departmentId,
self::COMMAND_BATCH_RELAY_STATUS,
$requests,
$userId,
$requireFastLocalPath
);
}
/**
* @param array<int,array<string,mixed>> $requests
* @throws Exception
*/
public function queueRelaySwitchBatch(
int $departmentId,
array $requests,
?int $userId = null,
bool $requireFastLocalPath = false
): array {
return $this->queueRelayBatch(
$departmentId,
self::COMMAND_BATCH_SET_RELAY_STATE,
$requests,
$userId,
$requireFastLocalPath
);
}
/**
* @return array<string,mixed>
* @throws Exception
*/
public function relayBatchStatus(string $batchId): array
{
$batchId = trim($batchId);
if ($batchId === '' || !preg_match('/^[a-f0-9]{32}$/', $batchId)) {
throw new Exception('Invalid relay batch id');
}
$statement = db::getPDO()->prepare(
"SELECT id
FROM edge_gateway_command_jobs
WHERE deleted_at IS NULL
AND JSON_UNQUOTE(JSON_EXTRACT(delivery_json, '$.batch_id')) = :batch_id
ORDER BY id ASC
LIMIT 10"
);
$statement->execute([':batch_id' => $batchId]);
$jobs = [];
foreach ($statement->fetchAll() ?: [] as $row) {
$jobId = (int)($row['id'] ?? 0);
if ($jobId > 0) {
$jobs[] = (new edge_gateway_command_jobs_o())->select($jobId);
}
}
if ($jobs === []) {
throw new Exception('Relay batch not found');
}
$items = [];
$pending = 0;
$failed = 0;
$completed = 0;
$unknownOutcomes = 0;
foreach ($jobs as $job) {
if (!$job->exists()) {
continue;
}
$delivery = (array)($job->delivery_json->value() ?? []);
if (
(string)$job->status->value() === 'DISPATCHING'
&& !empty($delivery['no_auto_retry'])
&& self::parseApplicationDateTime((string)($job->updated_at->value() ?? $job->requested_at->value())) <= time() - self::COMMAND_DISPATCH_STALE_AFTER_SECONDS
) {
$this->finalizeCommandJob(
$job,
false,
[],
'Edge gateway command outcome is unknown after dispatch interruption; not retrying non-idempotent gate pulse',
null,
'TIMED_OUT'
);
}
$status = strtoupper((string)$job->status->value());
$outcomeUnknown = $status === 'TIMED_OUT' && !empty($delivery['no_auto_retry']);
if ($outcomeUnknown) {
$unknownOutcomes++;
}
if (in_array($status, ['PENDING', 'DISPATCHING'], true)) {
$pending++;
} elseif ($status === 'COMPLETED') {
$completed++;
} else {
$failed++;
}
$request = (array)($job->request_json->value() ?? []);
$response = (array)($job->response_json->value() ?? []);
$payload = (array)($response['payload'] ?? []);
$resultItems = isset($payload['results']) && is_array($payload['results'])
? (array)$payload['results']
: [];
if ($resultItems === [] && $status === 'COMPLETED') {
$resultItems = [[
'target' => (string)($request['target'] ?? ''),
'relayId' => (string)($request['relayId'] ?? $request['relay_id'] ?? ''),
'relay_id' => (string)($request['relay_id'] ?? $request['relayId'] ?? ''),
'ok' => true,
'payload' => $payload,
]];
}
if ($resultItems === [] && $status !== 'COMPLETED') {
$items[] = [
'target' => (string)($request['target'] ?? ''),
'relay_id' => (string)($request['relayId'] ?? $request['relay_id'] ?? ''),
'status' => $status,
'ok' => false,
'outcome_unknown' => $outcomeUnknown,
'error' => $job->error_message->value() === null ? null : (string)$job->error_message->value(),
];
continue;
}
foreach ($resultItems as $item) {
if (!is_array($item)) {
continue;
}
$items[] = $item + [
'status' => !empty($item['ok']) ? 'COMPLETED' : 'FAILED',
'outcome_unknown' => $outcomeUnknown,
];
}
}
$itemFailures = count(array_filter($items, static fn(array $item): bool => empty($item['ok']) && (($item['status'] ?? '') !== 'PENDING')));
$status = 'PENDING';
if ($pending > 0 && ($completed > 0 || $failed > 0)) {
$status = 'RUNNING';
} elseif ($pending === 0 && $unknownOutcomes > 0 && $completed === 0) {
$status = 'UNKNOWN_OUTCOME';
} elseif ($pending === 0 && ($failed > 0 || $itemFailures > 0) && ($completed > 0 || count($items) > $itemFailures)) {
$status = 'PARTIAL';
} elseif ($pending === 0 && ($failed > 0 || $itemFailures > 0)) {
$status = 'FAILED';
} elseif ($pending === 0) {
$status = 'COMPLETED';
}
return [
'batch_id' => $batchId,
'status' => $status,
'unknown_outcome_count' => $unknownOutcomes,
'jobs' => array_map(static fn(edge_gateway_command_jobs_o $job): array => $job->asArray(), $jobs),
'items' => $items,
];
}
/**
* @throws Exception
*/
@@ -1201,6 +1381,275 @@ class edge_gateway_manager
}
}
/**
* @param array<int,array<string,mixed>> $requests
* @return array<string,mixed>
* @throws Exception
*/
private function queueRelayBatch(
int $departmentId,
string $commandType,
array $requests,
?int $userId,
bool $requireFastLocalPath
): array {
if ($departmentId <= 0) {
throw new Exception('A department_id is required for relay batches');
}
if ($requests === []) {
throw new Exception('At least one relay batch command is required');
}
if (count($requests) > 5) {
throw new Exception('Relay batch commands are limited to five targets');
}
$batchId = bin2hex(random_bytes(16));
$commands = [];
$jobs = [];
$gateway = null;
$seenTargets = [];
foreach ($requests as $request) {
if (!is_array($request)) {
throw new Exception('Invalid relay batch command');
}
$target = strtoupper(trim((string)($request['target'] ?? $request['relay'] ?? '')));
$logicalRelayId = trim((string)($request['relay_id'] ?? $request['relayId'] ?? ''));
if ($target === '' || $logicalRelayId === '') {
throw new Exception('Relay batch commands require target and relay_id');
}
if (isset($seenTargets[$target])) {
throw new Exception('Duplicate relay batch target: ' . $target);
}
$seenTargets[$target] = true;
$binding = $this->resolveRelayBinding($departmentId, $logicalRelayId);
$commandGateway = $this->requireGateway((int)$binding['gateway_id']);
if ($gateway === null) {
$gateway = $commandGateway;
} elseif ((int)$gateway->id !== (int)$commandGateway->id) {
throw new Exception('Relay batch commands must target the same edge gateway');
}
$resolution = $this->resolveRelayExecutionPlan($commandGateway, $binding, $logicalRelayId);
if ($requireFastLocalPath) {
$resolution = $this->forceLocalRelayExecutionPlan($resolution);
}
if (($resolution['execution_path'] ?? 'local') === 'cloud') {
throw new Exception('Cloud Shelly transport does not support asynchronous relay batches');
}
$command = [
'target' => $target,
'relayId' => $logicalRelayId,
'relay_id' => $logicalRelayId,
'deviceId' => $binding['device_id'],
'device_id' => $binding['device_id'],
'localIp' => $binding['local_ip'],
'local_ip' => $binding['local_ip'],
'channel' => (int)$binding['channel'],
];
$deviceGeneration = $this->resolveRelayBindingDeviceGeneration($binding);
if ($deviceGeneration !== null) {
$command['deviceGeneration'] = $deviceGeneration;
$command['device_generation'] = $deviceGeneration;
}
if ($commandType === self::COMMAND_BATCH_SET_RELAY_STATE) {
$command['on'] = (bool)($request['on'] ?? true);
$rawToggleAfter = $request['toggle_after'] ?? $request['toggleAfter'] ?? $request['timer'] ?? null;
$toggleAfter = $rawToggleAfter === null || $rawToggleAfter === ''
? null
: $this->normalizeRelayToggleAfter((int)$rawToggleAfter);
if ($toggleAfter !== null) {
$command['toggleAfter'] = $toggleAfter;
$command['toggle_after'] = $toggleAfter;
}
}
$commands[] = $command;
}
if ($gateway === null) {
throw new Exception('Unable to resolve edge gateway for relay batch');
}
$batchDedupeKey = $this->buildRelayBatchDedupeKey($departmentId, $commandType, $commands, $userId);
$existingBatch = $this->findRecentRelayBatchByDedupeKey((int)$gateway->id, $batchDedupeKey, self::RELAY_BATCH_DEDUPE_SECONDS);
if ($existingBatch !== null) {
$existingBatch['deduplicated'] = true;
$existingBatch['dedupe_window_seconds'] = self::RELAY_BATCH_DEDUPE_SECONDS;
return $existingBatch;
}
foreach ($commands as $command) {
$this->enforceRelayCommandRateLimit(
(int)$gateway->id,
$departmentId,
$commandType === self::COMMAND_BATCH_RELAY_STATUS ? 'GET_RELAY_STATUS' : 'SET_RELAY_STATE',
(string)$command['target'],
$userId
);
}
foreach ($commands as $command) {
$isGatePulse = $commandType === self::COMMAND_BATCH_SET_RELAY_STATE
&& in_array((string)$command['target'], ['ENTRANCE', 'EXIT'], true);
$jobs[] = $this->createCommandJob(
(int)$gateway->id,
$commandType === self::COMMAND_BATCH_RELAY_STATUS ? 'GET_RELAY_STATUS' : 'SET_RELAY_STATE',
$command,
$userId,
[
'department_id' => $departmentId,
'batch_id' => $batchId,
'batch_dedupe_key' => $batchDedupeKey,
'idempotency_key' => $batchId . ':' . (string)$command['target'],
'target' => (string)$command['target'],
'physical_command' => true,
'preferred_channel' => self::DELIVERY_CHANNEL_API,
'require_fast_path' => false,
'no_auto_retry' => $isGatePulse,
]
);
}
edge_gateway_view_cache::syncGateway($this->getGateway((int)$gateway->id));
return [
'batch_id' => $batchId,
'status' => 'PENDING',
'gateway_id' => (int)$gateway->id,
'deduplicated' => false,
'dedupe_window_seconds' => self::RELAY_BATCH_DEDUPE_SECONDS,
'jobs' => array_map(static fn(edge_gateway_command_jobs_o $job): array => $job->asArray(), $jobs),
'items' => array_map(static fn(array $command, edge_gateway_command_jobs_o $job): array => [
'target' => (string)$command['target'],
'relay_id' => (string)$command['relayId'],
'job_id' => (int)$job->id,
'status' => 'PENDING',
], $commands, $jobs),
];
}
/**
* @param array<int,array<string,mixed>> $commands
*/
private function buildRelayBatchDedupeKey(int $departmentId, string $commandType, array $commands, ?int $userId): string
{
$normalizedCommands = array_map(static function (array $command): array {
return [
'target' => (string)($command['target'] ?? ''),
'relay_id' => (string)($command['relay_id'] ?? $command['relayId'] ?? ''),
'device_id' => (string)($command['device_id'] ?? $command['deviceId'] ?? ''),
'channel' => (int)($command['channel'] ?? 0),
'on' => array_key_exists('on', $command) ? (bool)$command['on'] : null,
'toggle_after' => isset($command['toggle_after']) ? (int)$command['toggle_after'] : null,
];
}, $commands);
usort($normalizedCommands, static fn(array $left, array $right): int => ($left['target'] <=> $right['target']) ?: ($left['relay_id'] <=> $right['relay_id']));
return hash('sha256', json_encode([
'department_id' => $departmentId,
'command_type' => $commandType,
'requested_by' => $userId ?? 0,
'commands' => $normalizedCommands,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '');
}
/**
* @return array<string,mixed>|null
* @throws Exception
*/
private function findRecentRelayBatchByDedupeKey(int $gatewayId, string $dedupeKey, int $windowSeconds): ?array
{
if ($gatewayId <= 0 || $dedupeKey === '') {
return null;
}
$statement = db::getPDO()->prepare(
"SELECT JSON_UNQUOTE(JSON_EXTRACT(delivery_json, '$.batch_id')) AS batch_id
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND requested_at >= :cutoff
AND JSON_UNQUOTE(JSON_EXTRACT(delivery_json, '$.batch_dedupe_key')) = :dedupe_key
ORDER BY requested_at DESC, id DESC
LIMIT 1"
);
$statement->execute([
':gateway_id' => $gatewayId,
':cutoff' => $this->formatDateTime(time() - max(1, $windowSeconds)),
':dedupe_key' => $dedupeKey,
]);
$row = $statement->fetch();
$batchId = is_array($row) ? trim((string)($row['batch_id'] ?? '')) : '';
return $batchId === '' ? null : $this->relayBatchStatus($batchId);
}
/**
* @throws Exception
*/
private function enforceRelayCommandRateLimit(
int $gatewayId,
int $departmentId,
string $commandType,
string $target,
?int $userId
): void {
$target = strtoupper(trim($target));
$isGate = in_array($target, ['ENTRANCE', 'EXIT'], true);
$limit = $commandType === 'GET_RELAY_STATUS'
? self::RELAY_STATUS_RATE_LIMIT_PER_MINUTE
: ($isGate ? self::RELAY_GATE_RATE_LIMIT_PER_MINUTE : self::RELAY_SET_RATE_LIMIT_PER_MINUTE);
$clauses = [
'gateway_id = :gateway_id',
'deleted_at IS NULL',
'requested_at >= :cutoff',
'command_type = :command_type',
"JSON_UNQUOTE(JSON_EXTRACT(delivery_json, '$.department_id')) = :department_id",
];
$params = [
':gateway_id' => $gatewayId,
':cutoff' => $this->formatDateTime(time() - 60),
':command_type' => $commandType,
':department_id' => (string)$departmentId,
];
if ($userId === null) {
$clauses[] = 'requested_by IS NULL';
} else {
$clauses[] = 'requested_by = :requested_by';
$params[':requested_by'] = $userId;
}
if ($isGate) {
$clauses[] = "JSON_UNQUOTE(JSON_EXTRACT(delivery_json, '$.target')) = :target";
$params[':target'] = $target;
}
$statement = db::getPDO()->prepare(
'SELECT COUNT(*) AS c
FROM edge_gateway_command_jobs
WHERE ' . implode(' AND ', $clauses)
);
$statement->execute($params);
$row = $statement->fetch();
$count = isset($row['c']) ? (int)$row['c'] : 0;
if ($count >= $limit) {
throw new Exception(sprintf(
'Relay command rate limit exceeded for %s. Try again shortly.',
$isGate ? strtolower($target) . ' gate' : strtolower(str_replace('_', ' ', $commandType))
));
}
}
/**
* @return array<int,array<string,mixed>>
*/
@@ -3199,6 +3648,7 @@ BASH;
OR (
status = :dispatching_status_match
AND COALESCE(updated_at, created_at, requested_at) <= :stale_before
AND COALESCE(JSON_UNQUOTE(JSON_EXTRACT(delivery_json, \'$.no_auto_retry\')), \'false\') <> \'true\'
)
)
ORDER BY CASE WHEN status = :pending_status_order THEN 0 ELSE 1 END, requested_at ASC, id ASC
@@ -3841,6 +4291,13 @@ BASH;
$counts = [
'command_backlog' => 0,
'operation_backlog' => 0,
'stale_dispatching_commands' => 0,
'recent_command_failures' => 0,
'recent_command_timeouts' => 0,
'recent_unknown_gate_outcomes' => 0,
'recent_relay_commands' => 0,
'recent_relay_failure_rate' => 0.0,
'recent_command_avg_latency_seconds' => null,
'last_successful_command_at' => null,
'last_successful_discovery_at' => null,
'last_successful_operation_at' => null,
@@ -3858,15 +4315,76 @@ BASH;
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND status IN ('PENDING', 'IN_PROGRESS')",
'stale_dispatching_commands' => "SELECT COUNT(*) AS c
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND status = 'DISPATCHING'
AND COALESCE(updated_at, created_at, requested_at) <= :stale_before",
'recent_command_failures' => "SELECT COUNT(*) AS c
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND status IN ('FAILED', 'TIMED_OUT')
AND requested_at >= :recent_cutoff",
'recent_command_timeouts' => "SELECT COUNT(*) AS c
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND status = 'TIMED_OUT'
AND requested_at >= :recent_cutoff",
'recent_unknown_gate_outcomes' => "SELECT COUNT(*) AS c
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND status = 'TIMED_OUT'
AND requested_at >= :recent_cutoff
AND COALESCE(JSON_UNQUOTE(JSON_EXTRACT(delivery_json, '$.no_auto_retry')), 'false') = 'true'",
'recent_relay_commands' => "SELECT COUNT(*) AS c
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND command_type IN ('GET_RELAY_STATUS', 'SET_RELAY_STATE')
AND requested_at >= :recent_cutoff",
];
foreach ($countQueries as $key => $sql) {
$statement = $pdo->prepare($sql);
$statement->execute([':gateway_id' => $gatewayId]);
$params = [
':gateway_id' => $gatewayId,
];
if (str_contains($sql, ':stale_before')) {
$params[':stale_before'] = $this->formatDateTime(time() - self::COMMAND_DISPATCH_STALE_AFTER_SECONDS);
}
if (str_contains($sql, ':recent_cutoff')) {
$params[':recent_cutoff'] = $this->formatDateTime(time() - self::RELAY_OPERATIONAL_WINDOW_SECONDS);
}
$statement->execute($params);
$row = $statement->fetch();
$counts[$key] = isset($row['c']) ? (int)$row['c'] : 0;
}
$counts['recent_relay_failure_rate'] = $counts['recent_relay_commands'] > 0
? round($counts['recent_command_failures'] / $counts['recent_relay_commands'], 4)
: 0.0;
$latencyStatement = $pdo->prepare(
"SELECT AVG(TIMESTAMPDIFF(SECOND, requested_at, completed_at)) AS avg_latency
FROM edge_gateway_command_jobs
WHERE gateway_id = :gateway_id
AND deleted_at IS NULL
AND completed_at IS NOT NULL
AND requested_at >= :recent_cutoff"
);
$latencyStatement->execute([
':gateway_id' => $gatewayId,
':recent_cutoff' => $this->formatDateTime(time() - self::RELAY_OPERATIONAL_WINDOW_SECONDS),
]);
$latencyRow = $latencyStatement->fetch();
if (is_array($latencyRow) && $latencyRow['avg_latency'] !== null) {
$counts['recent_command_avg_latency_seconds'] = round((float)$latencyRow['avg_latency'], 2);
}
$timestampQueries = [
'last_successful_command_at' => "SELECT completed_at AS ts
FROM edge_gateway_command_jobs
@@ -167,6 +167,38 @@ function worker_switch_shelly_state(string $localIp, int $channel, bool $on): ar
return worker_fetch_shelly_state($localIp, $channel);
}
function worker_normalize_relay_commands(array $body): array
{
$commands = $body['commands'] ?? [];
return is_array($commands) ? array_values($commands) : [];
}
function worker_relay_command_result(array $command, callable $handler): array
{
$target = trim((string)($command['target'] ?? $command['relay'] ?? ''));
$relayId = trim((string)($command['relayId'] ?? $command['relay_id'] ?? ''));
try {
$localIp = trim((string)($command['local_ip'] ?? $command['localIp'] ?? ''));
$channel = (int)($command['channel'] ?? 0);
return [
'target' => $target,
'relayId' => $relayId,
'relay_id' => $relayId,
'ok' => true,
'payload' => $handler($localIp, $channel, $command),
];
} catch (Throwable $throwable) {
return [
'target' => $target,
'relayId' => $relayId,
'relay_id' => $relayId,
'ok' => false,
'error' => $throwable->getMessage(),
];
}
}
$method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
$path = (string)(parse_url((string)($_SERVER['REQUEST_URI'] ?? '/'), PHP_URL_PATH) ?? '/');
$body = worker_read_json_body();
@@ -248,6 +280,48 @@ try {
return;
}
if ($method === 'POST' && $path === '/relay/batch-status') {
if (!worker_require_authorization()) {
return;
}
$results = array_map(
fn(array $command): array => worker_relay_command_result(
$command,
fn(string $localIp, int $channel): array => worker_fetch_shelly_state($localIp, $channel)
),
worker_normalize_relay_commands($body)
);
worker_json_response(200, [
'batch_id' => $body['batch_id'] ?? $body['batchId'] ?? null,
'results' => $results,
]);
return;
}
if ($method === 'POST' && $path === '/relay/batch-switch') {
if (!worker_require_authorization()) {
return;
}
$results = array_map(
fn(array $command): array => worker_relay_command_result(
$command,
fn(string $localIp, int $channel, array $entry): array => worker_switch_shelly_state(
$localIp,
$channel,
(bool)($entry['on'] ?? false)
)
),
worker_normalize_relay_commands($body)
);
worker_json_response(200, [
'batch_id' => $body['batch_id'] ?? $body['batchId'] ?? null,
'results' => $results,
]);
return;
}
worker_json_response(404, ['message' => 'Not found']);
} catch (Throwable $throwable) {
worker_json_response(422, [
@@ -3,6 +3,7 @@
namespace routes;
use classes\authentication;
use classes\edge_gateway_manager;
use classes\email;
use classes\response;
use classes\router;
@@ -780,6 +781,83 @@ class moduleSelfServeRoute
'modules_selfserve_lane_gate_open' => 'Open ENTRANCE or EXIT gate for a self-serve lane'
]);
/** Modules > Self Serve > Lane > Hardware > Batch status */
$this->post('/modules/self-serve/lane/hardware/batch/status', function () {
global $response;
$selfserve = new selfserve();
self::requireParameters(['lane_id']);
$lane_id = (int)$this->getParameter('lane_id');
self::requireType($lane_id, self::type_int());
self::requireMinValue($lane_id, 1);
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$targets = self::isParametersSet(['targets']) ? (array)$this->getParameter('targets') : [
'MACHINE',
'PROGRAM_PICKER',
'CLEANER',
'ENTRANCE',
'EXIT',
];
$requests = $this->buildLaneHardwareBatchRequests($lane, $targets, false);
$user = (new authentication())->get_user();
$result = (new edge_gateway_manager())->queueRelayStatusBatch(
$this->departmentIdForLane($lane),
$requests,
$user instanceof users_o ? (int)$user->id : null,
$this->requestedShellyTransportOverride() === 'local'
);
$response->success($result, 202);
} catch (\Exception $e) {
$response->error('Failed to queue lane hardware status batch: ' . $e->getMessage(), 400);
}
}, [
'modules_selfserve_lane_relay_machine_status_view' => 'Queue self-serve lane hardware status batch'
]);
/** Modules > Self Serve > Lane > Hardware > Batch set/open */
$this->post('/modules/self-serve/lane/hardware/batch/set', function () {
global $response;
$selfserve = new selfserve();
self::requireParameters(['lane_id', 'commands']);
$lane_id = (int)$this->getParameter('lane_id');
self::requireType($lane_id, self::type_int());
self::requireMinValue($lane_id, 1);
$commands = (array)$this->getParameter('commands');
$lane = $selfserve->lane($lane_id);
try {
$this->applyShellyTransportOverride($lane);
$requests = $this->buildLaneHardwareBatchRequests($lane, $commands, true);
$user = (new authentication())->get_user();
$result = (new edge_gateway_manager())->queueRelaySwitchBatch(
$this->departmentIdForLane($lane),
$requests,
$user instanceof users_o ? (int)$user->id : null,
$this->requestedShellyTransportOverride() === 'local'
);
$response->success($result, 202);
} catch (\Exception $e) {
$response->error('Failed to queue lane hardware command batch: ' . $e->getMessage(), 400);
}
}, [
'modules_selfserve_lane_relay_machine_status_set' => 'Queue self-serve lane hardware command batch'
]);
/** Modules > Self Serve > Lane > Hardware > Batch poll */
$this->get('/modules/self-serve/lane/hardware/batch/{batch_id}', function () {
global $response;
self::requirePermission('modules_selfserve_lane_relay_machine_status_view');
try {
$response->success((new edge_gateway_manager())->relayBatchStatus((string)$this->fromRoute('batch_id')));
} catch (\Exception $e) {
$response->error('Failed to get lane hardware batch status: ' . $e->getMessage(), 400);
}
}, [
'modules_selfserve_lane_relay_machine_status_view' => 'Poll self-serve lane hardware batch'
]);
/** Modules > Self Serve > Lane > Relay > MACHINE_PROGRAM_PICKER status */
$this->get('/modules/self-serve/lane/relay/machine_program_picker/status', function () {
global $response;
@@ -1641,6 +1719,135 @@ class moduleSelfServeRoute
return $payload;
}
/**
* @param array<int,mixed> $items
* @return array<int,array<string,mixed>>
* @throws \Exception
*/
private function buildLaneHardwareBatchRequests(selfserve_lane $lane, array $items, bool $mutation): array
{
if (empty($lane->department_lane)) {
throw new \Exception("Department lane object not found for lane ID {$lane->id}");
}
$requests = [];
$seen = [];
foreach ($items as $item) {
$command = is_array($item) ? $item : ['target' => $item];
$target = strtoupper(trim((string)($command['target'] ?? $command['relay'] ?? '')));
if ($target === '') {
throw new \Exception('Lane hardware batch commands require a target');
}
if (isset($seen[$target])) {
throw new \Exception('Duplicate lane hardware target: ' . $target);
}
$seen[$target] = true;
$relayId = $this->relayIdForLaneHardwareTarget($lane, $target);
if ($relayId === '') {
throw new \Exception('No relay is configured for lane hardware target ' . $target);
}
if ($mutation) {
$this->requireLaneHardwareMutationPermission($target);
} else {
$this->requireLaneHardwareStatusPermission($target);
}
$request = [
'target' => $target,
'relay_id' => $relayId,
'relayId' => $relayId,
];
if ($mutation) {
if (in_array($target, ['ENTRANCE', 'EXIT'], true)) {
$action = strtoupper(trim((string)($command['action'] ?? 'OPEN')));
if ($action !== 'OPEN') {
throw new \Exception($target . ' only supports action=OPEN');
}
$request['on'] = true;
$request['toggle_after'] = $this->normalizeBatchToggleAfter(
$command['toggle_after'] ?? $command['toggleAfter'] ?? $command['timer'] ?? 1,
self::MAX_GATE_OPEN_TOGGLE_AFTER_SECONDS
);
} else {
if (!array_key_exists('on', $command)) {
throw new \Exception($target . ' relay commands require an on boolean');
}
$on = is_bool($command['on'])
? $command['on']
: filter_var($command['on'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
if ($on === null) {
throw new \Exception('Invalid on value for ' . $target . '. Expected boolean true/false.');
}
$request['on'] = (bool)$on;
if (isset($command['toggle_after']) || isset($command['toggleAfter']) || isset($command['timer'])) {
$request['toggle_after'] = $this->normalizeBatchToggleAfter(
$command['toggle_after'] ?? $command['toggleAfter'] ?? $command['timer'],
86400
);
}
}
}
$requests[] = $request;
}
if ($requests === []) {
throw new \Exception('At least one lane hardware target is required');
}
if (count($requests) > 5) {
throw new \Exception('Lane hardware batches are limited to five targets');
}
return $requests;
}
private function relayIdForLaneHardwareTarget(selfserve_lane $lane, string $target): string
{
return trim((string)match ($target) {
'MACHINE' => $lane->department_lane->relay_machine_id->value(),
'PROGRAM_PICKER', 'MACHINE_PROGRAM_PICKER' => $lane->department_lane->relay_machine_program_picker_id->value(),
'CLEANER', 'MACHINE_CLEANER' => $lane->department_lane->relay_machine_cleaner_id->value(),
'ENTRANCE' => $lane->department_lane->relay_in_id->value(),
'EXIT' => $lane->department_lane->relay_out_id->value(),
default => throw new \Exception('Unsupported lane hardware target: ' . $target),
});
}
private function requireLaneHardwareStatusPermission(string $target): void
{
match ($target) {
'MACHINE' => self::requirePermission('modules_selfserve_lane_relay_machine_status_view'),
'PROGRAM_PICKER', 'MACHINE_PROGRAM_PICKER' => self::requirePermission('modules_selfserve_lane_relay_machine_program_picker_status_view'),
'CLEANER', 'MACHINE_CLEANER' => self::requirePermission('modules_selfserve_lane_relay_machine_cleaner_status_view'),
'ENTRANCE', 'EXIT' => self::requirePermission('modules_selfserve_lane_gate_open'),
default => throw new \Exception('Unsupported lane hardware target: ' . $target),
};
}
private function requireLaneHardwareMutationPermission(string $target): void
{
match ($target) {
'MACHINE' => self::requirePermission('modules_selfserve_lane_relay_machine_status_set'),
'PROGRAM_PICKER', 'MACHINE_PROGRAM_PICKER' => self::requirePermission('modules_selfserve_lane_relay_machine_program_picker_status_set'),
'CLEANER', 'MACHINE_CLEANER' => self::requirePermission('modules_selfserve_lane_relay_machine_cleaner_status_set'),
'ENTRANCE', 'EXIT' => self::requirePermission('modules_selfserve_lane_gate_open'),
default => throw new \Exception('Unsupported lane hardware target: ' . $target),
};
}
private function normalizeBatchToggleAfter(mixed $value, int $max): int
{
$seconds = (int)$value;
if ($seconds <= 0) {
throw new \Exception('toggle_after must be greater than zero');
}
return min($seconds, $max);
}
/**
* @throws \Exception
*/
@@ -16,7 +16,11 @@ it('keeps relay dispatch and discovery queueing on the edge gateway manager', fu
expect($managerSource)->toContain('public function dispatchRelaySwitchLocalOnly');
expect($managerSource)->toContain('public function dispatchRelaySwitchWithTimer');
expect($managerSource)->toContain('public function dispatchRelaySwitchLocalOnlyWithTimer');
expect($managerSource)->toContain('public function queueRelayStatusBatch');
expect($managerSource)->toContain('public function queueRelaySwitchBatch');
expect($managerSource)->toContain('public function relayBatchStatus');
expect($managerSource)->toContain('private function createCommandJob');
expect($managerSource)->toContain('private function queueRelayBatch');
expect($managerSource)->toContain("'GET_RELAY_STATUS'");
expect($managerSource)->toContain("'SET_RELAY_STATE'");
expect($managerSource)->toContain("'relayId' => \$logicalRelayId");
@@ -34,6 +38,19 @@ it('keeps relay dispatch and discovery queueing on the edge gateway manager', fu
expect($managerSource)->toContain("'Edge gateway command timed out', null, 'TIMED_OUT'");
expect($managerSource)->toContain('private function normalizeCommandFailureStatus');
expect($managerSource)->toContain('private function isCommandTimeoutError');
expect($managerSource)->toContain("'batch_id' => \$batchId");
expect($managerSource)->toContain("'batch_dedupe_key' => \$batchDedupeKey");
expect($managerSource)->toContain("'idempotency_key' => \$batchId . ':' . (string)\$command['target']");
expect($managerSource)->toContain("'no_auto_retry' => \$isGatePulse");
expect($managerSource)->toContain('private function buildRelayBatchDedupeKey');
expect($managerSource)->toContain('private function findRecentRelayBatchByDedupeKey');
expect($managerSource)->toContain('private function enforceRelayCommandRateLimit');
expect($managerSource)->toContain('RELAY_GATE_RATE_LIMIT_PER_MINUTE');
expect($managerSource)->toContain("JSON_EXTRACT(delivery_json, \\'$.no_auto_retry\\')");
expect($managerSource)->toContain('not retrying non-idempotent gate pulse');
expect($managerSource)->toContain('UNKNOWN_OUTCOME');
expect($managerSource)->toContain('recent_unknown_gate_outcomes');
expect($managerSource)->toContain('recent_relay_failure_rate');
expect($managerSource)->toContain('local_transport_override');
expect($managerSource)->toContain('private function resolveRelayBindingLocalIp');
expect($managerSource)->toContain('private function findShellyCloudRelayLocalIp');
@@ -71,6 +88,9 @@ it('loads relay command helpers on the manager and gateway operations on the ded
expect($reflection->hasMethod('dispatchRelaySwitchLocalOnly'))->toBeTrue();
expect($reflection->hasMethod('dispatchRelaySwitchWithTimer'))->toBeTrue();
expect($reflection->hasMethod('dispatchRelaySwitchLocalOnlyWithTimer'))->toBeTrue();
expect($reflection->hasMethod('queueRelayStatusBatch'))->toBeTrue();
expect($reflection->hasMethod('queueRelaySwitchBatch'))->toBeTrue();
expect($reflection->hasMethod('relayBatchStatus'))->toBeTrue();
expect($reflection->hasMethod('claimNextCommandJob'))->toBeTrue();
expect($reflection->getMethod('claimNextCommandJob')->isPrivate())->toBeTrue();
expect($reflection->hasMethod('syncDeviceInventory'))->toBeTrue();
@@ -16,6 +16,10 @@ class GatewayShellyTransportManagerFake extends edge_gateway_manager
public array $localOnlyStatusCalls = [];
/** @var array<int,array<string,mixed>> */
public array $localOnlySwitchCalls = [];
/** @var array<int,array<string,mixed>> */
public array $batchStatusCalls = [];
/** @var array<int,array<string,mixed>> */
public array $batchSwitchCalls = [];
public ?Exception $statusException = null;
public ?Exception $switchException = null;
@@ -128,6 +132,44 @@ class GatewayShellyTransportManagerFake extends edge_gateway_manager
'raw' => ['source' => 'local-switch'],
];
}
public function queueRelayStatusBatch(
int $departmentId,
array $requests,
?int $userId = null,
bool $requireFastLocalPath = false
): array {
$this->batchStatusCalls[] = [
'department_id' => $departmentId,
'requests' => $requests,
'local_only' => $requireFastLocalPath,
];
return [
'batch_id' => 'batch-status',
'status' => 'PENDING',
'items' => $requests,
];
}
public function queueRelaySwitchBatch(
int $departmentId,
array $requests,
?int $userId = null,
bool $requireFastLocalPath = false
): array {
$this->batchSwitchCalls[] = [
'department_id' => $departmentId,
'requests' => $requests,
'local_only' => $requireFastLocalPath,
];
return [
'batch_id' => 'batch-switch',
'status' => 'PENDING',
'items' => $requests,
];
}
}
it('maps gateway relay status responses into the Shelly cloud payload shape', function (): void {
@@ -220,6 +262,46 @@ it('forwards Shelly toggle_after timers to gateway relay switches', function ():
]);
});
it('queues gateway relay batch status requests through the manager', function (): void {
$manager = new GatewayShellyTransportManagerFake();
$transport = new gateway_shelly_transport($manager);
$result = $transport->sendPostRequest('/v2/devices/api/batch/get', [
'commands' => [
['target' => 'MACHINE', 'relay_id' => 'relay-machine'],
['target' => 'ENTRANCE', 'relay_id' => 'relay-in'],
],
], 17);
expect($manager->batchStatusCalls)->toHaveCount(1)
->and($manager->batchStatusCalls[0]['department_id'])->toBe(17)
->and($manager->batchStatusCalls[0]['requests'])->toBe([
['target' => 'MACHINE', 'relay_id' => 'relay-machine'],
['target' => 'ENTRANCE', 'relay_id' => 'relay-in'],
])
->and($result['batch_id'])->toBe('batch-status');
});
it('queues gateway relay batch switch requests through the manager', function (): void {
$manager = new GatewayShellyTransportManagerFake();
$transport = new gateway_shelly_transport($manager, true);
$result = $transport->sendPostRequest('/v2/devices/api/batch/set/switch', [
'commands' => [
['target' => 'MACHINE', 'relay_id' => 'relay-machine', 'on' => true],
['target' => 'EXIT', 'relay_id' => 'relay-out', 'on' => true, 'toggle_after' => 1],
],
], 17);
expect($manager->batchSwitchCalls)->toHaveCount(1)
->and($manager->batchSwitchCalls[0]['local_only'])->toBeTrue()
->and($manager->batchSwitchCalls[0]['requests'])->toBe([
['target' => 'MACHINE', 'relay_id' => 'relay-machine', 'on' => true, 'toggle_after' => null],
['target' => 'EXIT', 'relay_id' => 'relay-out', 'on' => true, 'toggle_after' => 1],
])
->and($result['batch_id'])->toBe('batch-switch');
});
it('requires a valid department id for gateway transport requests', function (): void {
$transport = new gateway_shelly_transport(new GatewayShellyTransportManagerFake());