Compare commits

..
Author SHA1 Message Date
Jeppe Bundgaard 85eb0039d1 Add department hardware workspace backend 2026-04-23 12:45:07 +02:00
507 changed files with 4405 additions and 245217 deletions
+1 -7
View File
@@ -1,7 +1 @@
/docker-compose.yml /docker-compose.yml
# Runtime-generated replication bootstrap snapshots may contain infrastructure
# metadata and encrypted/plaintext credential material. They must be
# supplied at runtime via mounted storage, not baked into deployment images.
/services/nginx/app/storage/replication-bootstrap.json
/services/nginx/app/storage/replication-bootstrap-*.json
+1 -6
View File
@@ -31,9 +31,6 @@ CONFIG_DB_DATABASE=
CONFIG_DB_PORT=3306 CONFIG_DB_PORT=3306
CONFIG_DB_SSL_MODE=DISABLED CONFIG_DB_SSL_MODE=DISABLED
# Browser origins allowed to call the API. Path-like entries are normalized to origins by the PHP CORS policy.
CORS=https://truckwash.io,https://www.truckwash.io,https://api.truckwash.io,https://api.truckwash.io:4433,https://api-v2.truckwash.io,https://web.truckwash.dk,https://api.truckwash.dk,https://truckwash.dk,https://www.truckwash.dk,https://staging.truckwash.io,http://localhost,https://localhost,http://localhost:4433,https://localhost:4433,https://twdev.jeppeb.dk,http://localhost:5173
# Debug DB credentials (used when CONFIG_DB_TARGET=debug) # Debug DB credentials (used when CONFIG_DB_TARGET=debug)
# Any blank debug value falls back to the live value above. # Any blank debug value falls back to the live value above.
CONFIG_DB_DEBUG_HOST=mysql-debug CONFIG_DB_DEBUG_HOST=mysql-debug
@@ -52,9 +49,7 @@ ECONOMIC_API_APP_SECRET_TOKEN=
# Edge broker defaults for shell relay and gateway dispatch. # Edge broker defaults for shell relay and gateway dispatch.
EDGE_BROKER_URL=http://edge-broker:4300 EDGE_BROKER_URL=http://edge-broker:4300
EDGE_PUBLIC_BROKER_URL=http://localhost/api/edge-broker EDGE_BROKER_SHARED_SECRET=truckwash-edge-dev
EDGE_AUTH_MODE=strict
EDGE_BROKER_SHARED_SECRET=
# Redis credentials # Redis credentials
REDIS_CONFIG_HOST=redis REDIS_CONFIG_HOST=redis
+42
View File
@@ -0,0 +1,42 @@
USE_ENV=true
# Target of the database connection. Can be either 'live' or 'debug'.
CONFIG_DB_TARGET=live
CONFIG_DB_DATABASE=nnks_db
#CONFIG_DB_HOST=94.130.142.41
CONFIG_DB_HOST=23.88.23.183
CONFIG_DB_PASSWORD=562X0Lrr7Cz6zpXZ11I
CONFIG_DB_USER=root
CONFIG_DB_PORT=5432
CONFIG_DB_DEBUG_DATABASE=nnks_db
CONFIG_DB_DEBUG_HOST=23.88.23.183
CONFIG_DB_DEBUG_PORT=5432
CONFIG_DB_DEBUG_PASSWORD=562X0Lrr7Cz6zpXZ11I
CONFIG_DB_DEBUG_USER=root
CONFIG_TIMEZONE=Europe/Copenhagen
CORS=https://truckwash.io,https://www.truckwash.io,https://api.truckwash.io,https://api.truckwash.io:4433,https://web.truckwash.dk,https://api.truckwash.dk,https://truckwash.dk,https://www.truckwash.dk,https://staging.truckwash.io,http://localhost,https://localhost,http://localhost:4433,https://localhost:4433,https://twdev.jeppeb.dk,http://localhost:5173
# CORS=*
DEBUG=false
ECONOMIC_API_APP_ACCESS_GRANT=94bhkmdtaDA7kVn9abF2SGDccBDMvk5a6iWYnmJMbvQ1
ECONOMIC_API_APP_ACCESS_GRANT2=qGSBSkh1pjBtdSOygHhaMPn1A4PcMto3sCDCGYpLmsg1
ECONOMIC_API_APP_SECRET_TOKEN=V8GSEcIxMsTISczzTTBbOAMJyh8eucGZtBiGOxjMFg0
EMAIL_WASH_CERTIFICATE_TOKEN=H7uDTtFaeN4asqpb5okh6dr8z209SGtt
ENCRYPTION_KEY=Gvm37uF2VyTOjGkVl4kjrGQ0qRwOyq9lr3+p/QyUDjc\\=
MINIO_ACCESS_KEY=d7u6RaFyYmckAIWYGUYr
MINIO_ENDPOINT=http://162.55.225.220:9000
MINIO_SECRET_KEY=a2wJUQfkOPNO3UJfXYIdpNq4r1RrthcjiUfW1gVS
REDIS_CONFIG_DATABASE=0
REDIS_CONFIG_HOST=23.88.23.183
REDIS_CONFIG_PASSWORD=BlVg5o1NwkkR1IjKxQm
REDIS_CONFIG_PORT=5433
REDIS_CONFIG_USER=default
REDIS_CONFIG_DEBUG_PORT=5433
REDIS_CONFIG_DEBUG_USER=default
SLACK_DEFAULT_WEBHOOK=https://hooks.slaCk.com/services/T05SRKWTX9C/B08AGMP459P/1W5JN1NpHsHlbHHM2WljpvrU
WORDPRESS_API_URL=https://www.truckwash.dk/wp-admin/admin-ajax.php
WORDPRESS_STATIC_TOKEN=earm8BX4MFTgS6JCNQdqW5EzHUutv2Vx
ELASTIC_APM_SERVER_URL=http://elastic-agent:8200
ELASTIC_APM_SECRET_TOKEN=apm_dev_token
ELASTIC_APM_SERVICE_NAME=api-truckwash
ELASTIC_APM_ENVIRONMENT=dev
AUTO_COMPOSER_INSTALL=false
-15
View File
@@ -1,15 +0,0 @@
* text=auto eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.pdf binary
*.zip binary
*.webm binary
-48
View File
@@ -1,48 +0,0 @@
USE_ENV=true
DEBUG=true
ENCRYPTION_KEY=ci-test-encryption-key
CORS=*
CONFIG_TIMEZONE=Europe/Copenhagen
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=root
CONFIG_DB_PASSWORD=debug_root_password
CONFIG_DB_DATABASE=nnks_db_debug
CONFIG_DB_PORT=3306
CONFIG_DB_SSL_MODE=DISABLED
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=root
CONFIG_DB_DEBUG_PASSWORD=debug_root_password
CONFIG_DB_DEBUG_DATABASE=nnks_db_debug
CONFIG_DB_DEBUG_PORT=3306
CONFIG_DB_DEBUG_SSL_MODE=DISABLED
REDIS_CONFIG_HOST=redis
REDIS_CONFIG_USER=default
REDIS_CONFIG_DATABASE=0
REDIS_CONFIG_PASSWORD=
REDIS_CONFIG_PORT=6379
REDIS_CONFIG_DEBUG_HOST=redis
REDIS_CONFIG_DEBUG_USER=default
REDIS_CONFIG_DEBUG_DATABASE=0
REDIS_CONFIG_DEBUG_PASSWORD=
REDIS_CONFIG_DEBUG_PORT=6379
ECONOMIC_API_APP_ACCESS_GRANT=ci-test
ECONOMIC_API_APP_ACCESS_GRANT2=ci-test-secondary
ECONOMIC_API_APP_SECRET_TOKEN=ci-test-secret
WORDPRESS_STATIC_TOKEN=ci-test
EMAIL_WASH_CERTIFICATE_TOKEN=ci-test
WORDPRESS_API_URL=http://localhost
MINIO_ENDPOINT=
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
SLACK_DEFAULT_WEBHOOK=
EDGE_BROKER_URL=http://edge-broker:4300
EDGE_PUBLIC_BROKER_URL=http://localhost/api/edge-broker
EDGE_AUTH_MODE=manager
EDGE_BROKER_SHARED_SECRET=truckwash-edge-ci
EDGE_GATEWAY_VIEW_CACHE_TTL=0
TRUCKWASH_TEST_BLOCK_REAL_SHELLY=1
-48
View File
@@ -1,48 +0,0 @@
USE_ENV=true
DEBUG=true
ENCRYPTION_KEY=ci-test-encryption-key
CORS=*
CONFIG_TIMEZONE=Europe/Copenhagen
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=root
CONFIG_DB_PASSWORD=debug_root_password
CONFIG_DB_DATABASE=nnks_db_debug
CONFIG_DB_PORT=3306
CONFIG_DB_SSL_MODE=DISABLED
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=root
CONFIG_DB_DEBUG_PASSWORD=debug_root_password
CONFIG_DB_DEBUG_DATABASE=nnks_db_debug
CONFIG_DB_DEBUG_PORT=3306
CONFIG_DB_DEBUG_SSL_MODE=DISABLED
REDIS_CONFIG_HOST=redis
REDIS_CONFIG_USER=default
REDIS_CONFIG_DATABASE=0
REDIS_CONFIG_PASSWORD=
REDIS_CONFIG_PORT=6379
REDIS_CONFIG_DEBUG_HOST=redis
REDIS_CONFIG_DEBUG_USER=default
REDIS_CONFIG_DEBUG_DATABASE=0
REDIS_CONFIG_DEBUG_PASSWORD=
REDIS_CONFIG_DEBUG_PORT=6379
ECONOMIC_API_APP_ACCESS_GRANT=ci-test
ECONOMIC_API_APP_ACCESS_GRANT2=ci-test-secondary
ECONOMIC_API_APP_SECRET_TOKEN=ci-test-secret
WORDPRESS_STATIC_TOKEN=ci-test
EMAIL_WASH_CERTIFICATE_TOKEN=ci-test
WORDPRESS_API_URL=http://localhost
MINIO_ENDPOINT=
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
SLACK_DEFAULT_WEBHOOK=
EDGE_BROKER_URL=http://edge-broker:4300
EDGE_PUBLIC_BROKER_URL=http://localhost/api/edge-broker
EDGE_AUTH_MODE=manager
EDGE_BROKER_SHARED_SECRET=truckwash-edge-ci
EDGE_GATEWAY_VIEW_CACHE_TTL=0
TRUCKWASH_TEST_BLOCK_REAL_SHELLY=1
-90
View File
@@ -1,90 +0,0 @@
services:
traefik:
container_name: "${COMPOSE_PROJECT_NAME:-api}-traefik"
redis:
container_name: "${COMPOSE_PROJECT_NAME:-api}-redis"
mysql-debug:
container_name: "${COMPOSE_PROJECT_NAME:-api}-mysql-debug"
ports: !reset []
edge-broker:
container_name: "${COMPOSE_PROJECT_NAME:-api}-edge-broker"
ports:
- "127.0.0.1:${EDGE_BROKER_CI_PORT:-14300}:4300"
labels:
- "traefik.http.routers.edge-broker-local-ci.rule=PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-ci.entrypoints=web"
- "traefik.http.routers.edge-broker-local-ci.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-ci.priority=190"
- "traefik.http.routers.edge-broker-local-ci.service=edge-broker"
caddy:
container_name: "${COMPOSE_PROJECT_NAME:-api}-caddy"
depends_on: !reset []
labels:
- "traefik.http.routers.local-api-ci.rule=PathPrefix(`/api`)"
- "traefik.http.routers.local-api-ci.entrypoints=web"
- "traefik.http.routers.local-api-ci.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api-ci.priority=90"
- "traefik.http.routers.local-api-ci.service=caddy"
volumes:
- ci_php_app:/var/www/html
php1:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php1"
depends_on: !reset []
environment:
AUTO_COMPOSER_INSTALL: "false"
USE_ENV: "true"
CONFIG_DB_TARGET: "debug"
CONFIG_DB_HOST: "mysql-debug"
CONFIG_DB_USER: "root"
CONFIG_DB_PASSWORD: "debug_root_password"
CONFIG_DB_DATABASE: "nnks_db_debug"
CONFIG_DB_PORT: "3306"
CONFIG_DB_DEBUG_HOST: "mysql-debug"
CONFIG_DB_DEBUG_USER: "root"
CONFIG_DB_DEBUG_PASSWORD: "debug_root_password"
CONFIG_DB_DEBUG_DATABASE: "nnks_db_debug"
CONFIG_DB_DEBUG_PORT: "3306"
REDIS_CONFIG_HOST: "redis"
REDIS_CONFIG_PORT: "6379"
REDIS_CONFIG_DATABASE: "0"
REDIS_CONFIG_DEBUG_HOST: "redis"
REDIS_CONFIG_DEBUG_PORT: "6379"
REDIS_CONFIG_DEBUG_DATABASE: "0"
TRUCKWASH_TEST_BLOCK_REAL_SHELLY: "1"
EDGE_GATEWAY_VIEW_CACHE_TTL: "0"
volumes:
- ci_php_app:/var/www/html
php2:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php2"
volumes:
- ci_php_app:/var/www/html
php3:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php3"
volumes:
- ci_php_app:/var/www/html
php4:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php4"
volumes:
- ci_php_app:/var/www/html
php5:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php5"
volumes:
- ci_php_app:/var/www/html
volumes:
ci_php_app:
networks:
default:
ipam:
config:
- subnet: "${CI_DOCKER_SUBNET:-10.240.0.0/24}"
+7 -31
View File
@@ -9,44 +9,20 @@ on:
jobs: jobs:
qodana: qodana:
# CI runs on the repository's self-hosted runner pool. runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64, pleno, backend, docker]
permissions: permissions:
contents: read contents: write
pull-requests: read pull-requests: write
checks: read checks: write
steps: steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@v3
with: with:
ref: ${{ github.event.pull_request.head.sha || github.sha }} # Use PR head when available, otherwise the pushed SHA. ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis fetch-depth: 0 # a full history is required for pull request analysis
persist-credentials: false
- name: Mark repository as safe for Git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Prepare Qodana cache directories
run: |
mkdir -p "${RUNNER_TEMP}/qodana/caches"
mkdir -p "${RUNNER_TEMP}/qodana/results"
- name: Detect Qodana Cloud token
id: qodana-token
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
run: |
if [ -n "${QODANA_TOKEN:-}" ]; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
fi
- name: 'Qodana Scan' - name: 'Qodana Scan'
if: ${{ steps.qodana-token.outputs.present == 'true' }} uses: JetBrains/qodana-action@v2025.3
uses: JetBrains/qodana-action@v2026.1
with: with:
pr-mode: false pr-mode: false
env: env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
QODANA_ENDPOINT: 'https://qodana.cloud' QODANA_ENDPOINT: 'https://qodana.cloud'
- name: 'Skip Qodana Scan (missing cloud token)'
if: ${{ steps.qodana-token.outputs.present != 'true' }}
run: echo "Skipping Qodana because QODANA_TOKEN is not configured."
+164 -292
View File
@@ -5,60 +5,56 @@ on:
push: push:
jobs: jobs:
php: unit:
name: PHP ${{ matrix.suite }} (required) name: Unit (required)
runs-on: [self-hosted, Linux, X64, pleno, backend, docker] # Match the labels exposed by the Coolify-managed GitHub runner.
strategy: runs-on: [self-hosted, Linux, X64, default]
fail-fast: false
matrix:
suite: [unit, integration, api, legacy]
env:
COMPOSE_PROJECT_NAME: php-${{ github.run_id }}-${{ github.job }}-${{ matrix.suite }}-${{ github.run_attempt }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Ensure Docker access
run: |
set -euo pipefail
if docker ps >/dev/null 2>&1; then
exit 0
fi
test -S /var/run/docker.sock || (echo "Docker socket is not available." >&2; exit 1)
if command -v sudo >/dev/null 2>&1; then
sudo -n chmod 666 /var/run/docker.sock
else
chmod 666 /var/run/docker.sock
fi
docker ps >/dev/null
- name: Setup Node.js - name: Setup Node.js
if: ${{ matrix.suite == 'unit' }}
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
- name: Check AI workflow sync - name: Check AI workflow sync
if: ${{ matrix.suite == 'unit' }}
run: node scripts/sync-ai-workflow.mjs --check run: node scripts/sync-ai-workflow.mjs --check
- name: Run PHP ${{ matrix.suite }} suite - name: Setup PHP
run: bash scripts/php-ci-test.sh ${{ matrix.suite }} uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mysqli, curl, openssl, json, redis, xdebug
coverage: xdebug
ini-values: variables_order=EGPCS,xdebug.mode=coverage
- name: Upload PHP suite logs - name: Resolve dependencies
if: ${{ failure() }} working-directory: services/nginx/app
run: composer update --no-interaction --prefer-dist
- name: Run unit tests
working-directory: services/nginx/app
run: composer test:unit
- name: Generate coverage report
working-directory: services/nginx/app
run: composer test:coverage
- name: Upload coverage artifact
if: ${{ github.event_name == 'pull_request' }}
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: php-${{ matrix.suite }}-logs name: unit-coverage-clover
path: .tmp/ci-logs/${{ matrix.suite }} path: services/nginx/app/build/logs/clover.xml
if-no-files-found: warn if-no-files-found: warn
retention-days: 3 retention-days: 1
edge-agent: edge-agent:
name: Edge Agent (required) name: Edge Agent (required)
runs-on: [self-hosted, Linux, X64, pleno, backend] runs-on: [self-hosted, Linux, X64, default]
steps: steps:
- name: Checkout - name: Checkout
@@ -68,30 +64,8 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
cache: npm
- name: Install native build tools cache-dependency-path: services/edge-agent/package-lock.json
run: |
set -euo pipefail
if command -v make >/dev/null 2>&1 && command -v g++ >/dev/null 2>&1; then
exit 0
fi
if ! command -v apt-get >/dev/null 2>&1; then
echo "make and g++ are required to install node-pty, but apt-get is not available on this runner." >&2
exit 1
fi
apt_cmd=(apt-get)
if [ "$(id -u)" -ne 0 ]; then
if ! command -v sudo >/dev/null 2>&1; then
echo "make and g++ are missing, and sudo is not available to install them." >&2
exit 1
fi
apt_cmd=(sudo apt-get)
fi
"${apt_cmd[@]}" update
"${apt_cmd[@]}" install -y --no-install-recommends build-essential python3
- name: Install dependencies - name: Install dependencies
working-directory: services/edge-agent working-directory: services/edge-agent
@@ -103,31 +77,28 @@ jobs:
edge-broker: edge-broker:
name: Edge Broker (required) name: Edge Broker (required)
runs-on: [self-hosted, Linux, X64, pleno, backend, docker] runs-on: [self-hosted, Linux, X64, default]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Ensure Docker access - name: Materialize compose env files
env:
COMPOSE_ENV: ${{ secrets.COMPOSE_ENV }}
COMPOSE_ENV_STAGING: ${{ secrets.COMPOSE_ENV_STAGING }}
run: | run: |
set -euo pipefail set -euo pipefail
if docker ps >/dev/null 2>&1; then if [ -z "${COMPOSE_ENV}" ]; then
exit 0 echo "Required GitHub secret COMPOSE_ENV is not configured." >&2
exit 1
fi fi
test -S /var/run/docker.sock || (echo "Docker socket is not available." >&2; exit 1) if [ -z "${COMPOSE_ENV_STAGING}" ]; then
if command -v sudo >/dev/null 2>&1; then echo "Required GitHub secret COMPOSE_ENV_STAGING is not configured." >&2
sudo -n chmod 666 /var/run/docker.sock exit 1
else
chmod 666 /var/run/docker.sock
fi fi
docker ps >/dev/null printf '%s\n' "$COMPOSE_ENV" > .env
printf '%s\n' "$COMPOSE_ENV_STAGING" > .env.staging
- name: Materialize CI compose env files
run: |
set -euo pipefail
cp .github/ci.env .env
cp .github/ci.env.staging .env.staging
- name: Validate compose contracts - name: Validate compose contracts
run: | run: |
@@ -138,6 +109,8 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
cache: npm
cache-dependency-path: services/edge-broker/package-lock.json
- name: Install dependencies - name: Install dependencies
working-directory: services/edge-broker working-directory: services/edge-broker
@@ -147,240 +120,139 @@ jobs:
working-directory: services/edge-broker working-directory: services/edge-broker
run: npm test run: npm test
edge-gateway-backend: integration:
name: Edge Gateway Backend (required) name: Integration (advisory)
runs-on: [self-hosted, Linux, X64, pleno, backend, docker] runs-on: [self-hosted, Linux, X64, default]
env: continue-on-error: true
COMPOSE_FILE: docker-compose.yml:.github/docker-compose.ci.yml
COMPOSE_PROJECT_NAME: edge-gateway-backend-${{ github.run_id }}-${{ github.run_attempt }} services:
COMPOSE_PROFILES: dev redis:
TRAEFIK_WEB_PORT: "18080" image: redis:7
TRAEFIK_WEBSECURE_PORT: "18443" ports:
TRAEFIK_WEBSECURE_STAGING_PORT: "18433" - 6379:6379
TRAEFIK_METRICS_PORT: "19100" mysql:
EDGE_BROKER_CI_PORT: "14300" image: mysql:8
EDGE_GATEWAY_E2E_BASE_URL: "http://localhost:18080/api" env:
MYSQL_DATABASE: app_test
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -proot"
--health-interval=10s
--health-timeout=5s
--health-retries=10
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Ensure Docker access - name: Setup PHP
run: | uses: shivammathur/setup-php@v2
set -euo pipefail
if docker ps >/dev/null 2>&1; then
exit 0
fi
test -S /var/run/docker.sock || (echo "Docker socket is not available." >&2; exit 1)
if command -v sudo >/dev/null 2>&1; then
sudo -n chmod 666 /var/run/docker.sock
else
chmod 666 /var/run/docker.sock
fi
docker ps >/dev/null
- name: Allocate CI ports
run: |
set -euo pipefail
find_free_port() {
start="$1"
end="$2"
port="$start"
while [ "$port" -le "$end" ]; do
if ! ss -H -ltn "sport = :$port" 2>/dev/null | grep -q .; then
echo "$port"
return 0
fi
port=$((port + 1))
done
echo "No free port in range ${start}-${end}." >&2
exit 1
}
base=$((20000 + (GITHUB_RUN_ID % 20000)))
web_port="$(find_free_port "$base" "$((base + 2000))")"
websecure_port="$(find_free_port "$((web_port + 1))" "$((web_port + 2000))")"
staging_port="$(find_free_port "$((websecure_port + 1))" "$((websecure_port + 2000))")"
metrics_port="$(find_free_port "$((staging_port + 1))" "$((staging_port + 2000))")"
broker_port="$(find_free_port "$((metrics_port + 1))" "$((metrics_port + 2000))")"
checksum="$(printf '%s' "$COMPOSE_PROJECT_NAME" | cksum | awk '{print $1}')"
subnet_second=$((64 + ((checksum / 256) % 64)))
subnet_third=$((checksum % 256))
ci_docker_subnet="10.${subnet_second}.${subnet_third}.0/24"
{
echo "TRAEFIK_WEB_PORT=${web_port}"
echo "TRAEFIK_WEBSECURE_PORT=${websecure_port}"
echo "TRAEFIK_WEBSECURE_STAGING_PORT=${staging_port}"
echo "TRAEFIK_METRICS_PORT=${metrics_port}"
echo "EDGE_BROKER_CI_PORT=${broker_port}"
echo "CI_DOCKER_SUBNET=${ci_docker_subnet}"
echo "EDGE_GATEWAY_E2E_BASE_URL=http://localhost:${web_port}/api"
echo "EDGE_GATEWAY_E2E_COMPOSE_PROJECT=${COMPOSE_PROJECT_NAME}"
} >> "$GITHUB_ENV"
- name: Materialize CI compose env files
run: |
set -euo pipefail
cp .github/ci.env .env
cp .github/ci.env.staging .env.staging
printf '\nEDGE_PUBLIC_BROKER_URL=http://edge-broker:4300/edge-broker\n' >> .env
- name: Setup Node.js
uses: actions/setup-node@v4
with: with:
node-version: 22 php-version: '8.2'
extensions: mysqli, curl, openssl, json, redis
- name: Boot local stack ini-values: variables_order=EGPCS
run: sh scripts/ci-docker-compose-up.sh traefik redis mysql-debug edge-broker php1 php2 php3 php4 php5 caddy
- name: Sync PHP app checkout
run: >
tar
--exclude='./vendor'
--exclude='./.phpunit.cache'
--exclude='./build/logs'
-C services/nginx/app -cf - .
| docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 tar --no-same-owner -C /var/www/html -xf -
- name: Resolve dependencies - name: Resolve dependencies
run: | working-directory: services/nginx/app
set -euo pipefail run: composer update --no-interaction --prefer-dist
composer_install() {
install_mode="$1"
max_attempts="$2"
attempt=1
while :; do
if docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc "cd /var/www/html && composer install --no-interaction ${install_mode} --no-progress"; then
return 0
fi
if [ "$attempt" -ge "$max_attempts" ]; then
return 1
fi
sleep_seconds=$((attempt * 5))
echo "composer install ${install_mode} failed; retrying in ${sleep_seconds}s (attempt $((attempt + 1))/${max_attempts})" >&2
sleep "$sleep_seconds"
attempt=$((attempt + 1))
done
}
composer_install --prefer-dist 3 || { - name: Run integration tests
echo "Composer dist install failed; retrying with --prefer-source." >&2 working-directory: services/nginx/app
composer_install --prefer-source 2
}
- name: Verify edge gateway test files
run: >
docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc
"cd /var/www/html &&
php -r '\$composer = json_decode(file_get_contents(\"composer.json\"), true); echo \"Composer scripts: \", implode(\",\", array_keys(\$composer[\"scripts\"] ?? [])), PHP_EOL;' &&
find tests/Api -maxdepth 1 -type f -name 'EdgeGateway*ApiTest.php' -print &&
test -f tests/Api/EdgeGatewayAgentApiTest.php &&
test -f tests/Api/EdgeGatewayBrokerApiTest.php &&
test -f tests/Api/EdgeGatewayOperatorApiTest.php"
- name: Run edge gateway API tests
run: >
docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc
"cd /var/www/html &&
RUN_API_TESTS=1
API_TEST_BOOTSTRAP_SCHEMA=1
API_TEST_ALLOW_LIVE_DB=1
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=\${CONFIG_DB_USER:-root}
CONFIG_DB_PASSWORD=\${CONFIG_DB_PASSWORD:-debug_root_password}
CONFIG_DB_DATABASE=\${CONFIG_DB_DATABASE:-nnks_db_debug}
CONFIG_DB_PORT=3306
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=\${CONFIG_DB_DEBUG_USER:-root}
CONFIG_DB_DEBUG_PASSWORD=\${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
CONFIG_DB_DEBUG_DATABASE=\${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
CONFIG_DB_DEBUG_PORT=3306
API_TEST_REQUEST_TIMEOUT=180
EDGE_GATEWAY_VIEW_CACHE_TTL=0
EDGE_BROKER_URL=
vendor/bin/pest
tests/Api/EdgeGatewayAgentApiTest.php
tests/Api/EdgeGatewayBrokerApiTest.php
tests/Api/EdgeGatewayOperatorApiTest.php
--colors=always"
- name: Run edge gateway integration tests
run: >
docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc
"cd /var/www/html &&
RUN_INTEGRATION_TESTS=1
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=\${CONFIG_DB_USER:-root}
CONFIG_DB_PASSWORD=\${CONFIG_DB_PASSWORD:-debug_root_password}
CONFIG_DB_DATABASE=\${CONFIG_DB_DATABASE:-nnks_db_debug}
CONFIG_DB_PORT=3306
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=\${CONFIG_DB_DEBUG_USER:-root}
CONFIG_DB_DEBUG_PASSWORD=\${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
CONFIG_DB_DEBUG_DATABASE=\${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
CONFIG_DB_DEBUG_PORT=3306
EDGE_BROKER_URL=
vendor/bin/pest tests/Integration/EdgeGateway --colors=always"
- name: Run edge gateway E2E smoke
env: env:
EDGE_GATEWAY_E2E_COPY_CONFIG: "true" RUN_INTEGRATION_TESTS: '1'
EDGE_GATEWAY_E2E_SKIP_COMPOSE_UP: "true" USE_ENV: 'true'
run: node scripts/edge-gateway-e2e.mjs DEBUG: '0'
ENCRYPTION_KEY: test-key
CORS: '*'
CONFIG_TIMEZONE: Europe/Copenhagen
ECONOMIC_API_APP_ACCESS_GRANT: test
ECONOMIC_API_APP_ACCESS_GRANT2: test
ECONOMIC_API_APP_SECRET_TOKEN: test
WORDPRESS_STATIC_TOKEN: ''
EMAIL_WASH_CERTIFICATE_TOKEN: ''
WORDPRESS_API_URL: http://localhost
MINIO_ENDPOINT: ''
MINIO_ACCESS_KEY: ''
MINIO_SECRET_KEY: ''
SLACK_DEFAULT_WEBHOOK: ''
REDIS_CONFIG_HOST: 127.0.0.1
REDIS_CONFIG_DATABASE: '0'
REDIS_CONFIG_PASSWORD: ''
REDIS_CONFIG_PORT: '6379'
CONFIG_DB_HOST: 127.0.0.1
CONFIG_DB_USER: root
CONFIG_DB_PASSWORD: root
CONFIG_DB_DATABASE: app_test
CONFIG_DB_PORT: '3306'
run: composer test:integration
- name: Tear down local stack api:
if: always() name: API (advisory)
run: docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml down -v runs-on: [self-hosted, Linux, X64, default]
continue-on-error: true
release-manager-gate: services:
name: Release Manager gate redis:
runs-on: [self-hosted, Linux, X64, pleno, backend] image: redis:7
needs: [php, edge-agent, edge-broker, edge-gateway-backend] ports:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - 6379:6379
mysql:
image: mysql:8
env:
MYSQL_DATABASE: app_test
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -proot"
--health-interval=10s
--health-timeout=5s
--health-retries=10
steps: steps:
- name: Record Release Manager API gate - name: Checkout
run: | uses: actions/checkout@v4
set -euo pipefail
test -n "$RELEASE_MANAGER_GATE_TOKEN" || (echo "RELEASE_MANAGER_GATE_TOKEN is required" >&2; exit 1)
response_file="$(mktemp)"
http_code="$(curl --show-error --silent \
--connect-timeout 10 \
--retry 5 \
--retry-all-errors \
--retry-delay 15 \
--retry-max-time 300 \
-o "$response_file" \
-w '%{http_code}' \
-X POST "$RELEASE_MANAGER_GATE_URL" \
-H "Authorization: Bearer $RELEASE_MANAGER_GATE_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"channel_slug\":\"stable\",\"app\":\"api\",\"repository\":\"$RELEASE_REPOSITORY\",\"branch\":\"$RELEASE_BRANCH\",\"expected_commit\":\"$RELEASE_EXPECTED_COMMIT\",\"workflow_url\":\"$RELEASE_WORKFLOW_URL\",\"auto_sync\":true,\"wait_timeout_seconds\":300,\"poll_interval_seconds\":10,\"required_checks\":[\"api_gateway\"]}")"
response_body="$(cat "$response_file")"
rm -f "$response_file"
if [[ "$http_code" =~ ^2[0-9][0-9]$ ]]; then - name: Setup PHP
printf '%s\n' "$response_body" uses: shivammathur/setup-php@v2
exit 0 with:
fi php-version: '8.2'
extensions: mysqli, curl, openssl, json, redis
ini-values: variables_order=EGPCS
if printf '%s' "$response_body" | grep -qi '<b>Parse error</b>'; then - name: Resolve dependencies
echo "::warning::Release Manager API returned a PHP parse error while recording the gate. Treating this as a break-glass pass so a fix can be deployed." working-directory: services/nginx/app
printf '%s\n' "$response_body" run: composer update --no-interaction --prefer-dist
exit 0
fi
printf '%s\n' "$response_body" - name: Run API tests
echo "Release Manager gate failed with HTTP $http_code." >&2 working-directory: services/nginx/app
exit 1
env: env:
RELEASE_MANAGER_GATE_URL: ${{ secrets.RELEASE_MANAGER_GATE_URL || 'https://api.truckwash.io/release/gate/test-runs' }} RUN_API_TESTS: '1'
RELEASE_MANAGER_GATE_TOKEN: ${{ secrets.RELEASE_MANAGER_GATE_TOKEN }} API_TEST_BOOTSTRAP_SCHEMA: '1'
RELEASE_REPOSITORY: ${{ github.repository }} USE_ENV: 'true'
RELEASE_BRANCH: ${{ github.ref_name }} DEBUG: '0'
RELEASE_EXPECTED_COMMIT: ${{ github.sha }} ENCRYPTION_KEY: test-key
RELEASE_WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} CORS: '*'
CONFIG_TIMEZONE: Europe/Copenhagen
ECONOMIC_API_APP_ACCESS_GRANT: test
ECONOMIC_API_APP_ACCESS_GRANT2: test
ECONOMIC_API_APP_SECRET_TOKEN: test
WORDPRESS_STATIC_TOKEN: ''
EMAIL_WASH_CERTIFICATE_TOKEN: ''
WORDPRESS_API_URL: http://localhost
MINIO_ENDPOINT: ''
MINIO_ACCESS_KEY: ''
MINIO_SECRET_KEY: ''
SLACK_DEFAULT_WEBHOOK: ''
REDIS_CONFIG_HOST: 127.0.0.1
REDIS_CONFIG_DATABASE: '0'
REDIS_CONFIG_PASSWORD: ''
REDIS_CONFIG_PORT: '6379'
CONFIG_DB_HOST: 127.0.0.1
CONFIG_DB_USER: root
CONFIG_DB_PASSWORD: root
CONFIG_DB_DATABASE: app_test
CONFIG_DB_PORT: '3306'
run: composer test:api
-6
View File
@@ -10,10 +10,4 @@
/.idea/ /.idea/
.env .env
/services/caddy/logs* /services/caddy/logs*
.env.old
/.tmp/ /.tmp/
/.env.staging
/services/nginx/app/storage/replication-bootstrap.json
/.env_old_2
/.openclaw/
/services/nginx/app/build/phpstan/
+2 -4
View File
@@ -40,15 +40,13 @@ COPY . /var/www/html
# Copy Nginx configuration file # Copy Nginx configuration file
COPY nginx.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/nginx.conf
COPY services/php/php-fpm-pool.conf /usr/local/etc/php-fpm.d/zz-pleno-workers.conf
# Install Composer # Install Composer
COPY --from=composer:2.6 /usr/bin/composer /usr/bin/composer COPY --from=composer:2.6 /usr/bin/composer /usr/bin/composer
# Runtime bootstrap: lightweight entrypoint to ensure Composer deps exist when app is bind-mounted # Runtime bootstrap: lightweight entrypoint to ensure Composer deps exist when app is bind-mounted
COPY services/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh COPY services/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh \ RUN chmod +x /usr/local/bin/docker-entrypoint.sh
&& chmod +x /usr/local/bin/docker-entrypoint.sh
# Install PHP dependencies through Composer (only where composer.json exists) # Install PHP dependencies through Composer (only where composer.json exists)
# Main app dependencies # Main app dependencies
@@ -74,4 +72,4 @@ EXPOSE 80 443
ENTRYPOINT ["docker-entrypoint.sh"] ENTRYPOINT ["docker-entrypoint.sh"]
# Start services when no command is provided (docker-compose overrides this with ["php-fpm"]) # Start services when no command is provided (docker-compose overrides this with ["php-fpm"])
CMD ["php-fpm"] CMD ["php-fpm"]
-75
View File
@@ -1,75 +0,0 @@
FROM php:8.2.15-fpm
WORKDIR /var/www/html
COPY --from=composer:2.6 /usr/bin/composer /usr/bin/composer
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
$PHPIZE_DEPS \
ca-certificates \
curl \
default-mysql-client \
git \
imagemagick \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmagickcore-dev \
libmagickwand-dev \
libonig-dev \
libpng-dev \
libssl-dev \
libxml2-dev \
libzip-dev \
mariadb-client \
nginx \
pkg-config \
redis-tools \
unzip \
zip; \
update-ca-certificates; \
docker-php-ext-configure gd --with-freetype --with-jpeg; \
docker-php-ext-install -j"$(nproc)" \
bcmath \
exif \
gd \
mbstring \
mysqli \
pcntl \
pdo_mysql \
sockets \
zip; \
pecl install imagick-3.7.0 redis; \
docker-php-ext-enable imagick redis; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $PHPIZE_DEPS; \
rm -rf /var/lib/apt/lists/*
COPY services/nginx/app/ /var/www/html/
COPY services/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY services/php/php-fpm-pool.conf /usr/local/etc/php-fpm.d/zz-pleno-workers.conf
COPY services/coolify/api/nginx.conf /etc/nginx/nginx.conf
COPY services/coolify/api/start.sh /usr/local/bin/coolify-api-start
RUN set -eux; \
rm -f /var/www/html/storage/replication-bootstrap.json /var/www/html/storage/replication-bootstrap-*.json; \
sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh /usr/local/bin/coolify-api-start; \
chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/coolify-api-start; \
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction -d /var/www/html; \
if [ -f /var/www/html/modules/washcertificates/composer.json ]; then \
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction -d /var/www/html/modules/washcertificates; \
fi; \
COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload --no-dev --optimize --no-interaction -d /var/www/html; \
php -d display_errors=1 -r 'require "/var/www/html/vendor/autoload.php"; exit(interface_exists("Psr\\Http\\Message\\UriInterface") && interface_exists("Psr\\Http\\Message\\StreamInterface") ? 0 : 1);'; \
chown -R www-data:www-data /var/www/html; \
chmod -R 755 /var/www/html
ENV APP_DIR=/var/www/html \
MODULE_DIR=/var/www/html/modules/washcertificates \
AUTO_COMPOSER_INSTALL=false \
COMPOSER_ALLOW_SUPERUSER=1
EXPOSE 80
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["coolify-api-start"]
-29
View File
@@ -70,11 +70,6 @@ When updating `.env` values used by PHP containers (for example e-conomic tokens
docker compose up -d --force-recreate php1 php2 php3 php4 php5 php-cron docker compose up -d --force-recreate php1 php2 php3 php4 php5 php-cron
``` ```
#### Edge Broker Public URL
Set `EDGE_PUBLIC_BROKER_URL` to the public route that serves the edge broker, including the path prefix handled by the proxy. Local Traefik uses `http://localhost/api/edge-broker`; production routes use the public broker prefix, for example `https://api.truckwash.dk/edge-broker`.
The browser terminal connects to the exact advertised `EDGE_PUBLIC_BROKER_URL` plus `/ws/browser-shell`. That URL must be routable through the proxy to the edge-broker service. Do not rely on derived `/api/edge-broker` fallback paths outside the local Traefik setup.
## Testing ## Testing
The project now uses [Pest](https://pestphp.com/) as the primary test runner in `services/nginx/app`. The project now uses [Pest](https://pestphp.com/) as the primary test runner in `services/nginx/app`.
@@ -96,8 +91,6 @@ For local Docker development, run the PHP suites inside `php1`:
docker exec php1 sh -lc "cd /var/www/html && composer test:unit" docker exec php1 sh -lc "cd /var/www/html && composer test:unit"
docker exec php1 sh -lc "cd /var/www/html && composer test:integration" docker exec php1 sh -lc "cd /var/www/html && composer test:integration"
docker exec php1 sh -lc "cd /var/www/html && composer test:api" docker exec php1 sh -lc "cd /var/www/html && composer test:api"
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:api:edge"
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:integration:edge"
``` ```
Integration tests are opt-in and should be run with required services available: Integration tests are opt-in and should be run with required services available:
@@ -107,28 +100,6 @@ $env:RUN_INTEGRATION_TESTS='1'
composer test:integration composer test:integration
``` ```
### Edge Gateway Regression Coverage
The dedicated backend regression lane for the PHP edge gateway stack is split into:
- API contract tests for operator, agent, and broker-facing routes
- DB-backed integration tests for install sessions, heartbeats, tasks, logs, statistics, and shell persistence
- a local dockerized smoke that runs the real PHP edge agent against the local backend and broker
Run the targeted PHP suites inside `php1`:
```powershell
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:api:edge"
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:integration:edge"
```
Run the full local smoke from `backend-php` on the host:
```powershell
node .\scripts\edge-gateway-e2e.mjs
```
The E2E smoke expects the local compose stack and Docker daemon to be available. It boots a disposable gateway container, waits for a real heartbeat, validates live operations and telemetry, and verifies browser shell transcript persistence.
### Public Staging Edge-Gateway Smoke ### Public Staging Edge-Gateway Smoke
`api.truckwash.io:4433` is the public staging ingress. For Edge Gateways v2, the router must serve the canonical artifacts from `services/nginx/app/resources/edge-gateway-agent`, not from the legacy `dist/agent.mjs` output or a separate runtime mount. `api.truckwash.io:4433` is the public staging ingress. For Edge Gateways v2, the router must serve the canonical artifacts from `services/nginx/app/resources/edge-gateway-agent`, not from the legacy `dist/agent.mjs` output or a separate runtime mount.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -10,7 +10,7 @@ $CONFIG_DB = [
$DEBUG = true; // Set to true to enable debugging (Error messages will be shown, and this should never be used in production) $DEBUG = true; // Set to true to enable debugging (Error messages will be shown, and this should never be used in production)
$USE_PROD_ECONOMIC_IN_DEBUG = true; // Set to true to use the production economic API in debug mode $USE_PROD_ECONOMIC_IN_DEBUG = true; // Set to true to use the production economic API in debug mode
$ENCRYPTION_KEY = ''; // 44 Characters long encryption key $ENCRYPTION_KEY = ''; // 44 Characters long encryption key
$CORS = '*'; // Set to comma-separated allowed origins e.g. https://example.com,https://api-v2.truckwash.io $CORS = '*'; // Set to the domain that should be allowed to access the API e.g. https://example.com
$ECONOMIC_API = [ $ECONOMIC_API = [
'app_access_grant' => '', // Economic API access grant token (1) 'app_access_grant' => '', // Economic API access grant token (1)
'app_access_grant2' => '', // Economic API access grant token (2) 'app_access_grant2' => '', // Economic API access grant token (2)
+5 -21
View File
@@ -52,25 +52,9 @@ services:
dockerfile: services/edge-broker/Dockerfile dockerfile: services/edge-broker/Dockerfile
container_name: edge-broker container_name: edge-broker
environment: environment:
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-strict} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
EDGE_MANAGER_URL: ${EDGE_MANAGER_URL:-http://caddy} ports:
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} - "4300:4300"
labels:
- "traefik.enable=true"
- "traefik.http.routers.edge-broker-api.rule=Host(`api.example.com`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api.tls=true"
- "traefik.http.routers.edge-broker-api.tls.certresolver=le"
- "traefik.http.routers.edge-broker-api.middlewares=edge-broker-strip"
- "traefik.http.routers.edge-broker-api.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.middlewares.edge-broker-strip.stripPrefix.prefixes=/edge-broker"
- "traefik.http.middlewares.edge-broker-strip-local.stripPrefix.prefixes=/api/edge-broker"
- "traefik.http.services.edge-broker.loadbalancer.server.port=4300"
caddy: caddy:
image: caddy:2.7.6-alpine image: caddy:2.7.6-alpine
@@ -114,7 +98,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "true" AUTO_COMPOSER_INSTALL: "true"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -135,7 +119,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
-440
View File
@@ -1,440 +0,0 @@
services:
traefik:
image: traefik:2.11
container_name: traefik
ports:
- "80:80"
- "443:443"
- "4433:4433"
- "9100:9100"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./services/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- ./services/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
- ./services/traefik/acme.json:/acme.json
- ./services/traefik/acme-io.json:/acme-io.json
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.truckwash.dk`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.tls.certresolver=le"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=dashboard-allow-local@file,dashboard-auth@file"
- "traefik.http.routers.traefik-http.rule=Host(`traefik.truckwash.dk`)"
- "traefik.http.routers.traefik-http.entrypoints=web"
- "traefik.http.routers.traefik-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.traefik-http.service=api@internal"
- "traefik.http.routers.traefik-local.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.traefik-local.entrypoints=web"
- "traefik.http.routers.traefik-local.service=api@internal"
- "traefik.http.routers.traefik-local.middlewares=dashboard-allow-local@file,dashboard-auth@file"
redis:
image: redis:7
container_name: redis
volumes:
- nnks_redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
redis-staging:
image: redis:7
container_name: redis-staging
volumes:
- nnks_redis_staging:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
mysql-debug:
image: mysql:8.4
container_name: mysql-debug
environment:
MYSQL_ROOT_PASSWORD: ${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
MYSQL_DATABASE: ${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
ports:
- "3307:3306"
volumes:
- db_debug_data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "MYSQL_PWD=$$MYSQL_ROOT_PASSWORD mysqladmin -u root ping --silent"]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
edge-broker:
build:
context: .
dockerfile: services/edge-broker/Dockerfile
container_name: edge-broker
environment:
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-manager}
EDGE_MANAGER_URL: ${EDGE_MANAGER_URL:-http://caddy}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
labels:
- "traefik.enable=true"
- "traefik.http.routers.edge-broker-api.rule=Host(`api.truckwash.dk`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api.tls=true"
- "traefik.http.routers.edge-broker-api.tls.certresolver=le"
- "traefik.http.routers.edge-broker-api.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api.priority=200"
- "traefik.http.routers.edge-broker-api.service=edge-broker"
- "traefik.http.routers.edge-broker-api-io.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-io.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-io.tls=true"
- "traefik.http.routers.edge-broker-api-io.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-io.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-io.priority=200"
- "traefik.http.routers.edge-broker-api-io.service=edge-broker"
- "traefik.http.routers.edge-broker-api-v2.rule=Host(`api-v2.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-v2.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-v2.tls=true"
- "traefik.http.routers.edge-broker-api-v2.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-v2.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-v2.priority=200"
- "traefik.http.routers.edge-broker-api-v2.service=edge-broker"
- "traefik.http.routers.edge-broker-api-staging.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-api-staging.tls=true"
- "traefik.http.routers.edge-broker-api-staging.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-staging.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-staging.priority=200"
- "traefik.http.routers.edge-broker-api-staging.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.priority=200"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.routers.edge-broker-local-secure.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-secure.entrypoints=websecure"
- "traefik.http.routers.edge-broker-local-secure.tls=true"
- "traefik.http.routers.edge-broker-local-secure.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-secure.priority=200"
- "traefik.http.routers.edge-broker-local-secure.service=edge-broker"
- "traefik.http.routers.edge-broker-local-staging.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-local-staging.tls=true"
- "traefik.http.routers.edge-broker-local-staging.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-staging.priority=200"
- "traefik.http.routers.edge-broker-local-staging.service=edge-broker"
- "traefik.http.middlewares.edge-broker-strip.stripPrefix.prefixes=/edge-broker"
- "traefik.http.middlewares.edge-broker-strip-local.stripPrefix.prefixes=/api/edge-broker"
- "traefik.http.services.edge-broker.loadbalancer.server.port=4300"
caddy:
image: caddy:2.7.6-alpine
container_name: caddy
depends_on:
- php1
- php2
- php3
- php4
- php5
volumes:
- ./services/nginx/app:/var/www/html
- ./services/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./services/caddy/logs:/var/log/caddy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.truckwash.dk`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls=true"
- "traefik.http.routers.api.tls.domains[0].main=api.truckwash.dk"
- "traefik.http.routers.api.tls.certresolver=le"
- "traefik.http.routers.api.service=caddy"
- "traefik.http.routers.api.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.api-io.rule=Host(`api.truckwash.io`)"
- "traefik.http.routers.api-io.entrypoints=websecure"
- "traefik.http.routers.api-io.tls=true"
- "traefik.http.routers.api-io.tls.domains[0].main=api.truckwash.io"
- "traefik.http.routers.api-io.tls.certresolver=le_io"
- "traefik.http.routers.api-io.service=caddy"
- "traefik.http.routers.api-io.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.api-v2.rule=Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-v2.entrypoints=websecure"
- "traefik.http.routers.api-v2.tls=true"
- "traefik.http.routers.api-v2.tls.domains[0].main=api-v2.truckwash.io"
- "traefik.http.routers.api-v2.tls.certresolver=le_io"
- "traefik.http.routers.api-v2.service=caddy"
- "traefik.http.routers.api-v2.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.api-http.rule=Host(`api.truckwash.dk`) || Host(`api.truckwash.io`) || Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-http.entrypoints=web"
- "traefik.http.routers.api-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.api-http.service=caddy"
- "traefik.http.routers.local.rule=Host(`localhost`)"
- "traefik.http.routers.local.entrypoints=web"
- "traefik.http.routers.local.service=caddy"
- "traefik.http.routers.local.middlewares=secure-headers@file"
- "traefik.http.routers.local-secure.rule=Host(`localhost`)"
- "traefik.http.routers.local-secure.entrypoints=websecure"
- "traefik.http.routers.local-secure.tls=true"
- "traefik.http.routers.local-secure.service=caddy"
- "traefik.http.routers.local-secure.middlewares=secure-headers@file"
- "traefik.http.routers.local-api.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-api.entrypoints=web"
- "traefik.http.routers.local-api.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api.service=caddy"
- "traefik.http.routers.local-api.priority=100"
- "traefik.http.routers.local-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-api-secure.entrypoints=websecure"
- "traefik.http.routers.local-api-secure.tls=true"
- "traefik.http.routers.local-api-secure.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api-secure.service=caddy"
- "traefik.http.routers.local-api-secure.priority=100"
- "traefik.http.services.caddy.loadbalancer.server.port=80"
caddy-staging:
image: caddy:2.7.6-alpine
container_name: caddy-staging
depends_on:
- php-staging
volumes:
- ./services/nginx/staging:/var/www/html
- ./services/caddy/Caddyfile-staging:/etc/caddy/Caddyfile:ro
- ./services/caddy/logs-staging:/var/log/caddy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-staging.rule=Host(`api.truckwash.io`)"
- "traefik.http.routers.api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.api-staging.tls=true"
- "traefik.http.routers.api-staging.tls.domains[0].main=api.truckwash.io"
- "traefik.http.routers.api-staging.tls.certresolver=le_io"
- "traefik.http.routers.api-staging.service=caddy-staging"
- "traefik.http.routers.api-staging.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.local-staging.rule=Host(`localhost`)"
- "traefik.http.routers.local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging.service=caddy-staging"
- "traefik.http.routers.local-staging.middlewares=secure-headers@file"
- "traefik.http.routers.local-staging-secure.rule=Host(`localhost`)"
- "traefik.http.routers.local-staging-secure.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-secure.tls=true"
- "traefik.http.routers.local-staging-secure.service=caddy-staging"
- "traefik.http.routers.local-staging-secure.middlewares=secure-headers@file"
- "traefik.http.routers.local-staging-api.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-staging-api.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-api.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-staging-api.service=caddy-staging"
- "traefik.http.routers.local-staging-api.priority=100"
- "traefik.http.routers.local-staging-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-staging-api-secure.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-api-secure.tls=true"
- "traefik.http.routers.local-staging-api-secure.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-staging-api-secure.service=caddy-staging"
- "traefik.http.routers.local-staging-api-secure.priority=100"
- "traefik.http.services.caddy-staging.loadbalancer.server.port=80"
php1:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php1
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "true"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php2:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php2
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php3:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php3
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php4:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php4
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php5:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php5
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php-staging:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php-staging
depends_on:
- redis-staging
- edge-broker
command: ["php-fpm"]
env_file:
- .env.staging
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/staging:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs-staging:/var/log/php
php-cron:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php-cron
depends_on:
- redis
- edge-broker
command: ["sh", "-c", "while true; do php index.php run cron; sleep 60; done"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
portainer:
image: portainer/portainer-ce:2.21.4
container_name: portainer
profiles:
- dev
ports:
- "9443:9443"
- "9000:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
jaeger:
image: jaegertracing/all-in-one:1.53
container_name: jaeger
profiles:
- dev
environment:
- COLLECTOR_ZIPKIN_HTTP_PORT=9411
ports:
- "16686:16686"
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
environment:
- N8N_HOST=n8n.truckwash.io
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://n8n.truckwash.io/
- GENERIC_TIMEZONE=${CONFIG_TIMEZONE:-Europe/Copenhagen}
volumes:
- n8n_data:/home/node/.n8n
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`n8n.truckwash.io`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.routers.n8n.tls.certresolver=le_io"
- "traefik.http.routers.n8n.service=n8n"
- "traefik.http.routers.n8n-http.rule=Host(`n8n.truckwash.io`)"
- "traefik.http.routers.n8n-http.entrypoints=web"
- "traefik.http.routers.n8n-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.n8n-http.service=n8n"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
volumes:
db_data:
db_debug_data:
nnks_redis:
nnks_redis_staging:
es_data:
portainer_data:
fleet-server-data:
elastic-agent-data:
n8n_data:
+1 -1
View File
@@ -27,5 +27,5 @@ services:
## docker compose up -d traefik caddy php1 php2 php3 php4 php5 db redis ## docker compose up -d traefik caddy php1 php2 php3 php4 php5 db redis
## ##
## Notes: ## Notes:
## - Traefik uses Lets Encrypt production. Ensure DNS A/AAAA records for api.truckwash.dk, api.truckwash.io, api-v2.truckwash.io and traefik.truckwash.dk point to the expected ingress and ports 80/443 are reachable. ## - Traefik uses Lets Encrypt production. Ensure DNS A/AAAA records for api.truckwash.dk, api.truckwash.io and traefik.truckwash.dk point to this host and ports 80/443 are reachable.
## - The dashboard is protected by basic auth and an IP allowlist (defined in dynamic.yml). Replace the bcrypt hash before enabling in production. ## - The dashboard is protected by basic auth and an IP allowlist (defined in dynamic.yml). Replace the bcrypt hash before enabling in production.
+20 -118
View File
@@ -3,14 +3,12 @@ services:
traefik: traefik:
image: traefik:2.11 image: traefik:2.11
container_name: traefik container_name: traefik
group_add:
- "${DOCKER_SOCKET_GID:-65534}"
ports: ports:
- "${TRAEFIK_WEB_PORT:-80}:80" - "80:80"
- "${TRAEFIK_WEBSECURE_PORT:-443}:443" - "443:443"
- "${TRAEFIK_WEBSECURE_STAGING_PORT:-4433}:4433" - "4433:4433"
# Prometheus metrics endpoint (local dev) # Prometheus metrics endpoint (local dev)
- "${TRAEFIK_METRICS_PORT:-9100}:9100" - "9100:9100"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- ./services/traefik/traefik.yml:/etc/traefik/traefik.yml:ro - ./services/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
@@ -36,41 +34,6 @@ services:
- "traefik.http.routers.traefik-local.entrypoints=web" - "traefik.http.routers.traefik-local.entrypoints=web"
- "traefik.http.routers.traefik-local.service=api@internal" - "traefik.http.routers.traefik-local.service=api@internal"
- "traefik.http.routers.traefik-local.middlewares=dashboard-allow-local@file,dashboard-auth@file" - "traefik.http.routers.traefik-local.middlewares=dashboard-allow-local@file,dashboard-auth@file"
# Broker API (handled in edge-broker service)
- "traefik.http.routers.edge-broker.rule=Host(`api.truckwash.dk`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker.entrypoints=websecure"
- "traefik.http.routers.edge-broker.tls=true"
- "traefik.http.routers.edge-broker.tls.certresolver=le"
- "traefik.http.routers.edge-broker.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker.priority=200"
- "traefik.http.routers.edge-broker.service=edge-broker"
- "traefik.http.routers.edge-broker-io.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-io.entrypoints=websecure"
- "traefik.http.routers.edge-broker-io.tls=true"
- "traefik.http.routers.edge-broker-io.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-io.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-io.priority=200"
- "traefik.http.routers.edge-broker-io.service=edge-broker"
- "traefik.http.routers.edge-broker-v2.rule=Host(`api-v2.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-v2.entrypoints=websecure"
- "traefik.http.routers.edge-broker-v2.tls=true"
- "traefik.http.routers.edge-broker-v2.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-v2.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-v2.priority=200"
- "traefik.http.routers.edge-broker-v2.service=edge-broker"
- "traefik.http.routers.edge-broker-staging.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-staging.tls=true"
- "traefik.http.routers.edge-broker-staging.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-staging.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-staging.priority=200"
- "traefik.http.routers.edge-broker-staging.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.priority=200"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.routers.edge-broker-local-secure.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
redis: redis:
image: redis:7 image: redis:7
@@ -103,10 +66,8 @@ services:
mysql-debug: mysql-debug:
image: mysql:8.4 image: mysql:8.4
container_name: mysql-debug container_name: mysql-debug
profiles: [dev]
command: ["mysqld", "--innodb-use-native-aio=0"]
environment: environment:
MYSQL_ROOT_PASSWORD: ${CONFIG_DB_DEBUG_PASSWORD:?CONFIG_DB_DEBUG_PASSWORD is required for mysql-debug} MYSQL_ROOT_PASSWORD: ${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
MYSQL_DATABASE: ${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug} MYSQL_DATABASE: ${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
ports: ports:
- "3307:3306" - "3307:3306"
@@ -125,59 +86,9 @@ services:
dockerfile: services/edge-broker/Dockerfile dockerfile: services/edge-broker/Dockerfile
container_name: edge-broker container_name: edge-broker
environment: environment:
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-strict} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
EDGE_MANAGER_URL: ${EDGE_MANAGER_URL:-http://caddy} ports:
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} - "4300:4300"
labels:
- "traefik.enable=true"
- "traefik.http.routers.edge-broker-api.rule=Host(`api.truckwash.dk`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api.tls=true"
- "traefik.http.routers.edge-broker-api.tls.certresolver=le"
- "traefik.http.routers.edge-broker-api.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api.priority=200"
- "traefik.http.routers.edge-broker-api.service=edge-broker"
- "traefik.http.routers.edge-broker-api-io.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-io.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-io.tls=true"
- "traefik.http.routers.edge-broker-api-io.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-io.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-io.priority=200"
- "traefik.http.routers.edge-broker-api-io.service=edge-broker"
- "traefik.http.routers.edge-broker-api-v2.rule=Host(`api-v2.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-v2.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-v2.tls=true"
- "traefik.http.routers.edge-broker-api-v2.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-v2.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-v2.priority=200"
- "traefik.http.routers.edge-broker-api-v2.service=edge-broker"
- "traefik.http.routers.edge-broker-api-staging.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-api-staging.tls=true"
- "traefik.http.routers.edge-broker-api-staging.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-staging.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-staging.priority=200"
- "traefik.http.routers.edge-broker-api-staging.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.priority=200"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.routers.edge-broker-local-secure.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-secure.entrypoints=websecure"
- "traefik.http.routers.edge-broker-local-secure.tls=true"
- "traefik.http.routers.edge-broker-local-secure.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-secure.priority=200"
- "traefik.http.routers.edge-broker-local-secure.service=edge-broker"
- "traefik.http.routers.edge-broker-local-staging.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-local-staging.tls=true"
- "traefik.http.routers.edge-broker-local-staging.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-staging.priority=200"
- "traefik.http.routers.edge-broker-local-staging.service=edge-broker"
- "traefik.http.middlewares.edge-broker-strip.stripPrefix.prefixes=/edge-broker"
- "traefik.http.middlewares.edge-broker-strip-local.stripPrefix.prefixes=/api/edge-broker"
- "traefik.http.services.edge-broker.loadbalancer.server.port=4300"
caddy: caddy:
image: caddy:2.7.6-alpine image: caddy:2.7.6-alpine
@@ -190,7 +101,7 @@ services:
- php5 - php5
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/caddy:/etc/caddy:ro - ./services/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./services/caddy/logs:/var/log/caddy - ./services/caddy/logs:/var/log/caddy
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -210,16 +121,8 @@ services:
- "traefik.http.routers.api-io.tls.certresolver=le_io" - "traefik.http.routers.api-io.tls.certresolver=le_io"
- "traefik.http.routers.api-io.service=caddy" - "traefik.http.routers.api-io.service=caddy"
- "traefik.http.routers.api-io.middlewares=secure-headers@file,api-ratelimit@file" - "traefik.http.routers.api-io.middlewares=secure-headers@file,api-ratelimit@file"
# Public API (.io load-balanced gateway)
- "traefik.http.routers.api-v2.rule=Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-v2.entrypoints=websecure"
- "traefik.http.routers.api-v2.tls=true"
- "traefik.http.routers.api-v2.tls.domains[0].main=api-v2.truckwash.io"
- "traefik.http.routers.api-v2.tls.certresolver=le_io"
- "traefik.http.routers.api-v2.service=caddy"
- "traefik.http.routers.api-v2.middlewares=secure-headers@file,api-ratelimit@file"
# HTTP to HTTPS redirect for both API domains # HTTP to HTTPS redirect for both API domains
- "traefik.http.routers.api-http.rule=Host(`api.truckwash.dk`) || Host(`api.truckwash.io`) || Host(`api-v2.truckwash.io`)" - "traefik.http.routers.api-http.rule=Host(`api.truckwash.dk`) || Host(`api.truckwash.io`)"
- "traefik.http.routers.api-http.entrypoints=web" - "traefik.http.routers.api-http.entrypoints=web"
- "traefik.http.routers.api-http.middlewares=redirect-to-https@file" - "traefik.http.routers.api-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.api-http.service=caddy" - "traefik.http.routers.api-http.service=caddy"
@@ -255,10 +158,9 @@ services:
container_name: caddy-staging container_name: caddy-staging
depends_on: depends_on:
- php-staging - php-staging
command: ["caddy", "run", "--config", "/etc/caddy/Caddyfile-staging", "--adapter", "caddyfile"]
volumes: volumes:
- ./services/nginx/staging:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/caddy:/etc/caddy:ro - ./services/caddy/Caddyfile-staging:/etc/caddy/Caddyfile:ro
- ./services/caddy/logs-staging:/var/log/caddy - ./services/caddy/logs-staging:/var/log/caddy
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -311,7 +213,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "true" AUTO_COMPOSER_INSTALL: "true"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -331,7 +233,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -351,7 +253,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -371,7 +273,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -391,7 +293,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -411,9 +313,9 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/staging:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs-staging:/var/log/php - ./services/php/logs-staging:/var/log/php
@@ -431,7 +333,7 @@ services:
environment: environment:
AUTO_COMPOSER_INSTALL: "false" AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env} EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes: volumes:
- ./services/nginx/app:/var/www/html - ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
-105
View File
@@ -3357,9 +3357,6 @@
}, },
"email_notifications_enabled": { "email_notifications_enabled": {
"type": "boolean" "type": "boolean"
},
"superuser_new_customer_email_notifications_enabled": {
"type": "boolean"
} }
} }
} }
@@ -12958,54 +12955,6 @@
} }
} }
}, },
"/slack/config": {
"get": {
"tags": [
"Config"
],
"summary": "Get Slack config",
"operationId": "getSlackConfig",
"responses": {
"200": {
"description": "Slack configuration retrieved successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SlackConfigListResponse"
}
}
}
}
}
},
"post": {
"tags": [
"Config"
],
"summary": "Update Slack config",
"operationId": "updateSlackConfig",
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {}
}
}
},
"responses": {
"200": {
"description": "Slack configuration updated successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ModuleConfigUpdateResponse"
}
}
}
}
}
}
},
"/backups/config": { "/backups/config": {
"get": { "get": {
"tags": [ "tags": [
@@ -15547,39 +15496,6 @@
"value" "value"
] ]
}, },
"SlackConfigEntry": {
"type": "object",
"properties": {
"module": {
"type": "string",
"enum": [
"Slack"
]
},
"variable": {
"type": "string",
"enum": [
"customer_registration_webhook_url"
]
},
"type": {
"type": "string",
"enum": [
"string"
]
},
"value": {
"type": "string",
"example": "https://hooks.slack.com/services/..."
}
},
"required": [
"module",
"variable",
"type",
"value"
]
},
"BackupsConfigEntry": { "BackupsConfigEntry": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -16324,27 +16240,6 @@
} }
] ]
}, },
"SlackConfigListResponse": {
"allOf": [
{
"$ref": "#/components/schemas/ModuleConfigEnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SlackConfigEntry"
}
}
},
"required": [
"data"
]
}
]
},
"BackupsConfigListResponse": { "BackupsConfigListResponse": {
"allOf": [ "allOf": [
{ {
+3 -3
View File
@@ -38,14 +38,14 @@ http {
location / { location / {
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number, X-Release-Trace, X-Release-Channel, X-Frontend-Version"; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number";
add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Credentials true;
# If OPTIONS method is needed for preflight # If OPTIONS method is needed for preflight
if ($request_method = 'OPTIONS') { if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number, X-Release-Trace, X-Release-Channel, X-Frontend-Version"; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number";
return 204; # No Content return 204; # No Content
} }
@@ -65,4 +65,4 @@ http {
location ~* \.(cgi|shtml|phtml)$ { location ~* \.(cgi|shtml|phtml)$ {
} }
} }
} }
+3 -3
View File
@@ -52,14 +52,14 @@ http {
location / { location / {
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number, X-Release-Trace, X-Release-Channel, X-Frontend-Version"; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number";
add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Credentials true;
# If OPTIONS method is needed for preflight # If OPTIONS method is needed for preflight
if ($request_method = 'OPTIONS') { if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number, X-Release-Trace, X-Release-Channel, X-Frontend-Version"; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-Customer-Number";
return 204; # No Content return 204; # No Content
} }
@@ -80,4 +80,4 @@ http {
# Additional SSL options or configurations can be placed here, if necessary. # Additional SSL options or configurations can be placed here, if necessary.
} }
} }
} }
+21 -4272
View File
File diff suppressed because it is too large Load Diff
-132545
View File
File diff suppressed because one or more lines are too long
-102
View File
@@ -1,102 +0,0 @@
#!/usr/bin/env sh
set -eu
suite="${1:-}"
case "$suite" in
unit|integration|api|legacy|all)
;;
*)
echo "Usage: $0 <unit|integration|api|legacy|all>" >&2
exit 2
;;
esac
script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
repo_root="$(CDPATH= cd -- "$script_dir/.." && pwd)"
cd "$repo_root"
compose_files="-f docker-compose.yml -f .github/docker-compose.ci.yml"
project_suffix="$(date +%s)-$$"
export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-php-local-${suite}-${project_suffix}}"
log_dir=".tmp/ci-logs/$suite"
mkdir -p "$log_dir"
env_backup_dir=".tmp/php-ci-env-backup-$project_suffix"
mkdir -p "$env_backup_dir"
had_env=0
had_env_staging=0
if [ -f .env ]; then
cp .env "$env_backup_dir/env"
had_env=1
fi
if [ -f .env.staging ]; then
cp .env.staging "$env_backup_dir/env.staging"
had_env_staging=1
fi
cp .github/ci.env .env
cp .github/ci.env.staging .env.staging
collect_logs() {
status="$1"
if [ "$status" -eq 0 ]; then
return
fi
mkdir -p "$log_dir"
docker compose $compose_files ps > "$log_dir/docker-compose-ps.txt" 2>&1 || true
docker compose $compose_files logs --no-color > "$log_dir/docker-compose.log" 2>&1 || true
docker compose $compose_files cp php1:/var/www/html/build/logs "$log_dir/app-build-logs" >/dev/null 2>&1 || true
docker compose $compose_files cp php1:/var/log/php "$log_dir/php-logs" >/dev/null 2>&1 || true
}
cleanup() {
status="$?"
collect_logs "$status"
docker compose $compose_files down -v >/dev/null 2>&1 || true
if [ "$had_env" -eq 1 ]; then
cp "$env_backup_dir/env" .env
else
rm -f .env
fi
if [ "$had_env_staging" -eq 1 ]; then
cp "$env_backup_dir/env.staging" .env.staging
else
rm -f .env.staging
fi
rm -rf "$env_backup_dir"
exit "$status"
}
trap cleanup EXIT INT TERM
docker compose $compose_files up -d redis mysql-debug php1
docker compose $compose_files exec -T php1 sh -lc '
set -eu
for i in $(seq 1 90); do
if MYSQL_PWD="${CONFIG_DB_PASSWORD:-debug_root_password}" mysqladmin \
-h "${CONFIG_DB_HOST:-mysql-debug}" \
-P "${CONFIG_DB_PORT:-3306}" \
-u "${CONFIG_DB_USER:-root}" \
ping --silent >/dev/null 2>&1; then
exit 0
fi
sleep 1
done
echo "Timed out waiting for mysql-debug" >&2
exit 1
'
tar \
--exclude='./vendor' \
--exclude='./.phpunit.cache' \
--exclude='./build/logs' \
-C services/nginx/app -cf - . \
| docker compose $compose_files exec -T php1 tar -C /var/www/html -xf -
docker compose $compose_files exec -T php1 sh -lc \
'cd /var/www/html && composer install --no-interaction --prefer-dist --no-progress'
docker compose $compose_files exec -T php1 sh -lc \
"cd /var/www/html && composer test:ci:$suite"
-45
View File
@@ -1,45 +0,0 @@
#!/usr/bin/env sh
set -eu
if [ "$#" -eq 0 ]; then
echo "Usage: $0 <service> [service ...]" >&2
exit 2
fi
compose_files="${CI_DOCKER_COMPOSE_FILES:--f docker-compose.yml -f .github/docker-compose.ci.yml}"
lock_file="${CI_DOCKER_LOCK_FILE:-/tmp/pleno-api-ci-docker-compose-up.lock}"
max_attempts="${CI_DOCKER_UP_RETRIES:-${PHP_CI_DOCKER_RETRIES:-3}}"
export COMPOSE_PROFILES="${COMPOSE_PROFILES:-dev}"
compose_up() {
attempt=1
while :; do
docker network prune -f >/dev/null 2>&1 || true
if docker compose $compose_files up -d "$@"; then
return 0
fi
status="$?"
docker compose $compose_files down -v --remove-orphans >/dev/null 2>&1 || true
if [ "$attempt" -ge "$max_attempts" ]; then
return "$status"
fi
sleep_seconds=$((attempt * 5))
echo "Docker compose up failed with status $status; retrying in ${sleep_seconds}s (attempt $((attempt + 1))/$max_attempts)." >&2
sleep "$sleep_seconds"
attempt=$((attempt + 1))
done
}
if command -v flock >/dev/null 2>&1; then
(
flock 9
compose_up "$@"
) 9>"$lock_file"
else
echo "flock is not available; running Docker compose startup without a host lock." >&2
compose_up "$@"
fi
-453
View File
@@ -1,453 +0,0 @@
import { spawn } from "node:child_process";
import crypto from "node:crypto";
import fs from "node:fs";
import http from "node:http";
import net from "node:net";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const DEFAULT_AGENT_PATH = path.join(
repoRoot,
"services/nginx/app/resources/edge-gateway-agent/agent.php"
);
const DEFAULT_PHP_IMAGE = "php:8.2-cli-bookworm";
const DEFAULT_TIMEOUT_MS = 12000;
function parseArgs(argv = process.argv.slice(2)) {
const options = {
agentPath: DEFAULT_AGENT_PATH,
phpImage: DEFAULT_PHP_IMAGE,
timeoutMs: DEFAULT_TIMEOUT_MS,
keepTemp: false,
help: false,
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const next = argv[index + 1];
switch (arg) {
case "--agent-path":
options.agentPath = path.resolve(String(next || "").trim());
index += 1;
break;
case "--php-image":
options.phpImage = String(next || "").trim() || DEFAULT_PHP_IMAGE;
index += 1;
break;
case "--timeout-ms":
options.timeoutMs = Number.parseInt(String(next || ""), 10) || DEFAULT_TIMEOUT_MS;
index += 1;
break;
case "--keep-temp":
options.keepTemp = true;
break;
case "--help":
case "-h":
options.help = true;
break;
default:
throw new Error(`Unknown argument: ${arg}`);
}
}
return options;
}
function printUsage() {
process.stdout.write(`Usage:
node scripts/edge-agent-command-drain-proof.mjs [options]
Verifies that a broker-connected PHP compose edge agent still drains API-queued
SET_RELAY_STATE jobs to the LAN worker /relay/switch endpoint.
Options:
--agent-path <path> PHP agent artifact to execute.
Default: ${DEFAULT_AGENT_PATH}
--php-image <image> Docker PHP image with curl, sqlite3, and pdo_sqlite.
Default: ${DEFAULT_PHP_IMAGE}
--timeout-ms <ms> Proof timeout. Default: ${DEFAULT_TIMEOUT_MS}
--keep-temp Keep the temporary config/runtime directory.
--help Show this help text.
`);
}
function readJson(request) {
return new Promise((resolve) => {
let raw = "";
request.setEncoding("utf8");
request.on("data", (chunk) => {
raw += chunk;
});
request.on("end", () => {
if (raw.trim() === "") {
resolve({});
return;
}
try {
resolve(JSON.parse(raw));
} catch {
resolve({ __invalid: raw });
}
});
});
}
function sendJson(response, status, payload) {
const body = JSON.stringify(payload);
response.writeHead(status, {
"content-type": "application/json; charset=utf-8",
"content-length": Buffer.byteLength(body),
});
response.end(body);
}
function listen(server) {
return new Promise((resolve) => {
server.listen(0, "127.0.0.1", () => resolve(server.address().port));
});
}
function closeServer(server) {
return new Promise((resolve) => {
server.close(() => resolve());
});
}
function websocketAcceptKey(key) {
return crypto
.createHash("sha1")
.update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)
.digest("base64");
}
function createBrokerServer(state) {
const sockets = new Set();
const server = net.createServer((socket) => {
sockets.add(socket);
socket.on("close", () => sockets.delete(socket));
let buffer = "";
socket.on("data", (chunk) => {
buffer += chunk.toString("binary");
if (state.brokerHandshakeSeen || !buffer.includes("\r\n\r\n")) {
return;
}
const requestText = Buffer.from(buffer, "binary").toString("utf8");
const key = requestText.match(/Sec-WebSocket-Key:\s*(.+)\r\n/i)?.[1]?.trim();
const requestLine = requestText.split("\r\n")[0] || "";
if (!requestLine.includes("/ws/agent?")) {
state.failure = new Error(`unexpected broker path: ${requestLine}`);
}
if (!key) {
state.failure = new Error("broker handshake missing Sec-WebSocket-Key");
return;
}
socket.write([
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
`Sec-WebSocket-Accept: ${websocketAcceptKey(key)}`,
"",
"",
].join("\r\n"));
state.brokerHandshakeSeen = true;
buffer = "";
});
});
return { server, sockets };
}
function createWorkerServer(state) {
return http.createServer(async (request, response) => {
const url = new URL(request.url, "http://127.0.0.1");
const body = await readJson(request);
state.requests.push({ service: "worker", method: request.method, path: url.pathname, body });
if (request.method === "GET" && url.pathname === "/health") {
sendJson(response, 200, { status: "healthy", timestamp: new Date().toISOString() });
return;
}
if (request.method === "POST" && url.pathname === "/relay/switch") {
state.relaySwitchSeen = true;
if (body.local_ip !== "10.123.0.31" || body.channel !== 0 || body.on !== true) {
state.failure = new Error(`unexpected relay switch payload: ${JSON.stringify(body)}`);
}
sendJson(response, 200, {
online: true,
on: true,
output: true,
raw: { source: "fake-worker" },
});
return;
}
sendJson(response, 404, { message: "not found" });
});
}
function createApiServer(state, brokerPort, workerPort) {
return http.createServer(async (request, response) => {
const url = new URL(request.url, "http://127.0.0.1");
const body = await readJson(request);
state.requests.push({ service: "api", method: request.method, path: url.pathname, body });
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/heartbeat") {
sendJson(response, 200, { data: { ok: true, broker_url: `ws://127.0.0.1:${brokerPort}` } });
return;
}
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/selfserve/machine-signal-bindings") {
sendJson(response, 200, { data: { monitors: [] } });
return;
}
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/commands/poll") {
state.commandPollSeen = true;
if (body.wait_seconds !== 0) {
state.failure = new Error(
`broker-connected command poll should be non-blocking, got wait_seconds=${body.wait_seconds}`
);
}
if (!state.commandDelivered) {
state.commandDelivered = true;
sendJson(response, 200, {
data: {
id: 77,
command_type: "SET_RELAY_STATE",
payload: {
localIp: "10.123.0.31",
channel: 0,
on: true,
relayId: "relay-proof",
},
},
});
return;
}
sendJson(response, 200, { data: null });
return;
}
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/commands/77/result") {
state.resultSeen = true;
if (body.ok !== true || body.result?.on !== true || body.result?.raw?.source !== "fake-worker") {
state.failure = new Error(`unexpected command result: ${JSON.stringify(body)}`);
}
sendJson(response, 200, { data: { acknowledged: true } });
return;
}
sendJson(response, 404, { message: "not found", path: url.pathname, workerPort });
});
}
function writeConfig(tempDir, apiPort, brokerPort, workerPort) {
const containerProofDir = "/proof";
const runtimeDir = `${containerProofDir}/runtime`;
const config = {
apiUrl: `http://127.0.0.1:${apiPort}`,
brokerUrl: `ws://127.0.0.1:${brokerPort}`,
gatewayId: 42,
agentToken: "agent-token",
installDir: containerProofDir,
runtimeDir,
stateDatabasePath: `${runtimeDir}/gateway-state.sqlite`,
workerBaseUrl: `http://127.0.0.1:${workerPort}`,
heartbeatIntervalSeconds: 60,
operationPollTimeoutSeconds: 20,
};
const configPath = path.join(tempDir, "config.json");
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
return { configPath, containerConfigPath: `${containerProofDir}/config.json` };
}
function spawnAgent({ agentPath, phpImage, tempDir, containerConfigPath }) {
return spawn("docker", [
"run",
"--rm",
"--network",
"host",
"-v",
`${agentPath}:/agent.php:ro`,
"-v",
`${tempDir}:/proof`,
phpImage,
"php",
"/agent.php",
"--config",
containerConfigPath,
], { stdio: ["ignore", "pipe", "pipe"] });
}
async function stopChild(child) {
if (child.exitCode !== null || child.signalCode !== null) {
return;
}
child.kill("SIGTERM");
const hardKill = setTimeout(() => {
if (child.exitCode === null && child.signalCode === null) {
child.kill("SIGKILL");
}
}, 1500);
await Promise.race([
new Promise((resolve) => child.once("exit", resolve)),
new Promise((resolve) => setTimeout(resolve, 2200)),
]);
clearTimeout(hardKill);
}
function evidenceFromState(state, childExited) {
return {
brokerHandshakeSeen: state.brokerHandshakeSeen,
commandPollSeen: state.commandPollSeen,
relaySwitchSeen: state.relaySwitchSeen,
resultSeen: state.resultSeen,
agentStayedRunningUntilProofComplete: !childExited,
};
}
export async function runProof(options) {
if (process.platform !== "linux") {
throw new Error("This proof uses Docker --network host and currently expects Linux.");
}
if (!fs.existsSync(options.agentPath)) {
throw new Error(`Agent artifact not found: ${options.agentPath}`);
}
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "edge-agent-command-drain-proof-"));
fs.mkdirSync(path.join(tempDir, "runtime"), { recursive: true });
const state = {
brokerHandshakeSeen: false,
commandPollSeen: false,
relaySwitchSeen: false,
resultSeen: false,
commandDelivered: false,
failure: null,
requests: [],
};
const broker = createBrokerServer(state);
const workerServer = createWorkerServer(state);
let apiServer = null;
let child = null;
let stdout = "";
let stderr = "";
let childExited = false;
try {
const brokerPort = await listen(broker.server);
const workerPort = await listen(workerServer);
apiServer = createApiServer(state, brokerPort, workerPort);
const apiPort = await listen(apiServer);
const { containerConfigPath } = writeConfig(tempDir, apiPort, brokerPort, workerPort);
child = spawnAgent({ ...options, tempDir, containerConfigPath });
child.stdout.on("data", (chunk) => {
stdout += chunk.toString();
});
child.stderr.on("data", (chunk) => {
stderr += chunk.toString();
});
child.once("exit", () => {
childExited = true;
});
const deadline = Date.now() + options.timeoutMs;
while (Date.now() < deadline && !state.failure && !childExited) {
if (state.brokerHandshakeSeen && state.commandPollSeen && state.relaySwitchSeen && state.resultSeen) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
const evidence = evidenceFromState(state, childExited);
if (
state.failure ||
!state.brokerHandshakeSeen ||
!state.commandPollSeen ||
!state.relaySwitchSeen ||
!state.resultSeen
) {
const error = state.failure || new Error("missing proof evidence");
error.evidence = evidence;
error.requests = state.requests;
error.stdout = stdout.slice(-3000);
error.stderr = stderr.slice(-3000);
throw error;
}
return {
evidence,
agentPath: options.agentPath,
phpImage: options.phpImage,
tempDir,
requestCount: state.requests.length,
};
} finally {
if (child) {
await stopChild(child);
}
for (const socket of broker.sockets) {
socket.destroy();
}
await Promise.allSettled([
closeServer(broker.server),
closeServer(workerServer),
apiServer ? closeServer(apiServer) : Promise.resolve(),
]);
if (!options.keepTemp) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
}
}
async function main() {
const options = parseArgs();
if (options.help) {
printUsage();
return;
}
const result = await runProof(options);
process.stdout.write("PASS broker-connected API command poll triggered local relay switch and posted result\n");
process.stdout.write(`${JSON.stringify(result.evidence)}\n`);
process.stdout.write(`Agent: ${result.agentPath}\n`);
process.stdout.write(`PHP image: ${result.phpImage}\n`);
if (options.keepTemp) {
process.stdout.write(`Temp dir: ${result.tempDir}\n`);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
process.stderr.write(`FAIL ${error.message}\n`);
if (error.evidence) {
process.stderr.write(`Evidence: ${JSON.stringify(error.evidence)}\n`);
}
if (error.requests) {
process.stderr.write(`Requests: ${JSON.stringify(error.requests, null, 2)}\n`);
}
if (error.stdout) {
process.stderr.write(`stdout: ${error.stdout}\n`);
}
if (error.stderr) {
process.stderr.write(`stderr: ${error.stderr}\n`);
}
process.exit(1);
});
}
-619
View File
@@ -1,619 +0,0 @@
import { spawn } from "node:child_process";
import crypto from "node:crypto";
import fs from "node:fs";
import http from "node:http";
import net from "node:net";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const DEFAULT_AGENT_PATH = path.join(
repoRoot,
"services/nginx/app/resources/edge-gateway-agent/agent.php"
);
const DEFAULT_WORKER_PATH = path.join(
repoRoot,
"services/nginx/app/resources/edge-gateway-agent/lan-worker.php"
);
const DEFAULT_PHP_IMAGE = "php:8.2-cli-bookworm";
const DEFAULT_TIMEOUT_MS = 15000;
const AGENT_TOKEN = "agent-token";
function parseArgs(argv = process.argv.slice(2)) {
const options = {
agentPath: DEFAULT_AGENT_PATH,
workerPath: DEFAULT_WORKER_PATH,
phpImage: DEFAULT_PHP_IMAGE,
timeoutMs: DEFAULT_TIMEOUT_MS,
keepTemp: false,
help: false,
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const next = argv[index + 1];
switch (arg) {
case "--agent-path":
options.agentPath = path.resolve(String(next || "").trim());
index += 1;
break;
case "--worker-path":
options.workerPath = path.resolve(String(next || "").trim());
index += 1;
break;
case "--php-image":
options.phpImage = String(next || "").trim() || DEFAULT_PHP_IMAGE;
index += 1;
break;
case "--timeout-ms":
options.timeoutMs = Number.parseInt(String(next || ""), 10) || DEFAULT_TIMEOUT_MS;
index += 1;
break;
case "--keep-temp":
options.keepTemp = true;
break;
case "--help":
case "-h":
options.help = true;
break;
default:
throw new Error(`Unknown argument: ${arg}`);
}
}
return options;
}
function printUsage() {
process.stdout.write(`Usage:
node scripts/edge-agent-to-shelly-proof.mjs [options]
Runs the PHP edge agent and real LAN worker against fake broker, API, and
Shelly RPC endpoints. Verifies that a broker-connected SET_RELAY_STATE command
drains from the API, reaches the worker, triggers a Shelly-style Switch.Set
call, reads Switch.GetStatus, and posts the command result.
Options:
--agent-path <path> PHP agent artifact to execute.
Default: ${DEFAULT_AGENT_PATH}
--worker-path <path> PHP LAN worker artifact to execute.
Default: ${DEFAULT_WORKER_PATH}
--php-image <image> Docker PHP image with curl, sqlite3, and pdo_sqlite.
Default: ${DEFAULT_PHP_IMAGE}
--timeout-ms <ms> Proof timeout. Default: ${DEFAULT_TIMEOUT_MS}
--keep-temp Keep the temporary config/runtime directory.
--help Show this help text.
`);
}
function readJson(request) {
return new Promise((resolve) => {
let raw = "";
request.setEncoding("utf8");
request.on("data", (chunk) => {
raw += chunk;
});
request.on("end", () => {
if (raw.trim() === "") {
resolve({});
return;
}
try {
resolve(JSON.parse(raw));
} catch {
resolve({ __invalid: raw });
}
});
});
}
function sendJson(response, status, payload) {
const body = JSON.stringify(payload);
response.writeHead(status, {
"content-type": "application/json; charset=utf-8",
"content-length": Buffer.byteLength(body),
});
response.end(body);
}
function requestJson({ method = "GET", port, path: requestPath, body = null, headers = {} }) {
return new Promise((resolve, reject) => {
const payload = body === null ? null : JSON.stringify(body);
const request = http.request({
hostname: "127.0.0.1",
port,
path: requestPath,
method,
headers: {
accept: "application/json",
...(payload === null ? {} : {
"content-type": "application/json",
"content-length": Buffer.byteLength(payload),
}),
...headers,
},
timeout: 1000,
}, (response) => {
let raw = "";
response.setEncoding("utf8");
response.on("data", (chunk) => {
raw += chunk;
});
response.on("end", () => {
let decoded = {};
try {
decoded = raw.trim() === "" ? {} : JSON.parse(raw);
} catch {
decoded = { __invalid: raw };
}
resolve({ status: response.statusCode || 0, body: decoded });
});
});
request.on("error", reject);
request.on("timeout", () => {
request.destroy(new Error("request timed out"));
});
if (payload !== null) {
request.write(payload);
}
request.end();
});
}
function listen(server) {
return new Promise((resolve) => {
server.listen(0, "127.0.0.1", () => resolve(server.address().port));
});
}
function closeServer(server) {
return new Promise((resolve) => {
server.close(() => resolve());
});
}
async function reservePort() {
const server = net.createServer();
const port = await new Promise((resolve) => {
server.listen(0, "127.0.0.1", () => resolve(server.address().port));
});
await closeServer(server);
return port;
}
function websocketAcceptKey(key) {
return crypto
.createHash("sha1")
.update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)
.digest("base64");
}
function createBrokerServer(state) {
const sockets = new Set();
const server = net.createServer((socket) => {
sockets.add(socket);
socket.on("close", () => sockets.delete(socket));
let buffer = "";
socket.on("data", (chunk) => {
buffer += chunk.toString("binary");
if (state.brokerHandshakeSeen || !buffer.includes("\r\n\r\n")) {
return;
}
const requestText = Buffer.from(buffer, "binary").toString("utf8");
const key = requestText.match(/Sec-WebSocket-Key:\s*(.+)\r\n/i)?.[1]?.trim();
const requestLine = requestText.split("\r\n")[0] || "";
if (!requestLine.includes("/ws/agent?")) {
state.failure = new Error(`unexpected broker path: ${requestLine}`);
}
if (!key) {
state.failure = new Error("broker handshake missing Sec-WebSocket-Key");
return;
}
socket.write([
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
`Sec-WebSocket-Accept: ${websocketAcceptKey(key)}`,
"",
"",
].join("\r\n"));
state.brokerHandshakeSeen = true;
buffer = "";
});
});
return { server, sockets };
}
function createShellyServer(state) {
return http.createServer((request, response) => {
const url = new URL(request.url, "http://127.0.0.1");
state.requests.push({
service: "shelly",
method: request.method,
path: url.pathname,
query: Object.fromEntries(url.searchParams.entries()),
});
if (request.method === "GET" && url.pathname === "/rpc/Switch.Set") {
state.shellySwitchSetSeen = true;
if (url.searchParams.get("id") !== "0" || url.searchParams.get("on") !== "true") {
state.failure = new Error(`unexpected Shelly Switch.Set query: ${url.search}`);
}
sendJson(response, 200, { was_on: false, output: true });
return;
}
if (request.method === "GET" && url.pathname === "/rpc/Switch.GetStatus") {
state.shellyStatusSeen = true;
if (url.searchParams.get("id") !== "0") {
state.failure = new Error(`unexpected Shelly Switch.GetStatus query: ${url.search}`);
}
sendJson(response, 200, { id: 0, output: true, source: "fake-shelly-rpc" });
return;
}
if (url.pathname.startsWith("/relay/")) {
state.failure = new Error(`legacy Shelly endpoint should not be used for generation 2 proof: ${url.pathname}`);
}
sendJson(response, 404, { message: "not found" });
});
}
function createApiServer(state, brokerPort, shellyAddress) {
return http.createServer(async (request, response) => {
const url = new URL(request.url, "http://127.0.0.1");
const body = await readJson(request);
state.requests.push({ service: "api", method: request.method, path: url.pathname, body });
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/heartbeat") {
sendJson(response, 200, { data: { ok: true, broker_url: `ws://127.0.0.1:${brokerPort}` } });
return;
}
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/selfserve/machine-signal-bindings") {
sendJson(response, 200, { data: { monitors: [] } });
return;
}
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/commands/poll") {
state.commandPollSeen = true;
if (body.wait_seconds !== 0) {
state.failure = new Error(
`broker-connected command poll should be non-blocking, got wait_seconds=${body.wait_seconds}`
);
}
if (!state.commandDelivered) {
state.commandDelivered = true;
sendJson(response, 200, {
data: {
id: 77,
command_type: "SET_RELAY_STATE",
payload: {
localIp: shellyAddress,
local_ip: shellyAddress,
channel: 0,
on: true,
relayId: "relay-proof",
relay_id: "relay-proof",
deviceGeneration: 2,
device_generation: 2,
},
},
});
return;
}
sendJson(response, 200, { data: null });
return;
}
if (request.method === "POST" && url.pathname === "/edge-agent/gateways/42/commands/77/result") {
state.resultSeen = true;
if (
body.ok !== true ||
body.result?.on !== true ||
body.result?.output !== true ||
body.result?.raw?.source !== "fake-shelly-rpc"
) {
state.failure = new Error(`unexpected command result: ${JSON.stringify(body)}`);
}
sendJson(response, 200, { data: { acknowledged: true } });
return;
}
sendJson(response, 404, { message: "not found", path: url.pathname });
});
}
function writeConfig(tempDir, apiPort, brokerPort, workerPort) {
const containerProofDir = "/proof";
const runtimeDir = `${containerProofDir}/runtime`;
const config = {
apiUrl: `http://127.0.0.1:${apiPort}`,
brokerUrl: `ws://127.0.0.1:${brokerPort}`,
gatewayId: 42,
agentToken: AGENT_TOKEN,
installDir: containerProofDir,
runtimeDir,
stateDatabasePath: `${runtimeDir}/gateway-state.sqlite`,
workerBaseUrl: `http://127.0.0.1:${workerPort}`,
heartbeatIntervalSeconds: 60,
operationPollTimeoutSeconds: 20,
};
const configPath = path.join(tempDir, "config.json");
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
return { containerConfigPath: `${containerProofDir}/config.json` };
}
function spawnWorker({ workerPath, phpImage, workerPort }) {
return spawn("docker", [
"run",
"--rm",
"--network",
"host",
"-e",
`TRUCKWASH_WORKER_TOKEN=${AGENT_TOKEN}`,
"-v",
`${workerPath}:/lan-worker.php:ro`,
phpImage,
"php",
"-S",
`127.0.0.1:${workerPort}`,
"/lan-worker.php",
], { stdio: ["ignore", "pipe", "pipe"] });
}
function spawnAgent({ agentPath, phpImage, tempDir, containerConfigPath }) {
return spawn("docker", [
"run",
"--rm",
"--network",
"host",
"-v",
`${agentPath}:/agent.php:ro`,
"-v",
`${tempDir}:/proof`,
phpImage,
"php",
"/agent.php",
"--config",
containerConfigPath,
], { stdio: ["ignore", "pipe", "pipe"] });
}
async function waitForWorker(workerPort, child, timeoutMs) {
const deadline = Date.now() + timeoutMs;
let lastError = null;
while (Date.now() < deadline) {
if (child.exitCode !== null || child.signalCode !== null) {
throw new Error(`LAN worker exited before becoming healthy: ${child.exitCode ?? child.signalCode}`);
}
try {
const response = await requestJson({ port: workerPort, path: "/health" });
if (response.status === 200 && response.body?.service === "lan-worker") {
return;
}
} catch (error) {
lastError = error;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
throw lastError || new Error("LAN worker did not become healthy");
}
async function stopChild(child) {
if (child.exitCode !== null || child.signalCode !== null) {
return;
}
child.kill("SIGTERM");
const hardKill = setTimeout(() => {
if (child.exitCode === null && child.signalCode === null) {
child.kill("SIGKILL");
}
}, 1500);
await Promise.race([
new Promise((resolve) => child.once("exit", resolve)),
new Promise((resolve) => setTimeout(resolve, 2200)),
]);
clearTimeout(hardKill);
}
function evidenceFromState(state, agentExited, workerExited) {
return {
brokerHandshakeSeen: state.brokerHandshakeSeen,
commandPollSeen: state.commandPollSeen,
shellySwitchSetSeen: state.shellySwitchSetSeen,
shellyStatusSeen: state.shellyStatusSeen,
resultSeen: state.resultSeen,
agentStayedRunningUntilProofComplete: !agentExited,
workerStayedRunningUntilProofComplete: !workerExited,
};
}
export async function runProof(options) {
if (process.platform !== "linux") {
throw new Error("This proof uses Docker --network host and currently expects Linux.");
}
if (!fs.existsSync(options.agentPath)) {
throw new Error(`Agent artifact not found: ${options.agentPath}`);
}
if (!fs.existsSync(options.workerPath)) {
throw new Error(`LAN worker artifact not found: ${options.workerPath}`);
}
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "edge-agent-to-shelly-proof-"));
fs.mkdirSync(path.join(tempDir, "runtime"), { recursive: true });
const state = {
brokerHandshakeSeen: false,
commandPollSeen: false,
shellySwitchSetSeen: false,
shellyStatusSeen: false,
resultSeen: false,
commandDelivered: false,
failure: null,
requests: [],
};
const broker = createBrokerServer(state);
const shellyServer = createShellyServer(state);
let apiServer = null;
let agent = null;
let worker = null;
let agentStdout = "";
let agentStderr = "";
let workerStdout = "";
let workerStderr = "";
let agentExited = false;
let workerExited = false;
try {
const brokerPort = await listen(broker.server);
const shellyPort = await listen(shellyServer);
const workerPort = await reservePort();
const shellyAddress = `127.0.0.1:${shellyPort}`;
apiServer = createApiServer(state, brokerPort, shellyAddress);
const apiPort = await listen(apiServer);
const { containerConfigPath } = writeConfig(tempDir, apiPort, brokerPort, workerPort);
worker = spawnWorker({ ...options, workerPort });
worker.stdout.on("data", (chunk) => {
workerStdout += chunk.toString();
});
worker.stderr.on("data", (chunk) => {
workerStderr += chunk.toString();
});
worker.once("exit", () => {
workerExited = true;
});
await waitForWorker(workerPort, worker, 5000);
agent = spawnAgent({ ...options, tempDir, containerConfigPath });
agent.stdout.on("data", (chunk) => {
agentStdout += chunk.toString();
});
agent.stderr.on("data", (chunk) => {
agentStderr += chunk.toString();
});
agent.once("exit", () => {
agentExited = true;
});
const deadline = Date.now() + options.timeoutMs;
while (Date.now() < deadline && !state.failure && !agentExited && !workerExited) {
if (
state.brokerHandshakeSeen &&
state.commandPollSeen &&
state.shellySwitchSetSeen &&
state.shellyStatusSeen &&
state.resultSeen
) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
const evidence = evidenceFromState(state, agentExited, workerExited);
if (
state.failure ||
!state.brokerHandshakeSeen ||
!state.commandPollSeen ||
!state.shellySwitchSetSeen ||
!state.shellyStatusSeen ||
!state.resultSeen
) {
const error = state.failure || new Error("missing proof evidence");
error.evidence = evidence;
error.requests = state.requests;
error.agentStdout = agentStdout.slice(-3000);
error.agentStderr = agentStderr.slice(-3000);
error.workerStdout = workerStdout.slice(-3000);
error.workerStderr = workerStderr.slice(-3000);
throw error;
}
return {
evidence,
agentPath: options.agentPath,
workerPath: options.workerPath,
phpImage: options.phpImage,
tempDir,
requestCount: state.requests.length,
};
} finally {
if (agent) {
await stopChild(agent);
}
if (worker) {
await stopChild(worker);
}
for (const socket of broker.sockets) {
socket.destroy();
}
await Promise.allSettled([
closeServer(broker.server),
closeServer(shellyServer),
apiServer ? closeServer(apiServer) : Promise.resolve(),
]);
if (!options.keepTemp) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
}
}
async function main() {
const options = parseArgs();
if (options.help) {
printUsage();
return;
}
const result = await runProof(options);
process.stdout.write("PASS broker-connected API command triggered real LAN worker Shelly RPC signal and posted result\n");
process.stdout.write(`${JSON.stringify(result.evidence)}\n`);
process.stdout.write(`Agent: ${result.agentPath}\n`);
process.stdout.write(`LAN worker: ${result.workerPath}\n`);
process.stdout.write(`PHP image: ${result.phpImage}\n`);
if (options.keepTemp) {
process.stdout.write(`Temp dir: ${result.tempDir}\n`);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
process.stderr.write(`FAIL ${error.message}\n`);
if (error.evidence) {
process.stderr.write(`Evidence: ${JSON.stringify(error.evidence)}\n`);
}
if (error.requests) {
process.stderr.write(`Requests: ${JSON.stringify(error.requests, null, 2)}\n`);
}
if (error.agentStdout) {
process.stderr.write(`agent stdout: ${error.agentStdout}\n`);
}
if (error.agentStderr) {
process.stderr.write(`agent stderr: ${error.agentStderr}\n`);
}
if (error.workerStdout) {
process.stderr.write(`worker stdout: ${error.workerStdout}\n`);
}
if (error.workerStderr) {
process.stderr.write(`worker stderr: ${error.workerStderr}\n`);
}
process.exit(1);
});
}
-983
View File
@@ -1,983 +0,0 @@
import assert from "node:assert/strict";
import { execFile as execFileCallback, spawn as spawnCallback } from "node:child_process";
import { randomUUID } from "node:crypto";
import { promises as fs } from "node:fs";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { DEFAULT_CONFIG_FILE_NAME, DEFAULT_HOST_API_URL } from "./test-gateway.mjs";
const execFile = promisify(execFileCallback);
const COMPOSE_SERVICES = ["traefik", "redis", "mysql-debug", "edge-broker", "php1", "php2", "php3", "php4", "php5", "caddy"];
function composeArgs(projectName, args) {
return ["compose", "-p", projectName, ...args];
}
async function resolveRootDir(scriptPath) {
const cwd = process.cwd();
try {
await fs.access(path.join(cwd, "docker-compose.yml"));
return cwd;
} catch {
return path.resolve(path.dirname(scriptPath), "..");
}
}
async function runCommand(command, args, { cwd, allowFailure = false, stdio = "pipe" } = {}) {
if (stdio === "inherit") {
await new Promise((resolve, reject) => {
const child = spawnCallback(command, args, {
cwd,
stdio: "inherit",
windowsHide: true,
});
child.on("exit", (code) => {
if (code === 0 || allowFailure) {
resolve();
return;
}
reject(new Error(`${command} ${args.join(" ")} failed with exit code ${code}`));
});
child.on("error", reject);
});
return { stdout: "", stderr: "", code: 0 };
}
try {
const result = await execFile(command, args, {
cwd,
windowsHide: true,
encoding: "utf8",
});
return { stdout: result.stdout, stderr: result.stderr, code: 0 };
} catch (error) {
if (!allowFailure) {
throw error;
}
return {
stdout: error.stdout || "",
stderr: error.stderr || "",
code: typeof error.code === "number" ? error.code : 1,
};
}
}
function normalizeBaseUrl(url) {
return String(url || "").replace(/\/+$/, "");
}
function baseUrlWithHost(baseUrl, host, port = null) {
const url = new URL(normalizeBaseUrl(baseUrl));
url.hostname = host;
if (port !== null) {
url.port = port;
}
return normalizeBaseUrl(url.toString());
}
function directCaddyBaseUrl(baseUrl) {
const url = new URL(normalizeBaseUrl(baseUrl));
url.hostname = "caddy";
url.port = "";
if (url.pathname === "/api" || url.pathname === "/api/") {
url.pathname = "/";
}
return normalizeBaseUrl(url.toString());
}
function isLocalHost(hostname) {
const normalized = String(hostname || "").toLowerCase().replace(/^\[|\]$/g, "");
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
}
function resolveBrokerWebSocketUrl(rawUrl, apiBaseUrl) {
const websocketUrl = new URL(String(rawUrl));
const apiUrl = new URL(normalizeBaseUrl(apiBaseUrl));
const ciBrokerPort = String(process.env.EDGE_BROKER_CI_PORT || "").trim();
if (isLocalHost(apiUrl.hostname) && websocketUrl.hostname === "edge-broker" && ciBrokerPort !== "") {
websocketUrl.protocol = apiUrl.protocol === "https:" ? "wss:" : "ws:";
websocketUrl.hostname = apiUrl.hostname;
websocketUrl.port = ciBrokerPort;
websocketUrl.pathname = websocketUrl.pathname.replace(/^\/edge-broker(?=\/|$)/, "") || "/";
return websocketUrl.toString();
}
if (apiUrl.hostname === "caddy" && websocketUrl.hostname === "caddy") {
websocketUrl.hostname = "edge-broker";
websocketUrl.port = "4300";
websocketUrl.pathname = websocketUrl.pathname.replace(/^\/edge-broker(?=\/|$)/, "") || "/";
}
if (isLocalHost(apiUrl.hostname) && ["caddy", "edge-broker"].includes(websocketUrl.hostname)) {
const brokerPath = websocketUrl.pathname.replace(/^\/edge-broker(?=\/|$)/, "") || "/";
websocketUrl.protocol = apiUrl.protocol === "https:" ? "wss:" : "ws:";
websocketUrl.hostname = apiUrl.hostname;
websocketUrl.port = apiUrl.port;
websocketUrl.pathname = `/api/edge-broker${brokerPath}`;
}
if (isLocalHost(websocketUrl.hostname)) {
websocketUrl.pathname = websocketUrl.pathname.replace(/^\/edge-broker(?=\/|$)/, "") || "/";
}
return websocketUrl.toString();
}
async function readDefaultGatewayHost() {
if (process.platform === "win32") {
return null;
}
try {
const routeTable = await fs.readFile("/proc/net/route", "utf8");
const route = routeTable
.split(/\r?\n/)
.map((line) => line.trim().split(/\s+/))
.find((fields) => fields[1] === "00000000" && /^[0-9A-Fa-f]{8}$/.test(fields[2] || ""));
if (!route) {
return null;
}
const gateway = route[2];
const octets = [
gateway.slice(6, 8),
gateway.slice(4, 6),
gateway.slice(2, 4),
gateway.slice(0, 2),
].map((octet) => Number.parseInt(octet, 16));
if (octets.some((octet) => !Number.isInteger(octet)) || octets.every((octet) => octet === 0)) {
return null;
}
return octets.join(".");
} catch {
return null;
}
}
function composeNetworkName(composeProject) {
return `${composeProject}_default`;
}
async function readCurrentContainerRef() {
if (process.platform === "win32") {
return null;
}
const candidates = [];
const envHostname = String(process.env.HOSTNAME || "").trim();
if (envHostname !== "") {
candidates.push(envHostname);
}
try {
const hostname = (await fs.readFile("/etc/hostname", "utf8")).trim();
if (hostname !== "") {
candidates.push(hostname);
}
} catch {
// Not running in a container with /etc/hostname available.
}
try {
const cgroup = await fs.readFile("/proc/self/cgroup", "utf8");
const matches = cgroup.match(/[0-9a-f]{64}/gi) || [];
candidates.push(...matches);
} catch {
// cgroup metadata is optional in local development.
}
return candidates.find((candidate) => /^[a-zA-Z0-9][a-zA-Z0-9_.-]{1,127}$/.test(candidate)) || null;
}
async function connectCurrentContainerToComposeNetwork(rootDir, composeProject) {
const containerRef = await readCurrentContainerRef();
if (!containerRef) {
return false;
}
const inspection = await runCommand("docker", ["inspect", containerRef], {
cwd: rootDir,
allowFailure: true,
});
if (inspection.code !== 0) {
return false;
}
const networkName = composeNetworkName(composeProject);
const connection = await runCommand("docker", ["network", "connect", networkName, containerRef], {
cwd: rootDir,
allowFailure: true,
});
const stderr = String(connection.stderr || "");
if (connection.code === 0) {
process.stdout.write(`Attached runner container ${containerRef} to ${networkName}.\n`);
return true;
}
if (/already exists|already connected/i.test(stderr)) {
return true;
}
return false;
}
async function disconnectCurrentContainerFromComposeNetwork(rootDir, composeProject) {
const containerRef = await readCurrentContainerRef();
if (!containerRef) {
return;
}
await runCommand("docker", ["network", "disconnect", composeNetworkName(composeProject), containerRef], {
cwd: rootDir,
allowFailure: true,
});
}
async function readComposeServiceHost(rootDir, composeProject, serviceName) {
const ps = await runCommand("docker", composeArgs(composeProject, ["ps", "-q", serviceName]), {
cwd: rootDir,
allowFailure: true,
});
const containerId = String(ps.stdout || "").trim().split(/\s+/).find(Boolean);
const containerRefs = [
...(containerId ? [containerId] : []),
serviceName,
];
for (const containerRef of [...new Set(containerRefs)]) {
const inspection = await runCommand("docker", [
"inspect",
"-f",
"{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
containerRef,
], {
cwd: rootDir,
allowFailure: true,
});
const host = String(inspection.stdout || "").trim().split(/\s+/).find((value) => /^\d{1,3}(?:\.\d{1,3}){3}$/.test(value));
if (host) {
return host;
}
}
return null;
}
async function candidateApiBaseUrls(baseUrl, rootDir, composeProject, useComposeNetwork = false) {
const normalized = normalizeBaseUrl(baseUrl);
const candidates = [normalized];
const url = new URL(normalized);
if (["localhost", "127.0.0.1", "::1"].includes(url.hostname)) {
if (rootDir && composeProject) {
if (useComposeNetwork) {
candidates.push(directCaddyBaseUrl(normalized));
candidates.push(baseUrlWithHost(normalized, "traefik", ""));
}
const traefikHost = await readComposeServiceHost(rootDir, composeProject, "traefik");
if (traefikHost) {
candidates.push(baseUrlWithHost(normalized, traefikHost, ""));
}
}
const gatewayHost = await readDefaultGatewayHost();
if (gatewayHost) {
candidates.push(baseUrlWithHost(normalized, gatewayHost));
}
candidates.push(baseUrlWithHost(normalized, "host.docker.internal"));
}
return [...new Set(candidates)];
}
async function waitForCondition(predicate, { timeoutMs = 30_000, intervalMs = 500, message = "Timed out" } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (await predicate()) {
return;
}
await new Promise((resolve) => setTimeout(resolve, intervalMs));
}
throw new Error(message);
}
async function ensureComposeServices(rootDir, composeProject) {
await runCommand("docker", composeArgs(composeProject, ["up", "-d", ...COMPOSE_SERVICES]), {
cwd: rootDir,
stdio: "inherit",
});
}
async function waitForApiReady(baseUrl, rootDir, composeProject, useComposeNetwork = false, attempts = 60) {
const candidates = await candidateApiBaseUrls(baseUrl, rootDir, composeProject, useComposeNetwork);
let lastError = "API never responded";
for (let attempt = 0; attempt < attempts; attempt += 1) {
for (const root of candidates) {
try {
const response = await fetch(`${root}/ping`, {
signal: AbortSignal.timeout(1000),
});
if (response.ok) {
return root;
}
lastError = `${root}/ping returned HTTP ${response.status}`;
} catch (error) {
lastError = `${root}/ping failed: ${error instanceof Error ? error.message : String(error)}`;
}
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
throw new Error(`API did not become ready at ${candidates.map((candidate) => `${candidate}/ping`).join(", ")}: ${lastError}`);
}
function parseLastJsonLine(output) {
const lines = String(output || "")
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);
for (let index = lines.length - 1; index >= 0; index -= 1) {
try {
return JSON.parse(lines[index]);
} catch {
// Continue scanning backwards for the JSON payload.
}
}
throw new Error(`Unable to parse JSON from command output:\n${output}`);
}
async function runPhpFixture(rootDir, composeProject, action, payload = null) {
const encodedPayload = payload === null
? ""
: ` ${Buffer.from(JSON.stringify(payload), "utf8").toString("base64url")}`;
const command = `cd /var/www/html && CONFIG_DB_TARGET=debug php tests/Support/EdgeGatewayE2eFixture.php ${action}${encodedPayload}`;
const result = await runCommand("docker", composeArgs(composeProject, [
"exec",
"-T",
"php1",
"sh",
"-lc",
command,
]), {
cwd: rootDir,
});
return parseLastJsonLine(result.stdout);
}
async function apiRequest(baseUrl, method, endpoint, { token = null, body = null, headers = {} } = {}) {
const response = await fetch(`${normalizeBaseUrl(baseUrl)}${endpoint}`, {
method,
headers: {
...(body === null ? {} : { "content-type": "application/json" }),
...(token ? { Authorization: `Bearer ${token}` } : {}),
...headers,
},
body: body === null ? undefined : JSON.stringify(body),
});
const rawBody = await response.text();
let json = null;
if (rawBody !== "") {
try {
json = JSON.parse(rawBody);
} catch {
json = null;
}
}
if (!response.ok) {
const message =
json?.data?.message ||
json?.error ||
rawBody ||
`HTTP ${response.status}`;
throw new Error(`${method} ${endpoint} failed: ${message}`);
}
return json;
}
async function loadWebSocketImplementation() {
if (typeof WebSocket !== "undefined") {
return WebSocket;
}
const module = await import("ws");
return module.default;
}
function onSocket(socket, eventName, handler) {
if (typeof socket.addEventListener === "function") {
socket.addEventListener(eventName, (event) => {
if (eventName === "message") {
handler(event.data);
return;
}
handler(event);
});
return;
}
socket.on(eventName, handler);
}
function collectSocketMessages(socket) {
const messages = [];
onSocket(socket, "message", (payload) => {
const text = typeof payload === "string"
? payload
: Buffer.isBuffer(payload)
? payload.toString("utf8")
: typeof payload?.toString === "function"
? payload.toString()
: "";
if (text === "") {
return;
}
try {
messages.push(JSON.parse(text));
} catch {
// Ignore non-JSON frames.
}
});
return messages;
}
async function waitForSocketOpen(socket) {
if (socket.readyState === 1) {
return;
}
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error("Timed out waiting for websocket open.")), 10_000);
const socketUrl = typeof socket.url === "string" && socket.url !== "" ? ` (${socket.url})` : "";
const onOpen = () => {
clearTimeout(timeout);
resolve();
};
const onError = (error) => {
clearTimeout(timeout);
if (error instanceof Error) {
reject(error);
return;
}
const readyState = typeof socket.readyState === "number" ? socket.readyState : "unknown";
reject(new Error(`Websocket failed to open${socketUrl}; readyState=${readyState}.`));
};
const onClose = (event) => {
clearTimeout(timeout);
const code = event && typeof event === "object" && "code" in event ? event.code : "unknown";
const reason = event && typeof event === "object" && "reason" in event ? event.reason : "";
reject(new Error(`Websocket closed before open${socketUrl}; code=${code} reason=${reason || "none"}.`));
};
onSocket(socket, "open", onOpen);
onSocket(socket, "error", onError);
onSocket(socket, "close", onClose);
});
}
async function waitForSocketMessage(messages, predicate, options) {
await waitForCondition(() => messages.some(predicate), options);
}
function buildSocketUrl(wsUrl, token) {
const url = new URL(String(wsUrl));
url.searchParams.set("token", token);
return url.toString();
}
function closeSocket(socket) {
if (!socket || typeof socket.close !== "function") {
return;
}
const readyState = typeof socket.readyState === "number" ? socket.readyState : null;
if (readyState !== null && readyState >= 2) {
return;
}
socket.close();
}
function shouldCopyGatewayConfig() {
return /^(1|true|yes)$/i.test(String(process.env.EDGE_GATEWAY_E2E_COPY_CONFIG || "").trim());
}
function shouldSkipComposeUp() {
return /^(1|true|yes)$/i.test(String(process.env.EDGE_GATEWAY_E2E_SKIP_COMPOSE_UP || "").trim());
}
function collectMessages(rows) {
return Array.isArray(rows)
? rows
.map((row) => (row && typeof row === "object" ? row.message : null))
.filter((value) => typeof value === "string")
: [];
}
function summarizeStreamMessages(messages, limit = 12) {
return messages
.slice(-limit)
.map((message) => {
if (!message || typeof message !== "object") {
return null;
}
const summary = {
type: message.type || "unknown",
};
if (message.operationId !== undefined) {
summary.operationId = Number(message.operationId || 0);
}
if (message.operation && typeof message.operation === "object") {
summary.operationStatus = message.operation.status || null;
}
if (message.gateway && typeof message.gateway === "object") {
summary.gatewayStatus = message.gateway.status || null;
}
return summary;
})
.filter(Boolean);
}
async function readGatewayDiagnostics({ baseUrl, authToken, gatewayId, containerName }) {
const diagnostics = {};
if (gatewayId !== null && gatewayId > 0 && authToken) {
try {
const detail = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}`, {
token: authToken,
});
diagnostics.gateway = {
status: detail?.data?.status ?? null,
channelStatus: detail?.data?.channel_status ?? null,
brokerPresence: detail?.data?.metadata?.broker_presence ?? null,
brokerConnected: detail?.data?.metadata?.broker_connected ?? null,
brokerLastError: detail?.data?.metadata?.broker_last_error ?? null,
};
} catch (error) {
diagnostics.gatewayError = error instanceof Error ? error.message : String(error);
}
}
try {
const logs = await runCommand("docker", ["logs", "--tail", "120", containerName], {
allowFailure: true,
});
diagnostics.containerLogs = String(`${logs.stdout || ""}${logs.stderr || ""}`).trim().split(/\r?\n/).slice(-120);
} catch (error) {
diagnostics.containerLogError = error instanceof Error ? error.message : String(error);
}
return diagnostics;
}
async function main() {
const scriptPath = fileURLToPath(import.meta.url);
const rootDir = await resolveRootDir(scriptPath);
const runId = randomUUID().slice(0, 8);
const containerName = `truckwash-edge-e2e-${runId}`;
const configDir = path.join(rootDir, ".tmp", "edge-gateway-e2e", runId);
const configFilePath = path.join(configDir, DEFAULT_CONFIG_FILE_NAME);
let baseUrl = process.env.EDGE_GATEWAY_E2E_BASE_URL || DEFAULT_HOST_API_URL;
const composeProject =
process.env.EDGE_GATEWAY_E2E_COMPOSE_PROJECT
|| path.basename(rootDir);
let fixture = null;
let gatewayId = null;
let streamSocket = null;
let shellSocket = null;
let runnerNetworkAttached = false;
try {
if (!shouldSkipComposeUp()) {
await ensureComposeServices(rootDir, composeProject);
}
runnerNetworkAttached = await connectCurrentContainerToComposeNetwork(rootDir, composeProject);
baseUrl = await waitForApiReady(baseUrl, rootDir, composeProject, runnerNetworkAttached);
process.stdout.write(`Using API base URL ${baseUrl}\n`);
fixture = await runPhpFixture(rootDir, composeProject, "create");
const authToken = String(fixture.auth_token || "");
const departmentId = Number(fixture.department_id || 0);
assert.ok(authToken !== "", "Fixture helper did not return an auth token.");
assert.ok(departmentId > 0, "Fixture helper did not return a department id.");
const installTokenResponse = await apiRequest(baseUrl, "POST", "/edge-gateways/install-token", {
token: authToken,
body: {
department_id: departmentId,
label: `Edge Gateway E2E ${runId}`,
},
});
const installToken = String(installTokenResponse?.data?.token || "");
assert.ok(installToken !== "", "Install token creation did not return a token.");
await runCommand(process.execPath, [
"scripts/test-gateway.mjs",
"start",
"--install-token",
installToken,
"--host-api-url",
baseUrl,
"--container-name",
containerName,
"--config-dir",
configDir,
"--heartbeat-seconds",
"3",
"--skip-compose-up",
...(shouldCopyGatewayConfig() ? ["--copy-config"] : []),
], {
cwd: rootDir,
stdio: "inherit",
});
await waitForCondition(
async () => {
try {
await fs.access(configFilePath);
return true;
} catch {
return false;
}
},
{ message: `Gateway config file was not created at ${configFilePath}` }
);
const config = JSON.parse(await fs.readFile(configFilePath, "utf8"));
gatewayId = Number(config.gatewayId || 0);
assert.ok(gatewayId > 0, "Gateway config did not include a gateway id.");
await waitForCondition(
async () => {
const result = await runCommand("docker", [
"exec",
containerName,
"test",
"-f",
"/opt/truckwash-edge-agent/runtime/last-heartbeat-ok.txt",
], {
allowFailure: true,
});
return result.code === 0;
},
{
timeoutMs: 60_000,
message: "Gateway never wrote the successful heartbeat marker.",
}
);
await waitForCondition(
async () => {
const detail = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}`, {
token: authToken,
});
return detail?.data?.status === "ONLINE"
&& Object.keys(detail?.data?.metadata?.system_metrics || {}).length > 0;
},
{
timeoutMs: 60_000,
message: "Gateway detail never transitioned to ONLINE with fresh system metrics after install.",
}
);
try {
await waitForCondition(
async () => {
const detail = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}`, {
token: authToken,
});
return Boolean(
detail?.data?.channel_status?.broker?.connected
|| detail?.data?.metadata?.broker_connected
);
},
{
timeoutMs: 90_000,
message: "Gateway never established a live broker connection after install.",
}
);
} catch (error) {
const diagnostics = await readGatewayDiagnostics({
baseUrl,
authToken,
gatewayId,
containerName,
});
throw new Error([
error instanceof Error ? error.message : String(error),
`Broker diagnostics: ${JSON.stringify(diagnostics, null, 2)}`,
].join("\n"));
}
const WebSocketImpl = await loadWebSocketImplementation();
const streamSession = await apiRequest(baseUrl, "POST", `/edge-gateways/${gatewayId}/stream-session`, {
token: authToken,
body: {
scopes: ["overview", "tasks", "logs", "statistics"],
},
});
const streamWsUrl = buildSocketUrl(
resolveBrokerWebSocketUrl(String(streamSession?.data?.ws_url || ""), baseUrl),
String(streamSession?.data?.token || "")
);
streamSocket = new WebSocketImpl(streamWsUrl);
const streamMessages = collectSocketMessages(streamSocket);
await waitForSocketOpen(streamSocket);
await waitForSocketMessage(
streamMessages,
(message) => message?.type === "gateway.stream.ready",
{ timeoutMs: 15_000, message: "Gateway stream never became ready." }
);
await waitForSocketMessage(
streamMessages,
(message) => (
message?.type === "gateway.stream.ready" && message?.connected === true
) || (
message?.type === "presence.changed" && message?.status === "connected"
),
{ timeoutMs: 45_000, message: "Gateway stream never observed a connected broker presence." }
);
const operationResponse = await apiRequest(baseUrl, "POST", `/edge-gateways/${gatewayId}/operations`, {
token: authToken,
body: {
type: "DISCOVERY",
request: {
inventory: [{
device_id: `edge-e2e-${runId}`,
local_ip: "10.70.80.90",
model: "TruckWash Edge E2E",
channel_count: 1,
online: true,
capabilities: {
gateway_management_v2: true,
},
metadata: {
hostname: `edge-e2e-${runId}`,
},
}],
},
},
});
const operationId = Number(operationResponse?.data?.operation?.id || 0);
assert.ok(operationId > 0, "Operation creation did not return an operation id.");
try {
await waitForCondition(
async () => {
const operations = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}/operations`, {
token: authToken,
});
const operation = Array.isArray(operations?.data)
? operations.data.find((item) => Number(item?.id || 0) === operationId)
: null;
return operation?.status === "COMPLETED"
|| streamMessages.some((message) => (
message?.type === "task.updated"
&& Number(message?.operationId || 0) === operationId
&& message?.operation?.status === "COMPLETED"
));
},
{ timeoutMs: 180_000, message: "Gateway operation never completed through the live agent." }
);
} catch (error) {
let operationSnapshot = null;
try {
const operations = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}/operations`, {
token: authToken,
});
operationSnapshot = Array.isArray(operations?.data)
? operations.data.find((item) => Number(item?.id || 0) === operationId) || null
: null;
} catch {
operationSnapshot = null;
}
const diagnostic = [
error instanceof Error ? error.message : String(error),
`Recent stream messages: ${JSON.stringify(summarizeStreamMessages(streamMessages))}`,
`Operation snapshot: ${JSON.stringify(operationSnapshot)}`,
].join("\n");
throw new Error(diagnostic);
}
await waitForSocketMessage(
streamMessages,
(message) => message?.type === "gateway.telemetry" || message?.type === "stats.updated",
{ timeoutMs: 15_000, message: "Live gateway stream never emitted telemetry or statistics updates." }
);
await waitForCondition(
async () => {
const logs = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}/logs`, {
token: authToken,
});
const timeline = Array.isArray(logs?.data?.entries)
? logs.data.entries
: (Array.isArray(logs?.data?.timeline) ? logs.data.timeline : []);
return timeline.some((entry) => {
const nestedEntry = entry?.entry && typeof entry.entry === "object" ? entry.entry : null;
const directOperationId = Number(nestedEntry?.operation_id || 0);
const contextualOperationId = Number(nestedEntry?.context?.operation_id || 0);
return directOperationId === operationId || contextualOperationId === operationId;
});
},
{ timeoutMs: 30_000, message: "Gateway logs page never reflected the live operation timeline." }
);
const statistics = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}/statistics`, {
token: authToken,
});
assert.ok(
Object.keys(statistics?.data?.system_metrics || {}).length > 0,
"Gateway statistics page did not expose system metrics after live telemetry."
);
const shellSession = await apiRequest(baseUrl, "POST", `/edge-gateways/${gatewayId}/shell-sessions`, {
token: authToken,
body: {
reason: "Edge gateway E2E shell validation",
cwd: "/opt/truckwash-edge-agent",
cols: 120,
rows: 40,
},
});
const shellWsUrl = buildSocketUrl(
resolveBrokerWebSocketUrl(String(shellSession?.data?.ws_url || ""), baseUrl),
String(shellSession?.data?.token || "")
);
shellSocket = new WebSocketImpl(shellWsUrl);
const shellMessages = collectSocketMessages(shellSocket);
await waitForSocketOpen(shellSocket);
await waitForSocketMessage(
shellMessages,
(message) => message?.type === "opened",
{ timeoutMs: 20_000, message: "Browser shell never opened against the live gateway." }
);
shellSocket.send(JSON.stringify({
type: "input",
data: "printf 'edge-e2e-shell\\n'; exit\n",
}));
await waitForSocketMessage(
shellMessages,
(message) => message?.type === "output" && String(message?.data || "").includes("edge-e2e-shell"),
{ timeoutMs: 20_000, message: "Browser shell never returned the expected command output." }
);
await waitForSocketMessage(
shellMessages,
(message) => message?.type === "closed",
{ timeoutMs: 20_000, message: "Browser shell never closed cleanly." }
);
await waitForCondition(
async () => {
const logsAfterShell = await apiRequest(baseUrl, "GET", `/edge-gateways/${gatewayId}/logs`, {
token: authToken,
});
const shellTranscripts = Array.isArray(logsAfterShell?.data?.shell_sessions)
? logsAfterShell.data.shell_sessions.map((session) => String(session?.transcript || ""))
: [];
const timelineMessages = collectMessages(logsAfterShell?.data?.timeline || []);
return shellTranscripts.some((transcript) => transcript.includes("edge-e2e-shell"))
&& timelineMessages.includes("GATEWAY_SHELL_SESSION_CLOSED");
},
{ timeoutMs: 30_000, message: "Gateway logs page did not persist the shell transcript and close audit event." }
);
process.stdout.write("Edge gateway E2E smoke completed successfully.\n");
} finally {
closeSocket(shellSocket);
closeSocket(streamSocket);
await runCommand(process.execPath, [
"scripts/test-gateway.mjs",
"stop",
"--container-name",
containerName,
], {
cwd: rootDir,
allowFailure: true,
}).catch(() => {});
if (gatewayId !== null && fixture?.auth_token) {
await apiRequest(baseUrl, "DELETE", `/edge-gateways/${gatewayId}`, {
token: String(fixture.auth_token),
}).catch(() => {});
}
if (fixture !== null) {
await runPhpFixture(rootDir, composeProject, "cleanup", fixture).catch(() => {});
}
await fs.rm(configDir, { recursive: true, force: true }).catch(() => {});
if (runnerNetworkAttached) {
await disconnectCurrentContainerFromComposeNetwork(rootDir, composeProject).catch(() => {});
}
}
}
main().catch((error) => {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
});
-156
View File
@@ -1,156 +0,0 @@
#!/usr/bin/env sh
set -eu
suite="${1:-}"
case "$suite" in
unit|integration|api|legacy|all)
;;
*)
echo "Usage: $0 <unit|integration|api|legacy|all>" >&2
exit 2
;;
esac
script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
repo_root="$(CDPATH= cd -- "$script_dir/.." && pwd)"
cd "$repo_root"
compose_files="-f docker-compose.yml -f .github/docker-compose.ci.yml"
project_suffix="$(date +%s)-$$"
export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-php-local-${suite}-${project_suffix}}"
export COMPOSE_PROFILES="${COMPOSE_PROFILES:-dev}"
log_dir=".tmp/ci-logs/$suite"
mkdir -p "$log_dir"
env_backup_dir=".tmp/php-ci-env-backup-$project_suffix"
mkdir -p "$env_backup_dir"
had_env=0
had_env_staging=0
if [ -f .env ]; then
cp .env "$env_backup_dir/env"
had_env=1
fi
if [ -f .env.staging ]; then
cp .env.staging "$env_backup_dir/env.staging"
had_env_staging=1
fi
cp .github/ci.env .env
cp .github/ci.env.staging .env.staging
collect_logs() {
status="$1"
if [ "$status" -eq 0 ]; then
return
fi
mkdir -p "$log_dir"
docker compose $compose_files ps > "$log_dir/docker-compose-ps.txt" 2>&1 || true
docker compose $compose_files logs --no-color > "$log_dir/docker-compose.log" 2>&1 || true
docker compose $compose_files cp php1:/var/www/html/build/logs "$log_dir/app-build-logs" >/dev/null 2>&1 || true
docker compose $compose_files cp php1:/var/log/php "$log_dir/php-logs" >/dev/null 2>&1 || true
}
retry_command() {
max_attempts="$1"
shift
attempt=1
while :; do
"$@" && return 0
status="$?"
if [ "$attempt" -ge "$max_attempts" ]; then
return "$status"
fi
sleep_seconds=$((attempt * 5))
echo "Command failed with status $status; retrying in ${sleep_seconds}s (attempt $((attempt + 1))/$max_attempts): $*" >&2
sleep "$sleep_seconds"
attempt=$((attempt + 1))
done
}
composer_install() {
dist_attempts="${PHP_CI_COMPOSER_RETRIES:-3}"
source_attempts="${PHP_CI_COMPOSER_SOURCE_RETRIES:-2}"
if retry_command "$dist_attempts" \
docker compose $compose_files exec -T php1 sh -lc \
'cd /var/www/html && composer install --no-interaction --prefer-dist --no-progress'; then
return 0
fi
echo "Composer dist install failed after ${dist_attempts} attempts; retrying with --prefer-source." >&2
retry_command "$source_attempts" \
docker compose $compose_files exec -T php1 sh -lc \
'cd /var/www/html && composer install --no-interaction --prefer-source --no-progress'
}
configure_ci_docker_subnet() {
if [ -n "${CI_DOCKER_SUBNET:-}" ]; then
return
fi
checksum="$(printf '%s' "$COMPOSE_PROJECT_NAME" | cksum | awk '{print $1}')"
subnet_second=$((64 + ((checksum / 256) % 64)))
subnet_third=$((checksum % 256))
export CI_DOCKER_SUBNET="10.${subnet_second}.${subnet_third}.0/24"
}
cleanup() {
status="$?"
collect_logs "$status"
docker compose $compose_files down -v >/dev/null 2>&1 || true
if [ "$had_env" -eq 1 ]; then
cp "$env_backup_dir/env" .env
else
rm -f .env
fi
if [ "$had_env_staging" -eq 1 ]; then
cp "$env_backup_dir/env.staging" .env.staging
else
rm -f .env.staging
fi
rm -rf "$env_backup_dir"
exit "$status"
}
trap cleanup EXIT INT TERM
configure_ci_docker_subnet
sh scripts/ci-docker-compose-up.sh redis mysql-debug php1
docker compose $compose_files exec -T php1 sh -lc '
set -eu
for i in $(seq 1 90); do
if MYSQL_PWD="${CONFIG_DB_PASSWORD:-debug_root_password}" mysqladmin \
-h "${CONFIG_DB_HOST:-mysql-debug}" \
-P "${CONFIG_DB_PORT:-3306}" \
-u "${CONFIG_DB_USER:-root}" \
ping --silent >/dev/null 2>&1; then
exit 0
fi
sleep 1
done
echo "Timed out waiting for mysql-debug" >&2
exit 1
'
tar \
--exclude='./vendor' \
--exclude='./.phpunit.cache' \
--exclude='./build/logs' \
-C services/nginx/app -cf - . \
| docker compose $compose_files exec -T php1 tar --no-same-owner -C /var/www/html -xf -
docker compose $compose_files exec -T php1 sh -lc 'rm -rf /var/www/repo-root && mkdir -p /var/www/repo-root'
tar \
-cf - \
Dockerfile \
Dockerfile.coolify-api \
services/php/Dockerfile \
services/php/php-fpm-pool.conf \
| docker compose $compose_files exec -T php1 tar --no-same-owner -C /var/www/repo-root -xf -
composer_install
docker compose $compose_files exec -T php1 sh -lc \
"cd /var/www/html && PLENO_REPO_ROOT_FOR_TESTS=/var/www/repo-root composer test:ci:$suite"
+3 -73
View File
@@ -1,32 +1,14 @@
import process from "node:process"; import process from "node:process";
import path from "node:path"; import path from "node:path";
import { createHash } from "node:crypto";
import { pathToFileURL } from "node:url"; import { pathToFileURL } from "node:url";
export const DEFAULT_STAGING_BASE_URL = "https://api.truckwash.io:4433"; export const DEFAULT_STAGING_BASE_URL = "https://api.truckwash.io:4433";
export const EXPECTED_INSTALL_VERSION = "compose-php-agent-v3";
export const REQUIRED_MANIFEST_ARTIFACTS = [
"agent.php",
"lan-worker.php",
"auto-updater.php",
"docker-compose.gateway.yml",
"Dockerfile.edge-agent",
"Dockerfile.lan-worker",
"Dockerfile.auto-updater",
"gateway-launcher.sh",
"truckwash-edge-gateway-stack.service",
"truckwash-edge-agent.service",
];
export const INSTALLER_SCRIPT_REQUIRED_SNIPPETS = [ export const INSTALLER_SCRIPT_REQUIRED_SNIPPETS = [
"/edge-agent/install-token/status", "/edge-agent/install-token/status",
"/edge-agent/artifacts/manifest.json",
"report_install_status", "report_install_status",
'begin_install_phase "VERIFY_TOKEN"', 'begin_install_phase "VERIFY_TOKEN"',
'begin_install_phase "VERIFY_ARTIFACTS"',
'begin_install_phase "WAIT_FOR_CLAIM"', 'begin_install_phase "WAIT_FOR_CLAIM"',
'report_install_status "FAILED"', 'report_install_status "FAILED"',
"verify_manifest_artifact",
EXPECTED_INSTALL_VERSION,
]; ];
export function normalizeBaseUrl(url) { export function normalizeBaseUrl(url) {
@@ -73,20 +55,13 @@ export function buildChecks(baseUrl, installToken) {
name: "Ping", name: "Ping",
url: `${normalizedBaseUrl}/ping`, url: `${normalizedBaseUrl}/ping`,
}, },
{
name: "Artifact manifest",
url: `${normalizedBaseUrl}/edge-agent/artifacts/manifest.json`,
artifactName: "manifest.json",
},
{ {
name: "Agent PHP artifact", name: "Agent PHP artifact",
url: `${normalizedBaseUrl}/edge-agent/artifacts/agent.php`, url: `${normalizedBaseUrl}/edge-agent/artifacts/agent.php`,
artifactName: "agent.php",
}, },
{ {
name: "Service unit artifact", name: "Service unit artifact",
url: `${normalizedBaseUrl}/edge-agent/artifacts/truckwash-edge-agent.service`, url: `${normalizedBaseUrl}/edge-agent/artifacts/truckwash-edge-agent.service`,
artifactName: "truckwash-edge-agent.service",
}, },
{ {
name: "Installer script", name: "Installer script",
@@ -95,42 +70,6 @@ export function buildChecks(baseUrl, installToken) {
]; ];
} }
export function validateArtifactManifestBody(body) {
const manifest = JSON.parse(String(body || ""));
if (manifest.version !== EXPECTED_INSTALL_VERSION) {
throw new Error(`Artifact manifest version mismatch: expected ${EXPECTED_INSTALL_VERSION}, got ${manifest.version}`);
}
if (!Array.isArray(manifest.artifacts)) {
throw new Error("Artifact manifest is missing artifacts.");
}
const byName = new Map(manifest.artifacts.map((artifact) => [artifact?.name, artifact]));
const missingArtifacts = REQUIRED_MANIFEST_ARTIFACTS.filter((artifactName) => !byName.has(artifactName));
if (missingArtifacts.length) {
throw new Error(`Artifact manifest is missing required artifacts: ${missingArtifacts.join(", ")}`);
}
return manifest;
}
export function validateArtifactBodyAgainstManifest(manifest, artifactName, body) {
const artifact = manifest?.artifacts?.find((entry) => entry?.name === artifactName);
if (!artifact) {
throw new Error(`Artifact ${artifactName} is missing from manifest.`);
}
const buffer = Buffer.isBuffer(body) ? body : Buffer.from(String(body || ""));
const sha256 = createHash("sha256").update(buffer).digest("hex");
if (sha256 !== artifact.sha256) {
throw new Error(`Artifact ${artifactName} hash mismatch: ${sha256} !== ${artifact.sha256}`);
}
if (buffer.length !== artifact.bytes) {
throw new Error(`Artifact ${artifactName} size mismatch: ${buffer.length} !== ${artifact.bytes}`);
}
return artifact;
}
export function validateInstallerScriptBody(body) { export function validateInstallerScriptBody(body) {
const source = String(body || ""); const source = String(body || "");
const missingSnippets = INSTALLER_SCRIPT_REQUIRED_SNIPPETS.filter((snippet) => !source.includes(snippet)); const missingSnippets = INSTALLER_SCRIPT_REQUIRED_SNIPPETS.filter((snippet) => !source.includes(snippet));
@@ -168,17 +107,16 @@ export async function runSmoke({ baseUrl, installToken }) {
const checks = buildChecks(baseUrl, installToken); const checks = buildChecks(baseUrl, installToken);
const results = []; const results = [];
let artifactManifest = null;
for (const check of checks) { for (const check of checks) {
process.stdout.write(`[staging-smoke] GET ${check.url}\n`); process.stdout.write(`[staging-smoke] GET ${check.url}\n`);
const response = await fetch(check.url); const response = await fetch(check.url);
const body = Buffer.from(await response.arrayBuffer()); const body = await response.text();
const result = { const result = {
...check, ...check,
status: response.status, status: response.status,
ok: response.ok, ok: response.ok,
bodyPreview: previewBody(body.toString("utf8")), bodyPreview: previewBody(body),
}; };
results.push(result); results.push(result);
@@ -189,16 +127,8 @@ export async function runSmoke({ baseUrl, installToken }) {
); );
} }
if (check.name === "Artifact manifest") {
artifactManifest = validateArtifactManifestBody(body.toString("utf8"));
result.version = artifactManifest.version;
result.artifactCount = artifactManifest.artifacts.length;
}
if (artifactManifest && check.artifactName && check.artifactName !== "manifest.json") {
result.verifiedArtifact = validateArtifactBodyAgainstManifest(artifactManifest, check.artifactName, body);
}
if (check.name === "Installer script") { if (check.name === "Installer script") {
result.verifiedSnippets = validateInstallerScriptBody(body.toString("utf8")); result.verifiedSnippets = validateInstallerScriptBody(body);
} }
} }
@@ -3,13 +3,10 @@ import assert from "node:assert/strict";
import { import {
DEFAULT_STAGING_BASE_URL, DEFAULT_STAGING_BASE_URL,
EXPECTED_INSTALL_VERSION,
INSTALLER_SCRIPT_REQUIRED_SNIPPETS, INSTALLER_SCRIPT_REQUIRED_SNIPPETS,
buildChecks, buildChecks,
normalizeBaseUrl, normalizeBaseUrl,
parseArgs, parseArgs,
validateArtifactBodyAgainstManifest,
validateArtifactManifestBody,
validateInstallerScriptBody, validateInstallerScriptBody,
} from "./staging-edge-gateway-smoke.mjs"; } from "./staging-edge-gateway-smoke.mjs";
@@ -35,58 +32,18 @@ test("buildChecks targets the public staging endpoints", () => {
assert.deepEqual(checks.map((check) => check.url), [ assert.deepEqual(checks.map((check) => check.url), [
"https://api.truckwash.io:4433/ping", "https://api.truckwash.io:4433/ping",
"https://api.truckwash.io:4433/edge-agent/artifacts/manifest.json",
"https://api.truckwash.io:4433/edge-agent/artifacts/agent.php", "https://api.truckwash.io:4433/edge-agent/artifacts/agent.php",
"https://api.truckwash.io:4433/edge-agent/artifacts/truckwash-edge-agent.service", "https://api.truckwash.io:4433/edge-agent/artifacts/truckwash-edge-agent.service",
"https://api.truckwash.io:4433/edge-agent/install.sh?token=abc%20123", "https://api.truckwash.io:4433/edge-agent/install.sh?token=abc%20123",
]); ]);
}); });
test("validateArtifactManifestBody requires v3 install artifacts", () => {
const artifacts = [
"agent.php",
"lan-worker.php",
"auto-updater.php",
"docker-compose.gateway.yml",
"Dockerfile.edge-agent",
"Dockerfile.lan-worker",
"Dockerfile.auto-updater",
"gateway-launcher.sh",
"truckwash-edge-gateway-stack.service",
"truckwash-edge-agent.service",
].map((name) => ({ name, sha256: "abc", bytes: 1 }));
const manifest = validateArtifactManifestBody(JSON.stringify({
version: EXPECTED_INSTALL_VERSION,
artifacts,
}));
assert.equal(manifest.version, EXPECTED_INSTALL_VERSION);
});
test("validateArtifactBodyAgainstManifest verifies size and hash", () => {
const body = Buffer.from("hello");
const manifest = {
artifacts: [{
name: "agent.php",
sha256: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
bytes: body.length,
}],
};
assert.equal(validateArtifactBodyAgainstManifest(manifest, "agent.php", body).name, "agent.php");
});
test("validateInstallerScriptBody requires install-session reporting wiring", () => { test("validateInstallerScriptBody requires install-session reporting wiring", () => {
const script = ` const script = `
INSTALL_STATUS_URL="https://api.truckwash.io:4433/edge-agent/install-token/status" INSTALL_STATUS_URL="https://api.truckwash.io:4433/edge-agent/install-token/status"
fetch_http "Download artifact manifest" "https://api.truckwash.io:4433/edge-agent/artifacts/manifest.json"
report_install_status "FAILED" report_install_status "FAILED"
begin_install_phase "VERIFY_TOKEN" "Verifying install token" begin_install_phase "VERIFY_TOKEN" "Verifying install token"
begin_install_phase "VERIFY_ARTIFACTS" "Verifying edge gateway artifacts"
begin_install_phase "WAIT_FOR_CLAIM" "Waiting for gateway heartbeat and claim" begin_install_phase "WAIT_FOR_CLAIM" "Waiting for gateway heartbeat and claim"
verify_manifest_artifact
${EXPECTED_INSTALL_VERSION}
`; `;
assert.deepEqual(validateInstallerScriptBody(script), INSTALLER_SCRIPT_REQUIRED_SNIPPETS); assert.deepEqual(validateInstallerScriptBody(script), INSTALLER_SCRIPT_REQUIRED_SNIPPETS);
+29 -88
View File
@@ -12,7 +12,7 @@ export const DEFAULT_IMAGE_TAG = "truckwash-edge-agent:test-gateway";
export const DEFAULT_CONFIG_FILE_NAME = "test-gateway.json"; export const DEFAULT_CONFIG_FILE_NAME = "test-gateway.json";
export const DEFAULT_HOST_API_URL = "http://localhost/api"; export const DEFAULT_HOST_API_URL = "http://localhost/api";
export const DEFAULT_CONTAINER_API_URL = "http://caddy"; export const DEFAULT_CONTAINER_API_URL = "http://caddy";
export const DEFAULT_CONTAINER_BROKER_URL = "ws://edge-broker:4300"; export const DEFAULT_CONTAINER_BROKER_URL = "http://edge-broker:4300";
export const DEFAULT_INSTALL_DIR = "/opt/truckwash-edge-agent"; export const DEFAULT_INSTALL_DIR = "/opt/truckwash-edge-agent";
export const DEFAULT_RUNTIME_DIR = `${DEFAULT_INSTALL_DIR}/runtime`; export const DEFAULT_RUNTIME_DIR = `${DEFAULT_INSTALL_DIR}/runtime`;
export const DEFAULT_STATE_DATABASE_PATH = `${DEFAULT_RUNTIME_DIR}/gateway-state.sqlite`; export const DEFAULT_STATE_DATABASE_PATH = `${DEFAULT_RUNTIME_DIR}/gateway-state.sqlite`;
@@ -21,31 +21,6 @@ export const DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 15;
export const DEFAULT_INSTALLED_VERSION = "php-agent-v1"; export const DEFAULT_INSTALLED_VERSION = "php-agent-v1";
const DEFAULT_COMPOSE_SERVICES = ["traefik", "redis", "mysql-debug", "edge-broker", "caddy"]; const DEFAULT_COMPOSE_SERVICES = ["traefik", "redis", "mysql-debug", "edge-broker", "caddy"];
function composeArgs(projectName, args) {
return ["compose", "-p", projectName, ...args];
}
function usesWindowsPathSyntax(filePath) {
return /^[A-Za-z]:($|[\\/])/.test(filePath) || filePath.startsWith("\\\\") || filePath.includes("\\");
}
function pathForInputs(...filePaths) {
const hasWindowsPath = filePaths.some((filePath) => usesWindowsPathSyntax(String(filePath || "")));
return hasWindowsPath ? path.win32 : path;
}
async function resolveRootDir(scriptPath) {
const cwd = process.cwd();
try {
await fs.access(path.join(cwd, "docker-compose.yml"));
return cwd;
} catch {
return path.resolve(path.dirname(scriptPath), "..");
}
}
function printUsage() { function printUsage() {
process.stdout.write(`Usage: process.stdout.write(`Usage:
node scripts/test-gateway.mjs start [--install-token <token>] [--container-name <name>] [--hostname <hostname>] node scripts/test-gateway.mjs start [--install-token <token>] [--container-name <name>] [--hostname <hostname>]
@@ -66,7 +41,6 @@ Options:
--tail <lines> Log lines for the logs action. Default: 200 --tail <lines> Log lines for the logs action. Default: 200
--skip-compose-up Do not start the local Docker Compose stack before start. --skip-compose-up Do not start the local Docker Compose stack before start.
--skip-build Do not rebuild the test gateway image before start. --skip-build Do not rebuild the test gateway image before start.
--copy-config Copy generated config into the container instead of bind mounting it.
`); `);
} }
@@ -76,7 +50,7 @@ export function resolveComposeProjectName(rootDir, env = process.env) {
return explicit; return explicit;
} }
return pathForInputs(rootDir).basename(rootDir); return path.basename(rootDir);
} }
export function resolveComposeNetworkName(rootDir, env = process.env) { export function resolveComposeNetworkName(rootDir, env = process.env) {
@@ -84,13 +58,11 @@ export function resolveComposeNetworkName(rootDir, env = process.env) {
} }
export function resolveConfigDirectory(rootDir, explicitDir = null) { export function resolveConfigDirectory(rootDir, explicitDir = null) {
const pathModule = pathForInputs(rootDir, explicitDir);
if (explicitDir) { if (explicitDir) {
return pathModule.resolve(rootDir, explicitDir); return path.resolve(rootDir, explicitDir);
} }
return pathModule.join(rootDir, ".tmp", "test-gateway"); return path.join(rootDir, ".tmp", "test-gateway");
} }
export function shouldClaimGateway(existingConfig = {}, installToken = "") { export function shouldClaimGateway(existingConfig = {}, installToken = "") {
@@ -163,7 +135,6 @@ export function parseArgs(argv = process.argv.slice(2)) {
tail: "200", tail: "200",
skipComposeUp: false, skipComposeUp: false,
skipBuild: false, skipBuild: false,
copyConfig: false,
}; };
for (let index = 0; index < rest.length; index += 1) { for (let index = 0; index < rest.length; index += 1) {
@@ -218,9 +189,6 @@ export function parseArgs(argv = process.argv.slice(2)) {
case "--skip-build": case "--skip-build":
options.skipBuild = true; options.skipBuild = true;
break; break;
case "--copy-config":
options.copyConfig = true;
break;
case "--help": case "--help":
case "-h": case "-h":
options.help = true; options.help = true;
@@ -277,7 +245,7 @@ async function ensureComposeServices(rootDir, skipComposeUp) {
return; return;
} }
await runCommand("docker", composeArgs(resolveComposeProjectName(rootDir), ["up", "-d", ...DEFAULT_COMPOSE_SERVICES]), { await runCommand("docker", ["compose", "up", "-d", ...DEFAULT_COMPOSE_SERVICES], {
cwd: rootDir, cwd: rootDir,
stdio: "inherit", stdio: "inherit",
}); });
@@ -378,57 +346,35 @@ async function startContainer({
containerName, containerName,
hostname, hostname,
configDir, configDir,
copyConfig,
}) { }) {
const networkName = resolveComposeNetworkName(rootDir); const networkName = resolveComposeNetworkName(rootDir);
const mountedConfigDir = toDockerMountPath(configDir); const mountedConfigDir = toDockerMountPath(configDir);
const containerConfigPath = copyConfig
? `/tmp/${DEFAULT_CONFIG_FILE_NAME}`
: `/config/${DEFAULT_CONFIG_FILE_NAME}`;
const createArgs = [
copyConfig ? "create" : "run",
...(copyConfig ? [] : ["-d"]),
"--name",
containerName,
"--hostname",
hostname,
"--restart",
"unless-stopped",
"--network",
networkName,
...(copyConfig ? [] : ["-v", `${mountedConfigDir}:/config`]),
imageTag,
"--config",
containerConfigPath,
];
await removeContainer(containerName); await removeContainer(containerName);
await runCommand("docker", createArgs, { await runCommand(
cwd: rootDir, "docker",
stdio: "inherit", [
}); "run",
"-d",
if (copyConfig) { "--name",
const configFilePath = path.join(configDir, DEFAULT_CONFIG_FILE_NAME); containerName,
await fs.chmod(configFilePath, 0o666).catch(() => {}); "--hostname",
try { hostname,
await runCommand("docker", [ "--restart",
"cp", "unless-stopped",
configFilePath, "--network",
`${containerName}:${containerConfigPath}`, networkName,
], { "-v",
cwd: rootDir, `${mountedConfigDir}:/config`,
stdio: "inherit", imageTag,
}); "--config",
} finally { `/config/${DEFAULT_CONFIG_FILE_NAME}`,
await fs.chmod(configFilePath, 0o600).catch(() => {}); ],
} {
await runCommand("docker", ["start", containerName], {
cwd: rootDir, cwd: rootDir,
stdio: "inherit", stdio: "inherit",
}); }
} );
} }
async function printStatus({ imageTag, configDir, containerName }) { async function printStatus({ imageTag, configDir, containerName }) {
@@ -471,7 +417,7 @@ async function main() {
} }
const scriptPath = fileURLToPath(import.meta.url); const scriptPath = fileURLToPath(import.meta.url);
const rootDir = await resolveRootDir(scriptPath); const rootDir = path.resolve(path.dirname(scriptPath), "..");
const configDir = resolveConfigDirectory(rootDir, options.configDir); const configDir = resolveConfigDirectory(rootDir, options.configDir);
const configFilePath = path.join(configDir, DEFAULT_CONFIG_FILE_NAME); const configFilePath = path.join(configDir, DEFAULT_CONFIG_FILE_NAME);
@@ -508,7 +454,6 @@ async function main() {
containerName: options.containerName, containerName: options.containerName,
hostname: options.hostname, hostname: options.hostname,
configDir, configDir,
copyConfig: options.copyConfig,
}); });
process.stdout.write(`Test gateway container started. process.stdout.write(`Test gateway container started.
@@ -540,12 +485,8 @@ Gateway ID: ${config.gatewayId ?? "unclaimed"}
} }
const currentFilePath = fileURLToPath(import.meta.url); const currentFilePath = fileURLToPath(import.meta.url);
const currentRealPath = await fs.realpath(currentFilePath).catch(() => currentFilePath);
const invokedScript = process.argv[1] ? path.resolve(process.argv[1]) : ""; const invokedScript = process.argv[1] ? path.resolve(process.argv[1]) : "";
const invokedRealPath = invokedScript !== "" if (invokedScript === path.resolve(currentFilePath)) {
? await fs.realpath(invokedScript).catch(() => invokedScript)
: "";
if (invokedRealPath === currentRealPath) {
main().catch((error) => { main().catch((error) => {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1; process.exitCode = 1;
-6
View File
@@ -52,12 +52,6 @@ test("parseArgs accepts help without an explicit action", () => {
assert.equal(options.help, true); assert.equal(options.help, true);
}); });
test("parseArgs accepts copy config mode", () => {
const options = parseArgs(["start", "--copy-config"]);
assert.equal(options.copyConfig, true);
});
test("buildGatewayConfig applies defaults for a dockerized gateway", () => { test("buildGatewayConfig applies defaults for a dockerized gateway", () => {
const config = buildGatewayConfig({}); const config = buildGatewayConfig({});
-5
View File
@@ -10,11 +10,6 @@
# CORS is handled at the edge by Traefik's headers middleware. # CORS is handled at the edge by Traefik's headers middleware.
# Do not set or strip Access-Control-* headers here to avoid conflicts. # Do not set or strip Access-Control-* headers here to avoid conflicts.
# Do not expose local replication bootstrap material from the public web root.
# Bootstrap snapshots contain sensitive failover credentials.
@replicationBootstrap path /storage/replication-bootstrap.json /storage/replication-bootstrap-*
respond @replicationBootstrap 404
# PHP handling via FastCGI to php-fpm pool # PHP handling via FastCGI to php-fpm pool
php_fastcgi php1:9000 php2:9000 php3:9000 php4:9000 php5:9000 php_fastcgi php1:9000 php2:9000 php3:9000 php4:9000 php5:9000
-5
View File
@@ -10,11 +10,6 @@
# CORS is handled at the edge by Traefik's headers middleware. # CORS is handled at the edge by Traefik's headers middleware.
# Do not set or strip Access-Control-* headers here to avoid conflicts. # Do not set or strip Access-Control-* headers here to avoid conflicts.
# Do not expose local replication bootstrap material from the public web root.
# Bootstrap snapshots contain sensitive failover credentials.
@replicationBootstrap path /storage/replication-bootstrap.json /storage/replication-bootstrap-*
respond @replicationBootstrap 404
# PHP handling via FastCGI to php-fpm pool # PHP handling via FastCGI to php-fpm pool
php_fastcgi php-staging:9000 php_fastcgi php-staging:9000
+578
View File
@@ -0,0 +1,578 @@
{"level":"info","ts":1773826166.9708004,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.336730715,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826169.3245327,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.125025382,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826169.3245995,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.260210293,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826169.5794039,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.500289085,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826170.0778017,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.39669069,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826171.5490258,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.069669237,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826171.9627604,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.497760546,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826173.9760478,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.070845887,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826174.2890058,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.384107891,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826174.4404097,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.39908183,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826174.8434205,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Cache-Control":["no-cache"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.323563505,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826176.69389,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.385544905,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826176.7040854,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.395716047,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826179.9976525,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.252061272,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826179.9976532,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.252032823,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826180.2976575,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.416949968,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826180.702024,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.355183735,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826182.3615072,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.213493623,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826182.3619726,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.214062164,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826184.9015193,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.309120507,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826185.0053287,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Pragma":["no-cache"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.412929425,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826185.1834722,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Pragma":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.458085374,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826185.596906,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.404400262,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826187.2444446,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.245449894,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826187.2444932,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.245510879,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826189.7901962,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.351403066,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826189.845259,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.406399188,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826190.0125787,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.436858971,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826190.1053317,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.061400822,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826191.9140744,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.067352992,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826191.9140744,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Gpc":["1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.067144351,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"]}}
{"level":"info","ts":1773826194.5529275,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.27129644,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826194.5538201,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.272200234,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826194.8282824,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.410512911,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826195.345705,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.450611072,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826196.9063478,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.223679165,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826196.907733,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.225121309,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826199.390079,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.266549333,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826199.54275,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.419228455,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826199.7448642,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.486476363,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826200.1450813,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.406705347,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826200.902575,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Headers":["authorization"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Access-Control-Request-Method":["GET"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["*/*"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.004933848,"size":0,"status":200,"resp_headers":{"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"]}}
{"level":"info","ts":1773826200.9184515,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Access-Control-Request-Method":["GET"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Headers":["authorization"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.003871461,"size":0,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826201.313931,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Authorization":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.408608832,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826201.314516,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.417756253,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826201.6138952,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Authorization":[],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.297119022,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826201.614594,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.298338892,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826201.6740956,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.056775832,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826201.8352685,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.309177004,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826201.835635,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.309922332,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826202.1111634,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.434629705,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826204.2307596,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.270490136,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826204.231079,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.270777164,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826204.9306307,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.834871676,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826205.0144818,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.437812131,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826206.6582522,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.296923602,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826206.7820892,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.421257269,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826209.07706,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.28168601,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826209.0780072,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.282730259,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826209.2607417,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.329227333,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826210.719875,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.336777872,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826212.4557168,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.288418347,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826212.6393747,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.457420958,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826214.6817403,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.060898725,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826214.9970322,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.376193278,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826215.119907,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.37697543,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826215.5844464,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.358253193,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826217.364804,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.336250288,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826217.3651576,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.353011792,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826219.6290405,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.168509855,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826219.6290424,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Cache-Control":["no-cache"],"Pragma":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.168515835,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826220.0472326,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.451005017,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826220.5235884,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.459100139,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826221.9169457,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.056181782,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826221.917784,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.057027708,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826222.9502344,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.668844483,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826223.49054,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.537614159,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826223.9723587,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.479864308,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826224.343807,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.368627458,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826224.856207,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Host":["localhost"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.51019735,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826225.3152466,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.212788185,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826225.3152463,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.213877297,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826225.34763,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.489283626,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826225.3848963,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.280640345,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826225.3853478,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.284002289,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"]}}
{"level":"info","ts":1773826225.8986015,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.548822248,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826226.4152071,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.514747638,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826227.3371081,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.300847773,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826227.7347972,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.698537342,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826229.322464,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.180099137,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826229.3229284,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.182328794,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826229.5229506,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.547793197,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826229.7905357,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.515788574,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826230.0126746,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.487792958,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826230.015993,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.066576546,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826230.3144436,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.486501483,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826230.409719,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.09307799,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826231.4518874,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Access-Control-Request-Headers":["authorization"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["*/*"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"Access-Control-Request-Method":["GET"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.007941754,"size":0,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Server":["Caddy"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826231.9584615,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.504336719,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826232.00635,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.11080176,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826232.393898,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.498349724,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826233.7580044,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.31107267,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826234.0281086,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.268209641,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826234.856958,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.064741677,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826235.0811856,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.410918923,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826235.0965688,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.305344393,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826235.096562,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.305286142,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826235.758473,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.96652785,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826236.2328858,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":1.149166396,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826236.734551,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Access-Control-Request-Headers":["authorization"],"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Method":["GET"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.004409892,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826236.8764596,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Sec-Fetch-Mode":["cors"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.139966551,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826236.8765473,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.146188956,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826237.1359358,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.405598041,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826238.3543074,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.069293506,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826238.6079137,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.078009457,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826239.8719676,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.23115293,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826239.936697,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.297177805,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826239.93727,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.297747795,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826240.177016,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.301852145,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826240.2603126,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.619578163,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826240.2948732,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.115473072,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826242.5489316,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["*/*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"]}},"bytes_read":0,"user_id":"","duration":0.004359668,"size":0,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826242.840928,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.294881878,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826242.8434336,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.29885309,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826243.2189693,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Authorization":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.667883652,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826244.6206405,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.518181713,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826244.7226462,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.099885191,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826245.7282386,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.286842731,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826245.7282357,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.286843033,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826245.729071,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.40778575,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826245.8759341,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.433985598,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"]}}
{"level":"info","ts":1773826246.0560663,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.325072718,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826246.150864,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.709473378,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826247.3969293,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Access-Control-Request-Headers":["authorization"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Access-Control-Request-Method":["GET"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.004395296,"size":0,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826247.4287875,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Cache-Control":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.047798163,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826247.4294498,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.048426199,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826247.4584892,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Authorization":[],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.059579264,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826249.377847,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.434692156,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826249.9012983,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.520719503,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826250.3366525,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.039950465,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826250.3779438,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.21601427,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826250.3779619,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.081639673,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826250.423558,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.043639415,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826250.5279162,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.231591889,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826250.5284529,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.231734683,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826252.2262323,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["*/*"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.002952425,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826252.4899101,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.266604287,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826252.4902215,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Cache-Control":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.266939348,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826252.6353197,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.406947854,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826254.0988395,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.309949283,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826254.4142804,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.313229674,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826255.1939533,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.052917017,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826255.1943216,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Pragma":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.053291772,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826255.4786673,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.474724907,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826255.5260544,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.384703087,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826255.526563,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.385158447,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826256.113922,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.632900798,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826257.084515,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["*/*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Origin":["http://localhost:5173"],"Access-Control-Request-Headers":["authorization"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.003835498,"size":0,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826257.4487476,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.362283977,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826257.4492424,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"],"Sec-Fetch-Dest":["empty"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.368815775,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826257.6348832,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.55442496,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826259.0092547,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.376877321,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826259.6307788,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.619805015,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826260.145301,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.161590542,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826260.2827036,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.30025666,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826260.3579996,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.374290372,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826260.358565,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.375673265,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826260.3591213,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.495672175,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826260.9677532,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.005428654,"size":0,"status":200,"resp_headers":{"Content-Type":["application/json"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"]}}
{"level":"info","ts":1773826261.0812206,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.719474137,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826261.131758,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.152860403,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826261.9229548,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Access-Control-Request-Headers":["authorization"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Method":["GET"],"Referer":["http://localhost:5173/"],"Accept":["*/*"],"Origin":["http://localhost:5173"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.004796388,"size":0,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826262.1808798,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.262721205,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826262.3055892,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.387411559,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826262.3256145,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.399990235,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826264.0834198,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.605018867,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826264.4656212,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.379761165,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826265.159418,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Mode":["cors"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.337052132,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826265.1599183,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.338656721,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826265.159959,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.338665488,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826265.1599865,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.457723334,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826265.1608107,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.338186345,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826265.7457488,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.58234562,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826266.760665,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.003348445,"size":0,"status":200,"resp_headers":{"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"]}}
{"level":"info","ts":1773826267.099361,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.341886103,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826267.3528302,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.589548578,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826267.3544612,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.597138218,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826268.7962244,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.471960855,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826269.3874242,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.587826508,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826269.8864343,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.340632699,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826270.0011191,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.335564386,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826270.001632,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.336146783,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826270.1797457,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.289916136,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826270.244424,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.578840314,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826270.4123607,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.746906789,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826272.5798728,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Access-Control-Request-Method":["GET"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["*/*"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Access-Control-Request-Headers":["authorization"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.004585942,"size":0,"status":200,"resp_headers":{"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"]}}
{"level":"info","ts":1773826272.9760356,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.393565183,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826272.9766512,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.401031403,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"]}}
{"level":"info","ts":1773826273.757031,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":1.18182985,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826274.6127648,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.479104184,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826275.1434913,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.528749182,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826275.6235788,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.137716661,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826275.6238408,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.259205249,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826275.7685578,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.142324079,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826275.8653605,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.379437951,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826275.8658516,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.379986088,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826275.8674288,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.381435854,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826277.4191809,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["*/*"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Headers":["authorization"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.003160298,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Server":["Caddy"],"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826277.513314,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.083039333,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826277.513765,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.083276757,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826277.839947,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Authorization":[],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.418394618,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826279.311053,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.32588745,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826279.553671,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.240337937,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826280.4236414,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.219593491,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826280.4239388,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.097175698,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826280.609539,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.282907568,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826280.6095476,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.282945769,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826280.6837862,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.357176393,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826280.9124198,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.486043498,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826282.2816343,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Referer":["http://localhost:5173/"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.004272434,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826282.5014513,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.224096667,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826282.5932522,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"Authorization":[],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.309016132,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826282.762372,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.485008109,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826284.2473116,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.419944897,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826284.7155588,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.465644762,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826285.3731148,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.33201332,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826285.382416,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.204788999,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826285.3824492,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.204646815,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826285.527407,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.349765478,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826285.5277753,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.350248889,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826286.06445,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.688880394,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826287.113801,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.004059447,"size":0,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"Server":["Caddy"]}}
{"level":"info","ts":1773826287.419412,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.309658213,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826287.4198174,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.31017131,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826287.5938196,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Authorization":[],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.6"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.477313442,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826289.0298648,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.360409759,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826289.3319452,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.299687827,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826290.3376222,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.452177689,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826290.6402397,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.300352632,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826291.9410431,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.003132931,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826292.3842604,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Authorization":[],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.440860827,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826293.9343493,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.439952281,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826294.3440604,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.407360227,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826295.1711533,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.449134138,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826295.8527117,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.679430866,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826298.575222,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.239223342,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826299.1671898,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.589304101,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826299.9768214,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.42280195,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826300.3889349,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.409451117,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826304.4682908,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.297302011,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826304.6760879,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.205166353,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826305.7035754,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.309903469,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826306.06365,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.35758992,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826309.426586,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.411602016,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826309.7365882,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.307559619,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826310.5728822,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.340467364,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826311.1087942,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.53365395,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826314.3029535,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.451387991,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826314.7390003,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.433637015,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826315.145722,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Gpc":["1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.071645191,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826315.2184045,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.07060575,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826318.9327765,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.223040782,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826319.372069,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.435527131,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826320.2377045,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.310641846,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826320.6059396,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.366049107,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826321.019666,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Headers":["authorization"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["*/*"],"Access-Control-Request-Method":["GET"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.005125441,"size":0,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826321.0330973,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["same-site"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.004094559,"size":0,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826321.065755,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.050754619,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826321.0663266,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43688","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.049707903,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826321.0714931,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.058299498,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826321.3352807,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.31271021,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826321.3353183,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.320461487,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826321.33543,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.322217999,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826321.336045,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.319932569,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826321.454813,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.115766323,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826324.1107216,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.563325744,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826324.51782,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.404870334,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826325.0767791,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.313798868,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826325.516587,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.43789269,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826328.5783246,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.192510186,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826328.9600253,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.33591841,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826329.9461956,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.343614408,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826330.0325968,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.08370118,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826333.4395497,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.203711159,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826334.8488133,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.407913962,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826336.189148,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.775229936,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826336.5129259,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.321547405,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826339.8193758,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.778237382,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826340.484483,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.661839092,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826341.052277,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.565531578,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826341.1713974,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.116995635,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826344.3424993,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.459823803,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826344.9462178,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.600610796,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826345.400256,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.298155702,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826346.0480387,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.645135856,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826349.156766,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.41996003,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826349.5705867,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.410768907,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826350.1640942,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.209420494,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826350.5521028,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.385858993,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"]}}
{"level":"info","ts":1773826354.1082847,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.532853099,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826354.7754962,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.664431534,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826355.290558,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"X-Real-Ip":["172.18.0.1"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.498205064,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826355.7959008,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.50333574,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826359.4631894,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":1.046471635,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826359.8179033,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.352511432,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826360.3723376,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.552128883,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826361.6249712,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.250746166,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826364.3044171,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":1.036105659,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826365.7734706,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.500940415,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826366.3151329,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.539422058,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826366.9041758,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.585534079,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826369.6179724,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.552731569,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826370.0349576,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.414776487,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826370.3518865,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.066909465,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826370.719497,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.365441292,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826374.2375813,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.322032518,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826374.37445,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.134229129,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"]}}
{"level":"info","ts":1773826375.2585166,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.120342752,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826375.7874947,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.526105892,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826379.1529784,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.391462836,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826379.737555,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.582494402,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826380.8159359,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.838749844,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826380.9110472,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.0926876,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826381.0814805,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Access-Control-Request-Method":["GET"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.003464438,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826381.2150488,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43688","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.135002073,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826381.353293,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.275410157,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826381.4263964,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.347429674,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826381.4263968,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Pragma":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.347228296,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826381.4269779,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.347126352,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826381.4264684,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.346251891,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826381.4288092,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["*/*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.33539058,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["*"],"Server":["Caddy"],"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826381.725631,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Gpc":["1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.640679594,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826381.8789194,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Authorization":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.150518915,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826384.2855997,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.68677901,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826384.419676,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.131463831,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826385.296497,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.482235259,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826385.9763112,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.677502726,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826389.0283816,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.585028881,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826389.7241693,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.693332769,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826389.9961612,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.270062312,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826390.0560722,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"],"Origin":["http://localhost:5173"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.058272897,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826394.1860425,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.905101807,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826394.8389401,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.650678782,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826396.8760483,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":1.062733619,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826397.4128597,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.533872213,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826399.3711278,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Gpc":["1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.275338328,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826399.8407238,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.467445268,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826400.8211105,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Sec-Gpc":["1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.503336673,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826401.3927042,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.568617153,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826404.9276648,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.981689797,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826405.3865352,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.45668309,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826405.7212017,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.332704571,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"]}}
{"level":"info","ts":1773826406.24432,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.520752258,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826409.4144459,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.619358427,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826409.8032606,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.386445718,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826410.3962119,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.382138844,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826410.8523376,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.453331614,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826413.9524436,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.317999654,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826414.2163806,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.261299034,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826415.6521916,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.804872582,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826416.2200892,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.56558394,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826418.6992216,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.230586771,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826419.2315469,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.528964389,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826420.1994016,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Origin":["http://localhost:5173"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.516745862,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826420.5655065,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Sec-Fetch-Dest":["empty"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.364192672,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826423.6324012,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.336594454,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826424.1642637,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.529683486,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826424.9427843,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.417239584,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826425.25549,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.310120034,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826429.4322617,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.294377725,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826429.6303096,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.195912736,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826430.5558884,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.20020931,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826430.6338358,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.075953449,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826434.7283158,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.749575954,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826435.4002984,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.669408179,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826436.0852852,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.681158799,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826436.6932082,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.605147457,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826439.2860832,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.454779172,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826439.8214653,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.533406742,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826440.4660094,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.417585991,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826440.8675385,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.398997562,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826441.1522396,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.005090754,"size":0,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"]}}
{"level":"info","ts":1773826441.1626284,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43688","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["*/*"],"Access-Control-Request-Headers":["authorization"],"Access-Control-Request-Method":["GET"],"Referer":["http://localhost:5173/"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.002807514,"size":0,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826441.3849103,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.236757779,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826441.3854418,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.236391524,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826441.473212,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.326754444,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826441.4736257,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.325698639,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826441.473778,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.32471909,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826441.4738522,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.325672989,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826441.6903734,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Authorization":[],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.53561241,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826442.0301497,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.337053066,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826443.734806,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.057697718,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826444.2450626,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.328282862,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826445.1334405,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.237094723,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826445.4486778,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.312441438,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826448.7812324,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.276600283,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826449.2926083,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.509287977,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826450.3206308,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.58550417,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826450.4873588,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.163633449,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826453.6153731,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.261944392,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826453.958753,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.341302764,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826454.935746,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.358773571,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826455.1492126,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.21140475,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826459.3996916,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.239497817,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826460.007519,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.605473485,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826460.4367466,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.054840982,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826460.6668808,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.227959031,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826464.721618,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.516813632,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"error","ts":1773826464.7636774,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/departments","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.558936596,"size":111,"status":401,"resp_headers":{"Server":["Caddy"],"Status":["401 Unauthorized"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826464.8303838,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.606549241,"size":735,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"],"Server":["Caddy"]}}
{"level":"info","ts":1773826469.3836143,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.334187709,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826474.1502144,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.245024222,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826479.0968618,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.355127487,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826484.1756196,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.582159192,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826490.199057,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.804270926,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"error","ts":1773826493.4475157,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/departments","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.367623667,"size":111,"status":401,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["401 Unauthorized"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826493.652717,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Server":["0b64317da32a"]}},"bytes_read":0,"user_id":"","duration":0.572779984,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826495.118352,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":2.018611954,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1773826499.0833688,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":1.143638682,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826501.2080162,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["*/*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Headers":["authorization"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Access-Control-Request-Method":["GET"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.005821988,"size":0,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"]}}
{"level":"info","ts":1773826501.2162066,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43688","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"OPTIONS","host":"localhost","uri":"/ping","headers":{"Accept":["*/*"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Access-Control-Request-Method":["GET"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"Access-Control-Request-Headers":["authorization"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.002818669,"size":0,"status":200,"resp_headers":{"Content-Type":["application/json"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Access-Control-Allow-Headers":["*"],"Server":["Caddy"]}}
{"level":"info","ts":1773826501.3781483,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"55612","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.174063301,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826501.3781526,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.176308399,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826501.378165,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38484","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.174260755,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826501.5183465,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"41354","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.313467045,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826501.5187743,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"38470","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.314577812,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826501.6968036,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Authorization":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.486608324,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826502.2942631,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"43684","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Origin":["http://localhost:5173"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.090459252,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826502.6856973,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Authorization":[],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"]}},"bytes_read":0,"user_id":"","duration":0.986786982,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826503.1070702,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["0b64317da32a"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.329633423,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826507.9452004,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.330087892,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826512.99931,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Server":["0b64317da32a"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.545302906,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826518.1475375,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["0b64317da32a"],"X-Forwarded-Host":["localhost"],"Cache-Control":["no-cache"]}},"bytes_read":0,"user_id":"","duration":0.843202642,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"error","ts":1773826523.1225665,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"53894","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["0b64317da32a"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.007480931,"size":0,"status":502,"resp_headers":{"Server":["Caddy"]}}
{"level":"info","ts":1773826533.9554763,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Cache-Control":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":1.158905036,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826536.9338899,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":2.965340628,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1773826538.547151,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.910843089,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826543.0969942,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.629550116,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826547.7162032,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.395867947,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826554.0806224,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.934719548,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826558.3746915,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.379371845,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826561.631695,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"Cache-Control":["no-cache"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.377331413,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826561.631695,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.371442491,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826561.7586732,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.500663238,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826561.9058926,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.644824932,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826561.9058967,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.64280669,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826561.9065697,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.648565892,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826562.008113,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37104","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.744631138,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826562.745923,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Authorization":[],"Referer":["http://localhost:5173/"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.836964667,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826563.3830287,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.539581044,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826568.1970885,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.52872221,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826572.8485315,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["cf3606ea293b"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.329632635,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826577.5866983,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.246261041,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826583.649508,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.472560865,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826588.5287206,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.50213333,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826593.668152,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.794128836,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826598.0569248,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"]}},"bytes_read":0,"user_id":"","duration":0.3455446,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"]}}
{"level":"info","ts":1773826602.9588246,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.390555761,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826607.8736682,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.477587811,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826613.8135455,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.595724596,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826618.519822,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.469573999,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826621.5868504,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Cache-Control":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.269667338,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826621.5905738,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.273376391,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826621.7035396,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.386524533,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826621.703969,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.388962343,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826621.7040298,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.386885628,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826621.704212,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37104","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.388677708,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826621.7050414,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.387877353,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826622.5236545,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Fetch-Dest":["empty"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.816353161,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826623.0119357,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.108896811,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826628.1503737,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.398736135,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826633.0426946,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.458874081,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826637.766165,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.344250942,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826643.0986114,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.839096138,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826647.9172235,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37104","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Fetch-Site":["same-site"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.293915662,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826648.011681,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/departments","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.388416664,"size":111,"status":401,"resp_headers":{"Server":["Caddy"],"Status":["401 Unauthorized"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826649.156614,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":1.486069357,"size":735,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826652.7637017,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.287533254,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1773826657.6192331,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""]}},"bytes_read":0,"user_id":"","duration":0.304165869,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826662.484209,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.332249171,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826667.3339016,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.335003003,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826672.4496367,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"]}},"bytes_read":0,"user_id":"","duration":0.615472516,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"info","ts":1773826677.9462786,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.284755105,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826681.6040988,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.215791054,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"]}}
{"level":"info","ts":1773826681.604095,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.219227531,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826681.6041782,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-Server":["cf3606ea293b"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"]}},"bytes_read":0,"user_id":"","duration":0.220043092,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826681.7778602,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.391469649,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826681.7804117,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.391671889,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826681.7854679,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.398119439,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1773826682.2771666,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Authorization":[],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.496209609,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1773826682.2781265,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37104","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.893277484,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826683.178707,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37104","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.663738661,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"info","ts":1773826688.3381941,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37104","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.989881721,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826688.52026,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/departments","headers":{"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.517618677,"size":111,"status":401,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"Status":["401 Unauthorized"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"info","ts":1773826689.0678031,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["cf3606ea293b"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":1.064311318,"size":101,"status":200,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1773826689.8285036,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":1.804908345,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1773826693.5957835,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.750935948,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826697.3757653,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/departments","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"Origin":["http://localhost:5173"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.293729729,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826697.3757768,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Gpc":["1"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.293211565,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826697.7717874,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/guest/departments?include_lanes=true","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["same-site"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.666268221,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"error","ts":1773826701.5105367,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Authorization":[],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.53491044,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"error","ts":1773826701.7883804,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/auth/reCAPTCHA/public","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua-Mobile":["?0"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.816917711,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826701.853436,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"50300","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.339505529,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"error","ts":1773826701.8538113,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/auth/session","headers":{"Origin":["http://localhost:5173"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.6"],"Referer":["http://localhost:5173/"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.877614488,"size":72,"status":404,"resp_headers":{"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"error","ts":1773826701.984547,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"35502","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/guest/departments?include_lanes=true","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Cache-Control":["no-cache"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"]}},"bytes_read":0,"user_id":"","duration":0.172874076,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"Server":["Caddy"]}}
{"level":"error","ts":1773826702.0168717,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37054","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/auth/session","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Authorization":[],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.159969922,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826702.242746,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/departments","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"],"X-Forwarded-Port":["80"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Referer":["http://localhost:5173/"],"X-Forwarded-Proto":["http"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"Authorization":[],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":1.295959827,"size":72,"status":404,"resp_headers":{"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"error","ts":1773826702.3069026,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37072","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/departments","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Cache-Control":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.577721038,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826702.350668,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/auth/reCAPTCHA/public","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"]}},"bytes_read":0,"user_id":"","duration":0.555174873,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"error","ts":1773826702.8036997,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/departments","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Origin":["http://localhost:5173"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.557892356,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"error","ts":1773826702.8073766,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":1.077441848,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"error","ts":1773826703.4966028,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"]}},"bytes_read":0,"user_id":"","duration":0.684978633,"size":72,"status":404,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"error","ts":1773826708.0270803,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Fetch-Mode":["cors"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Authorization":[],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Origin":["http://localhost:5173"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.486951214,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"error","ts":1773826708.3696883,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Sec-Fetch-Mode":["cors"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.60697834,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"error","ts":1773826708.5068195,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Authorization":[],"Origin":["http://localhost:5173"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.476434872,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Server":["Caddy"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826708.6845531,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.17228174,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826713.0960047,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Server":["cf3606ea293b"],"Origin":["http://localhost:5173"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.704865696,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"]}}
{"level":"error","ts":1773826713.2229595,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""]}},"bytes_read":0,"user_id":"","duration":0.607684269,"size":72,"status":404,"resp_headers":{"Status":["404 Not Found"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"error","ts":1773826713.5119667,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Forwarded-Port":["80"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"Accept-Language":["en-GB,en;q=0.9"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"]}},"bytes_read":0,"user_id":"","duration":0.163409461,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826713.7476597,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Gpc":["1"]}},"bytes_read":0,"user_id":"","duration":0.648740312,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"error","ts":1773826717.6552675,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["cf3606ea293b"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":0.195128913,"size":72,"status":404,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"error","ts":1773826717.6557715,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Authorization":[],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.429146662,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826718.6928694,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Pragma":["no-cache"],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Cache-Control":["no-cache"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.496269746,"size":72,"status":404,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"error","ts":1773826718.6936173,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Fetch-Site":["same-site"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":1.035254318,"size":72,"status":404,"resp_headers":{"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"error","ts":1773826722.7025964,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Fetch-Site":["same-site"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.6"],"X-Forwarded-Host":["localhost"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Authorization":[],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.621774556,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"]}}
{"level":"error","ts":1773826723.3734825,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.338516843,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826723.3745852,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Authorization":[],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.667566008,"size":72,"status":404,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"error","ts":1773826723.4913466,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept":["application/json, text/plain, */*"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"X-Forwarded-Proto":["http"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":1.187267605,"size":72,"status":404,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"]}}
{"level":"error","ts":1773826727.4152093,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Gpc":["1"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Authorization":[],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Mode":["cors"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"]}},"bytes_read":0,"user_id":"","duration":0.497147953,"size":72,"status":404,"resp_headers":{"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"error","ts":1773826727.8890858,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37084","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.747295957,"size":72,"status":404,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826728.1730373,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Server":["cf3606ea293b"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Authorization":[],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Port":["80"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":0.754468923,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826728.6366873,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"X-Forwarded-For":["172.18.0.1"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Accept-Language":["en-GB,en;q=0.9"],"Cache-Control":["no-cache"],"Origin":["http://localhost:5173"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Pragma":["no-cache"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.746952708,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"error","ts":1773826732.221285,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Server":["cf3606ea293b"],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Authorization":[],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-site"],"X-Real-Ip":["172.18.0.1"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Language":["en-GB,en;q=0.6"],"Origin":["http://localhost:5173"],"Referer":["http://localhost:5173/"]}},"bytes_read":0,"user_id":"","duration":0.456508601,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"]}}
{"level":"error","ts":1773826732.2212849,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Accept-Language":["en-GB,en;q=0.9"],"Sec-Gpc":["1"],"X-Forwarded-For":["172.18.0.1"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Origin":["http://localhost:5173"],"Sec-Ch-Ua-Mobile":["?0"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Referer":["http://localhost:5173/"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Mode":["cors"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"]}},"bytes_read":0,"user_id":"","duration":0.234413772,"size":72,"status":404,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"]}}
{"level":"error","ts":1773826732.912765,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37058","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Referer":["http://localhost:5173/"],"Sec-Fetch-Mode":["cors"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["cf3606ea293b"],"X-Real-Ip":["172.18.0.1"],"Authorization":[],"Origin":["http://localhost:5173"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Accept-Language":["en-GB,en;q=0.6"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["same-site"],"Sec-Gpc":["1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"Accept":["application/json, text/plain, */*"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":0.687789355,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"error","ts":1773826733.4827838,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"37092","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/:4433/ping","headers":{"Accept":["application/json, text/plain, */*"],"Origin":["http://localhost:5173"],"Pragma":["no-cache"],"Sec-Ch-Ua":["\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Brave\";v=\"146\""],"Sec-Ch-Ua-Platform":["\"Windows\""],"Sec-Fetch-Dest":["empty"],"Sec-Fetch-Site":["same-site"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"Sec-Fetch-Mode":["cors"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Cache-Control":["no-cache"],"Referer":["http://localhost:5173/"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["cf3606ea293b"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Gpc":["1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Language":["en-GB,en;q=0.9"]}},"bytes_read":0,"user_id":"","duration":0.752769145,"size":72,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Access-Control-Allow-Headers":["Content-Type, Authorization, X-Customer-Number"],"Server":["Caddy"],"Access-Control-Allow-Methods":["GET, POST, PUT, DELETE, OPTIONS"],"Content-Type":["application/json; charset=utf-8"],"Status":["404 Not Found"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1774359410.7662385,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"]}},"bytes_read":0,"user_id":"","duration":1.419261331,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359411.8936794,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-Server":["4513febd0e98"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":1.095823154,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359413.4743714,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"]}},"bytes_read":0,"user_id":"","duration":1.569025749,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1774359414.9427295,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["4513febd0e98"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.453042866,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359416.5113647,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":1.555332673,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1774359417.9860232,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.45695831,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359418.5943637,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.595441308,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1774359419.6813276,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"42848","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":1.075013274,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359428.338712,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.910140374,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359429.22114,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.862201927,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359429.1707215,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":1.389081049,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359429.3823278,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.194319251,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359430.8409362,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":1.437162491,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1774359431.0736253,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["4513febd0e98"]}},"bytes_read":0,"user_id":"","duration":0.21548384,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359432.5463028,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.457021421,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359433.1541202,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.593640533,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359433.7735548,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.604391296,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359434.523004,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.735703774,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359435.3819067,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["4513febd0e98"]}},"bytes_read":0,"user_id":"","duration":0.841783131,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1774359436.0587533,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.65987697,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1774359436.7292738,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.654426603,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1774359437.4872267,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"],"X-Forwarded-Host":["localhost"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.737746322,"size":101,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"]}}
{"level":"info","ts":1774359438.1417146,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":0.638910553,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1774359438.9001129,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["4513febd0e98"]}},"bytes_read":0,"user_id":"","duration":0.744432539,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"]}}
{"level":"info","ts":1774359439.7426257,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-Server":["4513febd0e98"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":0.820541924,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359440.5554748,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"Accept-Encoding":["gzip"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"]}},"bytes_read":0,"user_id":"","duration":0.798630689,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359441.0909092,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"]}},"bytes_read":0,"user_id":"","duration":0.522440142,"size":101,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359441.87296,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/ping","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":0.768554371,"size":101,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359451.9496348,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":3.16841118,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359454.7461386,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":2.771633599,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359456.8014095,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"Accept-Encoding":["gzip"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":2.043545388,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359458.8112676,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.997627811,"size":735,"status":200,"resp_headers":{"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1774359460.0082002,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Host":["localhost"],"X-Forwarded-Server":["4513febd0e98"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"]}},"bytes_read":0,"user_id":"","duration":2.719624341,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"Content-Type":["application/json; charset=utf-8"],"X-Powered-By":["PHP/8.2.15"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359462.1559546,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Server":["4513febd0e98"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"]}},"bytes_read":0,"user_id":"","duration":2.129318162,"size":735,"status":200,"resp_headers":{"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"],"Server":["Caddy"]}}
{"level":"info","ts":1774359464.0260193,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Port":["80"],"X-Real-Ip":["172.18.0.1"]}},"bytes_read":0,"user_id":"","duration":1.858119212,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359466.158465,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Real-Ip":["172.18.0.1"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Prefix":["/api"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"]}},"bytes_read":0,"user_id":"","duration":2.120016812,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359468.6958492,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["4513febd0e98"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Prefix":["/api"],"X-Real-Ip":["172.18.0.1"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"]}},"bytes_read":0,"user_id":"","duration":2.523297017,"size":735,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"],"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"]}}
{"level":"info","ts":1774359470.677206,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.6","remote_port":"53830","client_ip":"172.18.0.6","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/guest/departments?include_lanes=true","headers":{"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Real-Ip":["172.18.0.1"],"Accept-Encoding":["gzip"],"X-Forwarded-Server":["4513febd0e98"],"User-Agent":["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.26100.7920"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Prefix":["/api"]}},"bytes_read":0,"user_id":"","duration":1.968647671,"size":735,"status":200,"resp_headers":{"Content-Encoding":["gzip"],"Vary":["Accept-Encoding"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1775042310.766326,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"49692","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"caddy","uri":"/economic/payment-terms","headers":{"User-Agent":["curl/8.14.1"],"Accept":["*/*"],"Authorization":[]}},"bytes_read":0,"user_id":"","duration":2.173924739,"size":2514,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1775042310.819993,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"49690","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"caddy","uri":"/economic/layouts","headers":{"User-Agent":["curl/8.14.1"],"Accept":["*/*"],"Authorization":[]}},"bytes_read":0,"user_id":"","duration":2.227691681,"size":721,"status":200,"resp_headers":{"Content-Type":["application/json; charset=utf-8"],"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"]}}
{"level":"info","ts":1775042311.5330641,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"49706","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"caddy","uri":"/economic/config","headers":{"User-Agent":["curl/8.14.1"],"Accept":["*/*"],"Authorization":[]}},"bytes_read":0,"user_id":"","duration":2.692967287,"size":604,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.30"],"Content-Type":["application/json; charset=utf-8"]}}
{"level":"info","ts":1775042615.6337473,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"172.18.0.5","remote_port":"54866","client_ip":"172.18.0.5","proto":"HTTP/1.1","method":"GET","host":"caddy","uri":"/customers?page=1&limit=100","headers":{"User-Agent":["curl/8.14.1"],"Accept":["*/*"],"Authorization":[]}},"bytes_read":0,"user_id":"","duration":10.029899933,"size":164529,"status":200,"resp_headers":{"Server":["Caddy"],"X-Powered-By":["PHP/8.2.15"],"Content-Type":["application/json; charset=utf-8"]}}
-42
View File
@@ -1,42 +0,0 @@
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /dev/stdout;
error_log /dev/stderr warn;
sendfile on;
keepalive_timeout 65;
client_max_body_size 64m;
gzip on;
gzip_types application/json application/javascript application/xml text/css text/plain;
server {
listen 80 default_server;
server_name _;
root /var/www/html;
index index.php;
location / {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_read_timeout 60s;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param X_REQUEST_ID $http_x_request_id;
}
location ~ /\.(?!well-known) {
deny all;
}
}
}
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
set -e
mkdir -p /run/nginx
php-fpm -D
exec nginx -g "daemon off;"
+7 -1
View File
@@ -3,7 +3,13 @@ FROM php:8.2-cli
WORKDIR /opt/truckwash-edge-agent WORKDIR /opt/truckwash-edge-agent
RUN set -eux; \ RUN set -eux; \
php -r 'foreach (["curl", "sqlite3"] as $extension) { if (!extension_loaded($extension)) { fwrite(STDERR, "Missing PHP extension: {$extension}\n"); exit(1); } }' apt-get update; \
apt-get install -y --no-install-recommends \
libcurl4-openssl-dev \
libsqlite3-dev \
ca-certificates; \
docker-php-ext-install curl sqlite3; \
rm -rf /var/lib/apt/lists/*
COPY services/nginx/app/resources/edge-gateway-agent/ ./ COPY services/nginx/app/resources/edge-gateway-agent/ ./
+36 -382
View File
@@ -17,8 +17,6 @@ const DEFAULT_UPDATE_VERIFICATION_TIMEOUT_SECONDS = 45;
const DEFAULT_UPDATE_VERIFY_INTERVAL_MS = 500; const DEFAULT_UPDATE_VERIFY_INTERVAL_MS = 500;
const DEFAULT_UPDATE_RESTART_GRACE_MS = 150; const DEFAULT_UPDATE_RESTART_GRACE_MS = 150;
const DEFAULT_BROKER_RECONNECT_DELAY_MS = 1500; const DEFAULT_BROKER_RECONNECT_DELAY_MS = 1500;
const DEFAULT_SHELLY_LOCAL_HTTP_TIMEOUT_MS = 1200;
const MAX_RELAY_TOGGLE_AFTER_SECONDS = 5;
const UPDATE_VERIFY_COMMAND = "post-update-verify"; const UPDATE_VERIFY_COMMAND = "post-update-verify";
const execFile = promisify(execFileCallback); const execFile = promisify(execFileCallback);
@@ -139,23 +137,16 @@ function buildTransportHeartbeatState(brokerState = {}) {
status: "ONLINE", status: "ONLINE",
metadata: { metadata: {
command_transport: brokerConnected ? "BROKER_FAST_PATH" : "API_POLLING", command_transport: brokerConnected ? "BROKER_FAST_PATH" : "API_POLLING",
shell_transport: brokerConnected ? "BROKER_FAST_PATH" : "API_POLLING",
broker_connected: brokerConnected, broker_connected: brokerConnected,
broker_url: brokerState.url || null, broker_url: brokerState.url || null,
broker_last_error: brokerState.lastError || null, broker_last_error: brokerState.lastError || null,
broker_last_connected_at: brokerState.lastConnectedAt || null, broker_last_connected_at: brokerState.lastConnectedAt || null,
broker_last_disconnected_at: brokerState.lastDisconnectedAt || null, broker_last_disconnected_at: brokerState.lastDisconnectedAt || null,
broker_disconnect_reason: brokerState.disconnectReason || null, broker_disconnect_reason: brokerState.disconnectReason || null,
broker_last_close_code: brokerState.lastCloseCode ?? null,
broker_last_close_clean: brokerState.lastCloseClean ?? null,
}, },
}; };
} }
function isShellAccessEnabled(config = {}) {
return config.enableShellAccess === true;
}
function normalizeBrokerBaseUrl(value) { function normalizeBrokerBaseUrl(value) {
const trimmed = String(value || "").trim().replace(/\/+$/, ""); const trimmed = String(value || "").trim().replace(/\/+$/, "");
if (trimmed === "") { if (trimmed === "") {
@@ -186,30 +177,6 @@ function buildBrokerSocketUrl(brokerUrl, gatewayId, token) {
return `${baseUrl}/ws/agent?${search.toString()}`; return `${baseUrl}/ws/agent?${search.toString()}`;
} }
function redactBrokerSocketUrl(value) {
try {
const parsed = new URL(String(value || ""));
for (const key of ["token", "agentToken", "agent_token"]) {
if (parsed.searchParams.has(key)) {
parsed.searchParams.set(key, "***");
}
}
return parsed.toString();
} catch {
return String(value || "").replace(/([?&](?:token|agentToken|agent_token)=)[^&]+/gi, "$1***");
}
}
function normalizeBrokerSocketError(error, socketUrl) {
const message = error instanceof Error && error.message
? error.message
: error?.message
? String(error.message)
: "Broker connection failed";
const redactedUrl = redactBrokerSocketUrl(socketUrl);
return redactedUrl ? `${message} (${redactedUrl})` : message;
}
async function readSocketMessageText(data) { async function readSocketMessageText(data) {
if (typeof data === "string") { if (typeof data === "string") {
return data; return data;
@@ -458,64 +425,8 @@ export async function claimIfNeeded(config, configPath, fetchImpl = fetch) {
return nextConfig; return nextConfig;
} }
function makeHttpTimeoutError(timeoutMs) { async function fetchJson(url, fetchImpl = fetch) {
const error = new Error(`HTTP request timed out after ${timeoutMs}ms`); const response = await fetchImpl(url);
error.code = "EDGE_AGENT_HTTP_TIMEOUT";
return error;
}
function isHttpTimeoutError(error) {
return error?.code === "EDGE_AGENT_HTTP_TIMEOUT";
}
function resolveShellyLocalHttpTimeoutMs(options = {}) {
const configured = Number(options.timeoutMs ?? process.env.EDGE_SHELLY_LOCAL_HTTP_TIMEOUT_MS);
if (Number.isFinite(configured) && configured > 0) {
return Math.max(50, Math.floor(configured));
}
return DEFAULT_SHELLY_LOCAL_HTTP_TIMEOUT_MS;
}
function resolveRelayToggleAfterSeconds(payload = {}) {
const configured = Number(payload.toggleAfter ?? payload.toggle_after ?? payload.timer);
if (!Number.isFinite(configured) || configured <= 0) {
return null;
}
return Math.min(Math.floor(configured), MAX_RELAY_TOGGLE_AFTER_SECONDS);
}
async function fetchJson(url, fetchImpl = fetch, options = {}) {
const timeoutMs = Number(options.timeoutMs || 0);
let timeout = null;
let controller = null;
const requestOptions = {};
if (timeoutMs > 0 && typeof AbortController !== "undefined") {
controller = new AbortController();
requestOptions.signal = controller.signal;
}
let response;
try {
const fetchPromise = Promise.resolve().then(() => fetchImpl(url, requestOptions));
response = timeoutMs > 0
? await Promise.race([
fetchPromise,
new Promise((_, reject) => {
timeout = setTimeout(() => {
controller?.abort();
reject(makeHttpTimeoutError(timeoutMs));
}, timeoutMs);
}),
])
: await fetchPromise;
} finally {
if (timeout !== null) {
clearTimeout(timeout);
}
}
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP ${response.status}`); throw new Error(`HTTP ${response.status}`);
} }
@@ -540,99 +451,13 @@ function expandCandidateIps(options = {}) {
return []; return [];
} }
function normalizeShellyDeviceGeneration(value) {
if (Number.isInteger(value) && value > 0) {
return value;
}
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
return Math.trunc(value);
}
return inferShellyDeviceGenerationFromString(value);
}
function inferShellyDeviceGenerationFromString(value) {
const normalized = String(value || "").trim();
if (!normalized) {
return null;
}
const explicit = normalized.match(/\bgen(?:eration)?\s*([1-9]\d*)\b/i);
if (explicit) {
return Number(explicit[1]);
}
const upper = normalized.toUpperCase();
const sSeries = upper.match(/\bS([3-9])(?:[A-Z0-9]+)?-[A-Z0-9-]+\b/);
if (sSeries) {
return Number(sSeries[1]);
}
if (/\b(?:SHELLY\s+)?(?:PLUS|PRO)\b/i.test(normalized) || /\bSP[A-Z0-9]+-[A-Z0-9-]+\b/.test(upper)) {
return 2;
}
if (/\bSH[A-Z0-9]+-?[A-Z0-9-]*\b/.test(upper)) {
return 1;
}
return null;
}
function resolveShellyDeviceGeneration(identity = {}) {
for (const candidate of [
identity.gen,
identity.generation,
identity.device_generation,
identity.capabilities?.generation,
]) {
const generation = normalizeShellyDeviceGeneration(candidate);
if (generation !== null) {
return generation;
}
}
for (const candidate of [
identity.model,
identity.type,
identity.app,
identity.name,
identity.id,
identity.mac,
]) {
const generation = inferShellyDeviceGenerationFromString(candidate);
if (generation !== null) {
return generation;
}
}
return null;
}
function resolveShellyCommandGeneration(payload = {}) {
return resolveShellyDeviceGeneration({
gen: payload.gen ?? payload.generation ?? payload.deviceGeneration ?? payload.device_generation,
device_generation: payload.device_generation,
capabilities: payload.capabilities,
model: payload.model ?? payload.deviceModel ?? payload.device_model ?? payload.deviceType ?? payload.device_type,
type: payload.type ?? payload.deviceType ?? payload.device_type,
app: payload.app,
name: payload.name ?? payload.deviceName ?? payload.device_name,
id: payload.deviceId ?? payload.device_id ?? payload.relayId ?? payload.relay_id,
mac: payload.mac,
});
}
export async function discoverShellyDevices(options = {}, fetchImpl = fetch) { export async function discoverShellyDevices(options = {}, fetchImpl = fetch) {
const candidateIps = expandCandidateIps(options); const candidateIps = expandCandidateIps(options);
const discovered = []; const discovered = [];
await Promise.all(candidateIps.map(async (ip) => { await Promise.all(candidateIps.map(async (ip) => {
try { try {
const identity = await fetchJson(`http://${ip}/shelly`, fetchImpl, { const identity = await fetchJson(`http://${ip}/shelly`, fetchImpl);
timeoutMs: resolveShellyLocalHttpTimeoutMs(options),
});
discovered.push({ discovered.push({
id: identity.mac || identity.id || ip, id: identity.mac || identity.id || ip,
device_id: identity.mac || identity.id || ip, device_id: identity.mac || identity.id || ip,
@@ -641,7 +466,7 @@ export async function discoverShellyDevices(options = {}, fetchImpl = fetch) {
channel_count: Number(identity.num_outputs || identity.num_switches || 1), channel_count: Number(identity.num_outputs || identity.num_switches || 1),
online: true, online: true,
capabilities: { capabilities: {
generation: resolveShellyDeviceGeneration(identity), generation: identity.gen || null,
}, },
metadata: identity, metadata: identity,
}); });
@@ -656,41 +481,24 @@ export async function discoverShellyDevices(options = {}, fetchImpl = fetch) {
export async function getRelayStatus(payload, fetchImpl = fetch) { export async function getRelayStatus(payload, fetchImpl = fetch) {
const ip = payload.localIp || payload.local_ip || payload.ip; const ip = payload.localIp || payload.local_ip || payload.ip;
const channel = Number.isInteger(payload.channel) ? payload.channel : 0; const channel = Number.isInteger(payload.channel) ? payload.channel : 0;
const timeoutMs = resolveShellyLocalHttpTimeoutMs(payload);
if (!ip) { if (!ip) {
throw new Error("Missing relay local IP"); throw new Error("Missing relay local IP");
} }
const attempts = [
async () => {
const rpc = await fetchJson(`http://${ip}/rpc/Switch.GetStatus?id=${channel}`, fetchImpl, { timeoutMs });
return {
online: true,
on: Boolean(rpc.output),
raw: rpc,
};
},
async () => {
const legacy = await fetchJson(`http://${ip}/relay/${channel}`, fetchImpl, { timeoutMs });
return {
online: true,
on: Boolean(legacy.ison ?? legacy.output),
raw: legacy,
};
},
];
try { try {
return await Promise.any(attempts.map((attempt) => attempt())); const rpc = await fetchJson(`http://${ip}/rpc/Switch.GetStatus?id=${channel}`, fetchImpl);
} catch (error) { return {
const errors = Array.isArray(error?.errors) ? error.errors : [error]; online: true,
const message = errors on: Boolean(rpc.output),
.map((entry) => entry?.message || String(entry)) raw: rpc,
.filter(Boolean) };
.join("; "); } catch {
const relayStatusError = new Error(message ? `Unable to read relay status: ${message}` : "Unable to read relay status"); const legacy = await fetchJson(`http://${ip}/relay/${channel}`, fetchImpl);
relayStatusError.cause = error; return {
throw relayStatusError; online: true,
on: Boolean(legacy.ison ?? legacy.output),
raw: legacy,
};
} }
} }
@@ -698,114 +506,25 @@ export async function setRelayState(payload, fetchImpl = fetch) {
const ip = payload.localIp || payload.local_ip || payload.ip; const ip = payload.localIp || payload.local_ip || payload.ip;
const channel = Number.isInteger(payload.channel) ? payload.channel : 0; const channel = Number.isInteger(payload.channel) ? payload.channel : 0;
const on = Boolean(payload.on); const on = Boolean(payload.on);
const timeoutMs = resolveShellyLocalHttpTimeoutMs(payload);
const toggleAfter = resolveRelayToggleAfterSeconds(payload);
const timerQuery = toggleAfter === null ? "" : `&toggle_after=${encodeURIComponent(String(toggleAfter))}`;
const legacyTimerQuery = toggleAfter === null ? "" : `&timer=${encodeURIComponent(String(toggleAfter))}`;
const deviceGeneration = resolveShellyCommandGeneration(payload);
if (!ip) { if (!ip) {
throw new Error("Missing relay local IP"); throw new Error("Missing relay local IP");
} }
const runRpcSwitch = async () => { try {
const rpc = await fetchJson(`http://${ip}/rpc/Switch.Set?id=${channel}&on=${on ? "true" : "false"}${timerQuery}`, fetchImpl, { timeoutMs }); const rpc = await fetchJson(`http://${ip}/rpc/Switch.Set?id=${channel}&on=${on ? "true" : "false"}`, fetchImpl);
return { return {
online: true, online: true,
on: Boolean(rpc.output ?? on), on: Boolean(rpc.output ?? on),
raw: rpc, raw: rpc,
}; };
}; } catch {
const runLegacySwitch = async () => { const legacy = await fetchJson(`http://${ip}/relay/${channel}?turn=${on ? "on" : "off"}`, fetchImpl);
const legacy = await fetchJson(`http://${ip}/relay/${channel}?turn=${on ? "on" : "off"}${legacyTimerQuery}`, fetchImpl, { timeoutMs });
return { return {
online: true, online: true,
on: Boolean(legacy.ison ?? legacy.output ?? on), on: Boolean(legacy.ison ?? legacy.output ?? on),
raw: legacy, raw: legacy,
}; };
};
if (toggleAfter !== null) {
const attempts = deviceGeneration === 1
? [runLegacySwitch, runRpcSwitch]
: [runRpcSwitch, runLegacySwitch];
let lastError = null;
for (const attempt of attempts) {
try {
return await attempt();
} catch (error) {
if (isHttpTimeoutError(error)) {
throw error;
}
lastError = error;
}
}
throw lastError || new Error("Unable to switch relay");
} }
try {
return await runRpcSwitch();
} catch (error) {
if (isHttpTimeoutError(error)) {
throw error;
}
return await runLegacySwitch();
}
}
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) { async function fetchArtifactBuffer(url, expectedSha256, label, fetchImpl = fetch) {
@@ -813,15 +532,6 @@ async function fetchArtifactBuffer(url, expectedSha256, label, fetchImpl = fetch
return null; return null;
} }
if (!expectedSha256) {
throw new Error(`${label} checksum is required`);
}
const normalizedExpectedSha256 = String(expectedSha256).toLowerCase();
if (!/^[a-f0-9]{64}$/.test(normalizedExpectedSha256)) {
throw new Error(`${label} checksum must be a valid sha256 hex digest`);
}
const response = await fetchImpl(url); const response = await fetchImpl(url);
if (!response.ok) { if (!response.ok) {
throw new Error(`${label} download failed: HTTP ${response.status}`); throw new Error(`${label} download failed: HTTP ${response.status}`);
@@ -829,7 +539,7 @@ async function fetchArtifactBuffer(url, expectedSha256, label, fetchImpl = fetch
const buffer = Buffer.from(await response.arrayBuffer()); const buffer = Buffer.from(await response.arrayBuffer());
const sha256 = createHash("sha256").update(buffer).digest("hex"); const sha256 = createHash("sha256").update(buffer).digest("hex");
if (normalizedExpectedSha256 !== sha256.toLowerCase()) { if (expectedSha256 && String(expectedSha256).toLowerCase() !== sha256.toLowerCase()) {
throw new Error(`${label} checksum mismatch`); throw new Error(`${label} checksum mismatch`);
} }
@@ -1412,13 +1122,12 @@ export function createShellBridge(sendMessage, { createPtyProcess = createDefaul
sessions.set(sessionId, sessionRecord); sessions.set(sessionId, sessionRecord);
sendMessage({ type: "SHELL_OPENED", sessionId }); sendMessage({ type: "SHELL_OPENED", sessionId });
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : String(error);
sendMessage({ sendMessage({
type: "SHELL_OUTPUT", type: "SHELL_OUTPUT",
sessionId, sessionId,
data: `Failed to start root shell: ${message}\r\n`, data: `Failed to start root shell: ${error instanceof Error ? error.message : String(error)}\r\n`,
}); });
sendMessage({ type: "SHELL_EXIT", sessionId, code: 1, reason: "shell_spawn_failed", message }); sendMessage({ type: "SHELL_EXIT", sessionId, code: 1 });
} }
}; };
@@ -1470,10 +1179,6 @@ export async function handleAgentCommand(command, deps = {}) {
return await getRelayStatus(command.payload || {}, fetchImpl); return await getRelayStatus(command.payload || {}, fetchImpl);
case "SET_RELAY_STATE": case "SET_RELAY_STATE":
return await setRelayState(command.payload || {}, fetchImpl); 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": case "RUN_UPDATE":
return await runUpdate(command.payload || {}, fetchImpl, deps); return await runUpdate(command.payload || {}, fetchImpl, deps);
case "UNINSTALL_AGENT": case "UNINSTALL_AGENT":
@@ -1757,15 +1462,13 @@ function normalizeShellEvent(message) {
} }
if (message.type === "SHELL_EXIT") { if (message.type === "SHELL_EXIT") {
const reason = String(message.reason || "agent_exit");
return { return {
sessionId: String(sessionId), sessionId: String(sessionId),
event: { event: {
type: "CLOSED", type: "CLOSED",
payload: { payload: {
code: Number.isFinite(message.code) ? message.code : 0, code: Number.isFinite(message.code) ? message.code : 0,
reason, reason: "agent_exit",
message: message.message ? String(message.message) : null,
}, },
}, },
}; };
@@ -1857,10 +1560,6 @@ export async function processPolledShellAction(config, action, shell, fetchImpl
} }
try { try {
if (!isShellAccessEnabled(config)) {
throw new Error("Shell access is disabled by local configuration");
}
if (actionType === "OPEN") { if (actionType === "OPEN") {
await shell.open(payload); await shell.open(payload);
} else if (actionType === "INPUT") { } else if (actionType === "INPUT") {
@@ -1900,8 +1599,6 @@ function createBrokerBridge({
disconnectReason: null, disconnectReason: null,
lastConnectedAt: null, lastConnectedAt: null,
lastDisconnectedAt: null, lastDisconnectedAt: null,
lastCloseCode: null,
lastCloseClean: null,
}; };
let stopped = false; let stopped = false;
@@ -1959,8 +1656,6 @@ function createBrokerBridge({
state.connected = true; state.connected = true;
state.lastError = null; state.lastError = null;
state.disconnectReason = null; state.disconnectReason = null;
state.lastCloseCode = null;
state.lastCloseClean = null;
state.lastConnectedAt = formatUpdateTimestamp(); state.lastConnectedAt = formatUpdateTimestamp();
}; };
@@ -1995,30 +1690,18 @@ function createBrokerBridge({
} }
if (message.type === "OPEN_ROOT_SHELL") { if (message.type === "OPEN_ROOT_SHELL") {
if (!isShellAccessEnabled(config)) {
throw new Error("Shell access is disabled by local configuration");
}
await shell.open(message.payload || {}); await shell.open(message.payload || {});
return; return;
} }
if (message.type === "SHELL_INPUT") { if (message.type === "SHELL_INPUT") {
if (!isShellAccessEnabled(config)) {
throw new Error("Shell access is disabled by local configuration");
}
shell.input(message.payload || {}); shell.input(message.payload || {});
return; return;
} }
if (message.type === "RESIZE_ROOT_SHELL") { if (message.type === "RESIZE_ROOT_SHELL") {
if (!isShellAccessEnabled(config)) {
throw new Error("Shell access is disabled by local configuration");
}
shell.resize(message.payload || {}); shell.resize(message.payload || {});
return; return;
} }
if (message.type === "CLOSE_ROOT_SHELL") { if (message.type === "CLOSE_ROOT_SHELL") {
if (!isShellAccessEnabled(config)) {
throw new Error("Shell access is disabled by local configuration");
}
shell.close(message.payload || {}); shell.close(message.payload || {});
} }
} catch { } catch {
@@ -2026,19 +1709,14 @@ function createBrokerBridge({
} }
}; };
socket.onerror = (error) => { socket.onerror = () => {
state.lastError = normalizeBrokerSocketError(error, socketUrl); state.lastError = "Broker connection failed";
}; };
socket.onclose = (event) => { socket.onclose = (event) => {
socket = null; socket = null;
state.connected = false; state.connected = false;
state.disconnectReason = event.reason || "broker_disconnected"; state.disconnectReason = event.reason || "broker_disconnected";
state.lastCloseCode = Number.isFinite(Number(event.code)) ? Number(event.code) : null;
state.lastCloseClean = typeof event.wasClean === "boolean" ? event.wasClean : null;
if (state.lastCloseCode && state.lastCloseCode !== 1000 && !state.lastError) {
state.lastError = `Broker websocket closed with code ${state.lastCloseCode}`;
}
state.lastDisconnectedAt = formatUpdateTimestamp(); state.lastDisconnectedAt = formatUpdateTimestamp();
if (!stopped) { if (!stopped) {
scheduleReconnect(); scheduleReconnect();
@@ -2086,18 +1764,19 @@ export async function startAgent({
const commandPollTimeoutSeconds = Number(config.commandPollTimeoutSeconds || 20); const commandPollTimeoutSeconds = Number(config.commandPollTimeoutSeconds || 20);
const commandPollRetryDelayMs = Number(config.commandPollRetryDelayMs || 1000); const commandPollRetryDelayMs = Number(config.commandPollRetryDelayMs || 1000);
const brokerReconnectDelayMs = Number(config.brokerReconnectDelayMs || DEFAULT_BROKER_RECONNECT_DELAY_MS); const brokerReconnectDelayMs = Number(config.brokerReconnectDelayMs || DEFAULT_BROKER_RECONNECT_DELAY_MS);
const shellActionPollTimeoutSeconds = Number(config.shellActionPollTimeoutSeconds || 20);
const shellActionPollRetryDelayMs = Number(config.shellActionPollRetryDelayMs || 1000);
let stopped = false; let stopped = false;
let cpuSnapshot = null; let cpuSnapshot = null;
let lastHeartbeatLatencyMs = null; let lastHeartbeatLatencyMs = null;
void createShellBridgeImpl;
let brokerBridge = null; let brokerBridge = null;
const shellEventPublisher = createShellEventPublisher(config, fetchImpl); const shell = {
const shell = createShellBridgeImpl((message) => { open: async () => {},
brokerBridge?.send(message); input: () => {},
shellEventPublisher.publish(message); resize: () => {},
}); close: () => {},
dispose: () => {},
};
brokerBridge = createBrokerBridge({ brokerBridge = createBrokerBridge({
config, config,
shell, shell,
@@ -2162,32 +1841,8 @@ export async function startAgent({
} }
}; };
const runShellActionPollLoop = async () => {
while (!stopped) {
try {
const action = await pollShellActionJob(config, fetchImpl, shellActionPollTimeoutSeconds);
if (stopped) {
break;
}
if (!action) {
continue;
}
await processPolledShellAction(config, action, shell, fetchImpl);
} catch {
if (stopped) {
break;
}
await new Promise((resolve) => setTimeout(resolve, shellActionPollRetryDelayMs));
}
}
};
await sendTransportHeartbeat(); await sendTransportHeartbeat();
const commandPollPromise = runCommandPollLoop(); const commandPollPromise = runCommandPollLoop();
const shellActionPollPromise = runShellActionPollLoop();
const timer = setInterval(() => { const timer = setInterval(() => {
sendTransportHeartbeat().catch(() => {}); sendTransportHeartbeat().catch(() => {});
@@ -2198,9 +1853,8 @@ export async function startAgent({
stopped = true; stopped = true;
clearInterval(timer); clearInterval(timer);
brokerBridge?.stop(); brokerBridge?.stop();
await shellEventPublisher.drain();
shell.dispose(); shell.dispose();
await Promise.allSettled([commandPollPromise, shellActionPollPromise]); await Promise.allSettled([commandPollPromise]);
}; };
return { return {
+2 -351
View File
@@ -1,7 +1,6 @@
import test from "node:test"; import test from "node:test";
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { execFile as execFileCallback } from "node:child_process"; import { execFile as execFileCallback } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import os from "node:os"; import os from "node:os";
import path from "node:path"; import path from "node:path";
@@ -12,13 +11,11 @@ import {
buildStatusReport, buildStatusReport,
claimIfNeeded, claimIfNeeded,
createShellBridge, createShellBridge,
discoverShellyDevices,
finalizePendingUpdateOnStartup, finalizePendingUpdateOnStartup,
getAgentStatus, getAgentStatus,
getRelayStatus, getRelayStatus,
loadConfig, loadConfig,
parseCliArgs, parseCliArgs,
processPolledShellAction,
processPolledCommand, processPolledCommand,
runCli, runCli,
runUpdate, runUpdate,
@@ -41,10 +38,6 @@ function makeFetchResponse(body) {
}; };
} }
function sha256Hex(body) {
return createHash("sha256").update(body).digest("hex");
}
async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, description = "condition" } = {}) { async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, description = "condition" } = {}) {
const deadline = Date.now() + timeoutMs; const deadline = Date.now() + timeoutMs;
@@ -127,215 +120,6 @@ test("relay status and switch commands support both Shelly RPC and legacy endpoi
assert.equal(switched.on, false); 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");
return {
ok: true,
async json() {
return {
id: "shelly1minig3-e4b3231f6410",
mac: "E4B3231F6410",
model: "S3SW-001X8EU",
type: "Shelly 1 Mini Gen3",
num_switches: 1,
};
},
};
});
assert.equal(inventory.length, 1);
assert.equal(inventory[0].device_id, "E4B3231F6410");
assert.equal(inventory[0].model, "S3SW-001X8EU");
assert.equal(inventory[0].capabilities.generation, 3);
});
test("relay status reads use the legacy endpoint when the RPC status endpoint stalls", async () => {
const urls = [];
const fakeFetch = async (url) => {
urls.push(String(url));
if (String(url).includes("Switch.GetStatus")) {
return new Promise(() => {});
}
if (String(url) === "http://10.1.0.31/relay/0") {
return {
ok: true,
async json() {
return { ison: false };
},
};
}
throw new Error(`Unexpected URL: ${url}`);
};
const startedAt = Date.now();
const status = await getRelayStatus({ localIp: "10.1.0.31", channel: 0, timeoutMs: 50 }, fakeFetch);
assert.equal(status.online, true);
assert.equal(status.on, false);
assert.deepEqual(urls.sort(), [
"http://10.1.0.31/relay/0",
"http://10.1.0.31/rpc/Switch.GetStatus?id=0",
]);
assert.ok(Date.now() - startedAt < 250);
});
test("relay switch commands fail quickly when the local Shelly request stalls", async () => {
let calls = 0;
const hangingFetch = async () => {
calls += 1;
return new Promise(() => {});
};
const startedAt = Date.now();
await assert.rejects(
() => setRelayState({ localIp: "10.1.0.31", channel: 0, on: true, timeoutMs: 50 }, hangingFetch),
/HTTP request timed out after 50ms/
);
assert.equal(calls, 1);
assert.ok(Date.now() - startedAt < 500);
});
test("relay switch commands pass timer values to local Shelly APIs", async () => {
const legacyUrls = [];
const legacyFetch = async (url) => {
legacyUrls.push(String(url));
return {
ok: true,
async json() {
return { ison: true, has_timer: true, timer_duration: 3 };
},
};
};
await setRelayState({
localIp: "10.1.0.31",
channel: 0,
on: true,
toggleAfter: 3,
deviceGeneration: 1,
}, legacyFetch);
assert.equal(
legacyUrls[0],
"http://10.1.0.31/relay/0?turn=on&timer=3"
);
const gen3Urls = [];
const gen3Fetch = async (url) => {
gen3Urls.push(String(url));
return {
ok: true,
async json() {
return { output: true };
},
};
};
await setRelayState({
localIp: "10.1.0.31",
channel: 0,
on: true,
toggle_after: 3,
device_generation: 3,
}, gen3Fetch);
assert.equal(
gen3Urls[0],
"http://10.1.0.31/rpc/Switch.Set?id=0&on=true&toggle_after=3"
);
const fallbackUrls = [];
const legacyFallbackFetch = async (url) => {
fallbackUrls.push(String(url));
if (String(url).includes("/rpc/")) {
throw new Error("RPC switch endpoint unsupported");
}
return {
ok: true,
async json() {
return { ison: true, has_timer: true, timer_duration: 3 };
},
};
};
await setRelayState({
localIp: "10.1.0.31",
channel: 0,
on: true,
toggle_after: 3,
device_model: "S3SW-001X8EU",
}, legacyFallbackFetch);
assert.deepEqual(fallbackUrls, [
"http://10.1.0.31/rpc/Switch.Set?id=0&on=true&toggle_after=3",
"http://10.1.0.31/relay/0?turn=on&timer=3",
]);
const cappedUrls = [];
const cappedFetch = async (url) => {
cappedUrls.push(String(url));
return {
ok: true,
async json() {
return { output: true };
},
};
};
await setRelayState({
localIp: "10.1.0.31",
channel: 0,
on: true,
toggle_after: 999999999,
device_generation: 3,
}, cappedFetch);
assert.equal(
cappedUrls[0],
"http://10.1.0.31/rpc/Switch.Set?id=0&on=true&toggle_after=5"
);
});
test("runUpdate stages a pending verification restart after installing new artifacts", async () => { test("runUpdate stages a pending verification restart after installing new artifacts", async () => {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-update-")); const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-update-"));
const configPath = path.join(tempDir, "config.json"); const configPath = path.join(tempDir, "config.json");
@@ -358,23 +142,19 @@ test("runUpdate stages a pending verification restart after installing new artif
execCalls.push({ command, args, options }); execCalls.push({ command, args, options });
return { stdout: "{}" }; return { stdout: "{}" };
}; };
const agentBody = "// new agent\n";
const packageBody = JSON.stringify({ name: "new-edge-agent" }, null, 2);
const fakeFetch = async (url) => { const fakeFetch = async (url) => {
if (String(url).endsWith("/agent.mjs")) { if (String(url).endsWith("/agent.mjs")) {
return makeFetchResponse(agentBody); return makeFetchResponse("// new agent\n");
} }
if (String(url).endsWith("/package.json")) { if (String(url).endsWith("/package.json")) {
return makeFetchResponse(packageBody); return makeFetchResponse(JSON.stringify({ name: "new-edge-agent" }, null, 2));
} }
throw new Error(`Unexpected URL: ${url}`); throw new Error(`Unexpected URL: ${url}`);
}; };
const result = await runUpdate({ const result = await runUpdate({
artifactUrl: "https://api.example.test/edge-agent/artifacts/agent.mjs", artifactUrl: "https://api.example.test/edge-agent/artifacts/agent.mjs",
sha256: sha256Hex(agentBody),
packageUrl: "https://api.example.test/edge-agent/artifacts/package.json", packageUrl: "https://api.example.test/edge-agent/artifacts/package.json",
packageSha256: sha256Hex(packageBody),
targetVersion: "1.1.0", targetVersion: "1.1.0",
releaseChannel: "stable", releaseChannel: "stable",
restartMode: "spawn", restartMode: "spawn",
@@ -402,45 +182,6 @@ test("runUpdate stages a pending verification restart after installing new artif
await rm(tempDir, { recursive: true, force: true }); await rm(tempDir, { recursive: true, force: true });
}); });
test("runUpdate rejects artifacts without required checksums", async () => {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-update-checksum-"));
const configPath = path.join(tempDir, "config.json");
const liveConfig = {
apiUrl: "https://api.example.test",
gatewayId: 42,
agentToken: "agent-token",
installDir: tempDir,
restartMode: "spawn",
installedVersion: "1.0.0",
targetVersion: "1.0.0",
};
await writeFile(configPath, JSON.stringify(liveConfig, null, 2));
await writeFile(path.join(tempDir, "agent.mjs"), "// old agent\n");
let fetchCalled = false;
await assert.rejects(
runUpdate({
artifactUrl: "https://api.example.test/edge-agent/artifacts/agent.mjs",
targetVersion: "1.1.0",
}, async () => {
fetchCalled = true;
return makeFetchResponse("// new agent\n");
}, {
configPath,
config: liveConfig,
liveConfig,
execFileImpl: async () => ({ stdout: "{}" }),
}),
/Agent artifact checksum is required/
);
assert.equal(fetchCalled, false);
assert.equal(await readFile(path.join(tempDir, "agent.mjs"), "utf8"), "// old agent\n");
await rm(tempDir, { recursive: true, force: true });
});
test("handleAgentCommand returns an uninstall follow-up envelope for gateway removal", async () => { test("handleAgentCommand returns an uninstall follow-up envelope for gateway removal", async () => {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-uninstall-envelope-")); const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-uninstall-envelope-"));
@@ -732,39 +473,6 @@ test("shell bridge proxies PTY output, input, resize, close, and dispose events"
assert.ok(messages.some((message) => message.type === "SHELL_EXIT" && message.code === 0)); assert.ok(messages.some((message) => message.type === "SHELL_EXIT" && message.code === 0));
}); });
test("shell bridge reports structured spawn failures", async () => {
const messages = [];
const shell = createShellBridge(
(message) => messages.push(message),
{
createPtyProcess: async () => {
throw new Error("node-pty unavailable");
},
}
);
await shell.open({ sessionId: "spawn-failure-shell" });
assert.ok(
messages.some(
(message) =>
message.type === "SHELL_OUTPUT" &&
message.sessionId === "spawn-failure-shell" &&
/Failed to start root shell: node-pty unavailable/.test(message.data)
)
);
assert.ok(
messages.some(
(message) =>
message.type === "SHELL_EXIT" &&
message.sessionId === "spawn-failure-shell" &&
message.code === 1 &&
message.reason === "shell_spawn_failed" &&
message.message === "node-pty unavailable"
)
);
});
test("startAgent reports API polling metadata, executes polled commands, and uploads shell events", async () => { test("startAgent reports API polling metadata, executes polled commands, and uploads shell events", async () => {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-")); const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-"));
const configPath = path.join(tempDir, "config.json"); const configPath = path.join(tempDir, "config.json");
@@ -777,7 +485,6 @@ test("startAgent reports API polling metadata, executes polled commands, and upl
commandPollRetryDelayMs: 5, commandPollRetryDelayMs: 5,
shellActionPollTimeoutSeconds: 0, shellActionPollTimeoutSeconds: 0,
shellActionPollRetryDelayMs: 5, shellActionPollRetryDelayMs: 5,
enableShellAccess: true,
})); }));
const heartbeats = []; const heartbeats = [];
@@ -1002,7 +709,6 @@ test("startAgent reports API polling metadata, executes polled commands, and upl
assert.equal(commandResultPosts[0].body.ok, true); assert.equal(commandResultPosts[0].body.ok, true);
assert.equal(Array.isArray(commandResultPosts[0].body.payload.inventory), true); assert.equal(Array.isArray(commandResultPosts[0].body.payload.inventory), true);
assert.equal(commandResultPosts[0].body.payload.inventory[0].device_id, "AA:BB:CC:DD:EE:FF"); assert.equal(commandResultPosts[0].body.payload.inventory[0].device_id, "AA:BB:CC:DD:EE:FF");
assert.equal(commandResultPosts[0].body.payload.inventory[0].capabilities.generation, 2);
assert.ok(shellCalls.some((call) => call.type === "open")); assert.ok(shellCalls.some((call) => call.type === "open"));
assert.ok(shellCalls.some((call) => call.type === "close")); assert.ok(shellCalls.some((call) => call.type === "close"));
@@ -1036,61 +742,6 @@ test("startAgent reports API polling metadata, executes polled commands, and upl
} }
}); });
test("processPolledShellAction denies shell access when locally disabled", async () => {
const submissions = [];
const fakeFetch = async (url, options = {}) => {
if (/\/shell-actions\/\d+\/result$/.test(String(url))) {
submissions.push({ url, body: JSON.parse(options.body) });
return {
ok: true,
async json() {
return { data: { acknowledged: true } };
},
};
}
throw new Error(`Unexpected URL: ${url}`);
};
const shell = {
async open() {
throw new Error("should not run");
},
input() {
throw new Error("should not run");
},
resize() {
throw new Error("should not run");
},
close() {
throw new Error("should not run");
},
};
const result = await processPolledShellAction(
{
apiUrl: "https://api.example.test",
gatewayId: 42,
agentToken: "agent-token",
enableShellAccess: false,
},
{
id: 501,
actionType: "OPEN",
payload: {
sessionId: 44,
},
},
shell,
fakeFetch
);
assert.equal(result.ok, false);
assert.match(result.error, /Shell access is disabled/);
assert.equal(submissions.length, 1);
assert.equal(submissions[0].body.ok, false);
});
test("status helpers report config without exposing the agent token", async () => { test("status helpers report config without exposing the agent token", async () => {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-status-")); const tempDir = await mkdtemp(path.join(os.tmpdir(), "edge-agent-status-"));
const configPath = path.join(tempDir, "config.json"); const configPath = path.join(tempDir, "config.json");
+180 -518
View File
@@ -1,11 +1,7 @@
import http from "node:http"; import http from "node:http";
import { randomUUID } from "node:crypto"; import { randomUUID } from "node:crypto";
import { fileURLToPath } from "node:url";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
const DEFAULT_SHELL_OPEN_TIMEOUT_MS = 15000;
const TELEMETRY_INGEST_ERROR_MESSAGE = "Telemetry ingestion failed";
function parseJsonBody(req) { function parseJsonBody(req) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let raw = ""; let raw = "";
@@ -56,33 +52,7 @@ function resolveAuthMode(options = {}, managerUrl = "") {
if (process.env.EDGE_AUTH_MODE) { if (process.env.EDGE_AUTH_MODE) {
return process.env.EDGE_AUTH_MODE; return process.env.EDGE_AUTH_MODE;
} }
return "strict"; return managerUrl ? "manager" : "stub";
}
function resolveSharedSecret(options = {}) {
return String(options.sharedSecret ?? process.env.EDGE_BROKER_SHARED_SECRET ?? "").trim();
}
function requireSharedSecret(req, res, sharedSecret) {
if (sharedSecret === "") {
jsonResponse(res, 503, {
ok: false,
error: "Edge broker shared secret is not configured",
shared_secret_required: true,
});
return false;
}
if (req.headers["x-edge-broker-secret"] !== sharedSecret) {
jsonResponse(res, 403, {
ok: false,
error: "Forbidden",
shared_secret_required: true,
});
return false;
}
return true;
} }
function parseScopes(value) { function parseScopes(value) {
@@ -135,53 +105,11 @@ function sendJson(ws, payload) {
return true; return true;
} }
function currentTimestamp() {
return new Date().toISOString();
}
function normalizeErrorMessage(error, fallback = "Connection step failed") {
if (error instanceof Error && error.message) {
return error.message;
}
const message = String(error || "").trim();
return message || fallback;
}
function rejectUpgrade(socket, statusCode, errorCode, message, details = {}) {
const statusText = {
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
500: "Internal Server Error",
503: "Service Unavailable",
}[statusCode] || "WebSocket Upgrade Rejected";
const body = JSON.stringify({
ok: false,
error_code: errorCode,
message,
details,
});
socket.end(
[
`HTTP/1.1 ${statusCode} ${statusText}`,
"content-type: application/json; charset=utf-8",
`content-length: ${Buffer.byteLength(body)}`,
"connection: close",
"",
body,
].join("\r\n")
);
}
export function createBrokerServer(options = {}) { export function createBrokerServer(options = {}) {
const sharedSecret = resolveSharedSecret(options); const sharedSecret = options.sharedSecret ?? process.env.EDGE_BROKER_SHARED_SECRET ?? "";
const managerUrl = resolveManagerUrl(options); const managerUrl = resolveManagerUrl(options);
const authMode = resolveAuthMode(options, managerUrl); const authMode = resolveAuthMode(options, managerUrl);
const commandTimeoutMs = options.commandTimeoutMs ?? 10000; const commandTimeoutMs = options.commandTimeoutMs ?? 10000;
const shellOpenTimeoutMs = options.shellOpenTimeoutMs ?? DEFAULT_SHELL_OPEN_TIMEOUT_MS;
const agents = new Map(); const agents = new Map();
const pendingCommands = new Map(); const pendingCommands = new Map();
@@ -205,11 +133,7 @@ export function createBrokerServer(options = {}) {
}); });
const json = await parseJsonResponse(response); const json = await parseJsonResponse(response);
if (!response.ok) { if (!response.ok) {
const error = new Error(json?.data?.message || json?.message || json?.error || `HTTP ${response.status}`); throw new Error(json?.data?.message || json?.message || json?.error || `HTTP ${response.status}`);
error.status = response.status;
error.code = json?.data?.error_code || json?.error_code || null;
error.details = json?.data?.diagnostics || json?.diagnostics || null;
throw error;
} }
return json?.data ?? json; return json?.data ?? json;
@@ -239,12 +163,11 @@ export function createBrokerServer(options = {}) {
options.closeShellSession || options.closeShellSession ||
(authMode === "stub" (authMode === "stub"
? async () => ({}) ? async () => ({})
: async (_id, token, transcript, reason, details = {}) => : async (_id, token, transcript, reason) =>
managerRequest("/edge-agent/internal/shell-sessions/close", { managerRequest("/edge-agent/internal/shell-sessions/close", {
token, token,
transcript, transcript,
reason, reason,
...details,
})); }));
const validateBrowserStream = const validateBrowserStream =
options.validateBrowserStream || options.validateBrowserStream ||
@@ -296,12 +219,6 @@ export function createBrokerServer(options = {}) {
? async (_gatewayId, payload = {}) => payload ? async (_gatewayId, payload = {}) => payload
: async (gatewayId, payload = {}) => : async (gatewayId, payload = {}) =>
managerRequest(`/edge-agent/internal/gateways/${gatewayId}/logs`, payload)); managerRequest(`/edge-agent/internal/gateways/${gatewayId}/logs`, payload));
const ingestMachineSignal =
options.ingestMachineSignal ||
(authMode === "stub"
? async (_gatewayId, payload = {}) => payload
: async (gatewayId, payload = {}) =>
managerRequest(`/edge-agent/internal/gateways/${gatewayId}/selfserve/machine-signal`, payload));
const broadcastGatewayEvent = (gatewayId, message) => { const broadcastGatewayEvent = (gatewayId, message) => {
const sessionIds = gatewayStreamSessions.get(String(gatewayId)); const sessionIds = gatewayStreamSessions.get(String(gatewayId));
@@ -323,66 +240,29 @@ export function createBrokerServer(options = {}) {
} }
}; };
const closeBrowserShellSession = async (sessionRecord, reason, details = {}) => { const closeBrowserShellSession = async (sessionRecord, reason) => {
try { try {
await closeShellSession( await closeShellSession(
sessionRecord.session.id, sessionRecord.session.id,
sessionRecord.ws.sessionToken, sessionRecord.ws.sessionToken,
sessionRecord.transcript, sessionRecord.transcript,
reason, reason
{
message: sessionRecord.closedMessage || null,
code: sessionRecord.closedCode ?? null,
stage: sessionRecord.closedStage || null,
broker_connection_id: sessionRecord.agentConnectionId || null,
details: sessionRecord.closedDetails || {},
...details,
}
); );
} catch { } catch {
// Preserve socket teardown even when the manager callback is unavailable. // Preserve socket teardown even when the manager callback is unavailable.
} }
}; };
const clearShellOpenTimer = (sessionRecord) => {
if (sessionRecord?.openTimer) {
clearTimeout(sessionRecord.openTimer);
sessionRecord.openTimer = null;
}
};
const closeBrowserShellSocket = (sessionRecord, reason, message = null, code = 1000, details = {}) => {
sessionRecord.closedReason = reason;
sessionRecord.closedMessage = message || null;
sessionRecord.closedCode = code;
sessionRecord.closedDetails = details && typeof details === "object" ? details : {};
sessionRecord.closedStage = String(sessionRecord.closedDetails.stage || (sessionRecord.opened ? "shell_active" : "shell_open"));
clearShellOpenTimer(sessionRecord);
if (sessionRecord.ws.readyState >= 2) {
return;
}
sendJson(sessionRecord.ws, {
type: "closed",
reason,
code,
...(message ? { message } : {}),
...(Object.keys(sessionRecord.closedDetails).length ? { details: sessionRecord.closedDetails } : {}),
});
sessionRecord.ws.close(code, reason);
};
const markGatewayShellSessionsClosed = (gatewayId, reason) => { const markGatewayShellSessionsClosed = (gatewayId, reason) => {
for (const sessionRecord of browserShellSessions.values()) { for (const sessionRecord of browserShellSessions.values()) {
if (String(sessionRecord.session.gateway_id) !== String(gatewayId)) { if (String(sessionRecord.session.gateway_id) !== String(gatewayId)) {
continue; continue;
} }
closeBrowserShellSocket(sessionRecord, reason, "Gateway agent disconnected from the broker.", 1011, { sessionRecord.closedReason = reason;
stage: "agent_disconnect", if (sessionRecord.ws.readyState < 2) {
gateway_id: gatewayId, sessionRecord.ws.close();
shell_session_id: sessionRecord.session.id, }
});
} }
}; };
@@ -439,73 +319,12 @@ export function createBrokerServer(options = {}) {
return syncPromise; return syncPromise;
}; };
const sendAgentWelcome = (ws) =>
sendJson(ws, {
type: "WELCOME",
target: "edge-agent",
gatewayId: String(ws.gatewayId || ""),
connectionId: ws.connectionId || null,
agentInstanceId: ws.agentInstanceId || null,
serverTime: currentTimestamp(),
broker: {
authMode,
},
});
const sendAgentConnectionProgress = (ws, stage, status, message, details = {}) =>
sendJson(ws, {
type: "CONNECTION_PROGRESS",
gatewayId: String(ws.gatewayId || ""),
connectionId: ws.connectionId || null,
stage,
status,
message,
...details,
serverTime: currentTimestamp(),
});
const sendAgentConnectionError = (ws, stage, error, details = {}) =>
sendJson(ws, {
type: "CONNECTION_ERROR",
gatewayId: String(ws.gatewayId || ""),
connectionId: ws.connectionId || null,
stage,
status: "failed",
message: normalizeErrorMessage(error),
error: normalizeErrorMessage(error),
...details,
serverTime: currentTimestamp(),
});
const server = http.createServer(async (req, res) => { const server = http.createServer(async (req, res) => {
try { try {
const url = new URL(req.url, "http://localhost"); const url = new URL(req.url, "http://localhost");
if (req.method === "GET" && url.pathname === "/api/health") {
jsonResponse(res, 200, {
ok: true,
service: "edge-broker",
auth_mode: authMode,
manager_url_configured: Boolean(managerUrl),
shared_secret_configured: Boolean(sharedSecret),
agents_connected: agents.size,
});
return;
}
if (req.method === "POST" && url.pathname === "/api/diagnostics/shared-secret") {
if (!requireSharedSecret(req, res, sharedSecret)) {
return;
}
jsonResponse(res, 200, {
ok: true,
shared_secret_required: true,
});
return;
}
if (req.method === "POST" && /^\/api\/gateways\/\d+\/commands$/.test(url.pathname)) { if (req.method === "POST" && /^\/api\/gateways\/\d+\/commands$/.test(url.pathname)) {
if (!requireSharedSecret(req, res, sharedSecret)) { if (sharedSecret && req.headers["x-edge-broker-secret"] !== sharedSecret) {
jsonResponse(res, 403, { error: "Forbidden" });
return; return;
} }
@@ -548,7 +367,8 @@ export function createBrokerServer(options = {}) {
} }
if (req.method === "POST" && /^\/api\/gateways\/\d+\/sync$/.test(url.pathname)) { if (req.method === "POST" && /^\/api\/gateways\/\d+\/sync$/.test(url.pathname)) {
if (!requireSharedSecret(req, res, sharedSecret)) { if (sharedSecret && req.headers["x-edge-broker-secret"] !== sharedSecret) {
jsonResponse(res, 403, { error: "Forbidden" });
return; return;
} }
@@ -577,18 +397,7 @@ export function createBrokerServer(options = {}) {
return; return;
} }
let gatewayInfo; const gatewayInfo = await validateAgent({ gatewayId, token, headers: req.headers });
try {
gatewayInfo = await validateAgent({ gatewayId, token, headers: req.headers });
} catch (error) {
const status = Number(error?.status) === 403 ? 403 : Number(error?.status) === 401 ? 401 : 503;
rejectUpgrade(socket, status, error?.code || "agent_validation_failed", "Gateway agent could not be validated.", {
stage: "agent_validate",
});
return;
}
wss.handleUpgrade(req, socket, head, (ws) => { wss.handleUpgrade(req, socket, head, (ws) => {
const existing = agents.get(gatewayId); const existing = agents.get(gatewayId);
if (existing && existing.readyState < 2) { if (existing && existing.readyState < 2) {
@@ -600,9 +409,6 @@ export function createBrokerServer(options = {}) {
ws.agentInstanceId = agentInstanceId || null; ws.agentInstanceId = agentInstanceId || null;
ws.connectionId = randomUUID(); ws.connectionId = randomUUID();
agents.set(gatewayId, ws); agents.set(gatewayId, ws);
wss.emit("connection", ws, req);
sendAgentWelcome(ws);
sendAgentConnectionProgress(ws, "presence", "started", "Reporting broker presence to the edge manager.");
reportGatewayPresence(gatewayId, { reportGatewayPresence(gatewayId, {
status: "connected", status: "connected",
connectionId: ws.connectionId, connectionId: ws.connectionId,
@@ -610,30 +416,15 @@ export function createBrokerServer(options = {}) {
remote_address: req.socket.remoteAddress || null, remote_address: req.socket.remoteAddress || null,
agent_instance_id: ws.agentInstanceId, agent_instance_id: ws.agentInstanceId,
}, },
}) }).catch(() => {});
.then(() => {
sendAgentConnectionProgress(ws, "presence", "succeeded", "Broker presence was reported.");
})
.catch((error) => {
sendAgentConnectionError(ws, "presence", error);
});
broadcastGatewayEvent(gatewayId, { broadcastGatewayEvent(gatewayId, {
type: "presence.changed", type: "presence.changed",
gatewayId, gatewayId,
status: "connected", status: "connected",
connectionId: ws.connectionId, connectionId: ws.connectionId,
}); });
sendAgentConnectionProgress(ws, "backlog", "started", "Synchronizing queued gateway work."); syncGatewayBacklog(gatewayId, ws).catch(() => {});
syncGatewayBacklog(gatewayId, ws) wss.emit("connection", ws, req);
.then((result) => {
sendAgentConnectionProgress(ws, "backlog", "succeeded", "Queued gateway work was synchronized.", {
queued: Boolean(result?.queued),
dispatchCount: Array.isArray(result?.dispatch) ? result.dispatch.length : 0,
});
})
.catch((error) => {
sendAgentConnectionError(ws, "backlog", error);
});
}); });
return; return;
} }
@@ -641,21 +432,11 @@ export function createBrokerServer(options = {}) {
if (url.pathname === "/ws/browser-shell") { if (url.pathname === "/ws/browser-shell") {
const token = String(url.searchParams.get("token") || ""); const token = String(url.searchParams.get("token") || "");
if (token === "") { if (token === "") {
rejectUpgrade(socket, 400, "shell_session_token_missing", "Missing shell session token."); socket.destroy();
return; return;
} }
let session; const session = await validateShellSession({ token, headers: req.headers });
try {
session = await validateShellSession({ token, headers: req.headers });
} catch (error) {
rejectUpgrade(socket, Number(error?.status) === 403 ? 403 : 401, error?.code || "shell_session_invalid", "Shell session could not be validated.", {
stage: "shell_session_validate",
});
return;
}
wss.handleUpgrade(req, socket, head, (ws) => { wss.handleUpgrade(req, socket, head, (ws) => {
ws.sessionToken = token; ws.sessionToken = token;
ws.sessionInfo = session; ws.sessionInfo = session;
@@ -664,20 +445,11 @@ export function createBrokerServer(options = {}) {
session, session,
transcript: "", transcript: "",
closedReason: null, closedReason: null,
closedMessage: null,
closedCode: null,
closedDetails: {},
closedStage: null,
opened: false,
openTimer: null,
agentConnectionId: null,
}; };
browserShellSessions.set(String(session.id), sessionRecord); browserShellSessions.set(String(session.id), sessionRecord);
wss.emit("connection", ws, req);
const agent = agents.get(String(session.gateway_id)); const agent = agents.get(String(session.gateway_id));
if (agent && agent.readyState === 1) { if (agent && agent.readyState === 1) {
sessionRecord.agentConnectionId = agent.connectionId || null;
sendJson(agent, { sendJson(agent, {
type: "OPEN_ROOT_SHELL", type: "OPEN_ROOT_SHELL",
payload: { payload: {
@@ -690,34 +462,11 @@ export function createBrokerServer(options = {}) {
shellArgs: session.shell_args ?? session.metadata?.shell_args ?? [], shellArgs: session.shell_args ?? session.metadata?.shell_args ?? [],
}, },
}); });
sessionRecord.openTimer = setTimeout(() => {
closeBrowserShellSocket(
sessionRecord,
"shell_open_timeout",
"Gateway agent did not confirm that the shell opened before the broker timeout.",
1011,
{
stage: "shell_open",
timeout_ms: shellOpenTimeoutMs,
gateway_id: session.gateway_id,
shell_session_id: session.id,
}
);
}, Math.max(250, shellOpenTimeoutMs));
sessionRecord.openTimer.unref?.();
} else { } else {
closeBrowserShellSocket( sessionRecord.closedReason = "agent_offline";
sessionRecord, ws.close();
"agent_offline",
"Gateway agent is not connected to the broker.",
1011,
{
stage: "agent_lookup",
gateway_id: session.gateway_id,
shell_session_id: session.id,
}
);
} }
wss.emit("connection", ws, req);
}); });
return; return;
} }
@@ -749,8 +498,8 @@ export function createBrokerServer(options = {}) {
}); });
return; return;
} }
} catch (error) { } catch {
rejectUpgrade(socket, 500, "websocket_upgrade_failed", "WebSocket upgrade failed."); socket.destroy();
return; return;
} }
@@ -766,226 +515,183 @@ export function createBrokerServer(options = {}) {
return; return;
} }
try { if (ws.gatewayId) {
if (ws.gatewayId) { if (message.type === "COMMAND_RESULT") {
if (message.type === "COMMAND_RESULT") { const pending = pendingCommands.get(message.commandId);
const pending = pendingCommands.get(message.commandId); if (!pending) {
if (!pending) {
return;
}
clearTimeout(pending.timeout);
pendingCommands.delete(message.commandId);
pending.resolve({
ok: Boolean(message.ok),
payload: message.payload,
error: message.error,
});
return; return;
} }
clearTimeout(pending.timeout);
if (message.type === "TELEMETRY") { pendingCommands.delete(message.commandId);
const payload = { pending.resolve({
...(message.payload || {}), ok: Boolean(message.ok),
broker_connection_id: ws.connectionId || null, payload: message.payload,
broker_agent_instance_id: ws.agentInstanceId || null, error: message.error,
}; });
let ingested = null;
let ingestError = null;
try {
ingested = await ingestTelemetry(String(ws.gatewayId), payload);
} catch {
ingestError = TELEMETRY_INGEST_ERROR_MESSAGE;
}
const fallbackStatistics = {
system_metrics: payload?.metadata?.system_metrics || {},
container_health: payload?.metadata?.container_health || {},
};
broadcastGatewayEvent(String(ws.gatewayId), {
type: "gateway.telemetry",
gatewayId: String(ws.gatewayId),
telemetry: payload,
gateway: ingested?.gateway || ingested || null,
error: ingestError,
});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "stats.updated",
gatewayId: String(ws.gatewayId),
statistics: ingested?.statistics || ingested || fallbackStatistics,
error: ingestError,
});
return;
}
if (message.type === "TASK_EVENT") {
const operationId = Number(message.operationId ?? message.payload?.operation_id ?? 0);
if (!Number.isFinite(operationId) || operationId <= 0) {
return;
}
const operation = await ingestTaskEvent(String(ws.gatewayId), operationId, message.payload || {});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "task.updated",
gatewayId: String(ws.gatewayId),
operationId,
operation,
});
return;
}
if (message.type === "TASK_RESULT") {
const operationId = Number(message.operationId ?? message.payload?.operation_id ?? 0);
if (!Number.isFinite(operationId) || operationId <= 0) {
return;
}
const operation = await ingestTaskResult(String(ws.gatewayId), operationId, message.payload || {});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "task.updated",
gatewayId: String(ws.gatewayId),
operationId,
operation,
});
await syncGatewayBacklog(String(ws.gatewayId), ws).catch(() => {});
return;
}
if (message.type === "LOG_FRAME") {
const logEntry = await ingestLogEntry(String(ws.gatewayId), message.payload || {});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "log.append",
gatewayId: String(ws.gatewayId),
entry: logEntry,
});
return;
}
if (message.type === "MACHINE_SIGNAL") {
await ingestMachineSignal(String(ws.gatewayId), message.payload || {});
return;
}
if (["SHELL_OUTPUT", "SHELL_OPENED", "SHELL_EXIT"].includes(message.type)) {
const sessionRecord = browserShellSessions.get(String(message.sessionId));
if (!sessionRecord) {
return;
}
if (message.type === "SHELL_OUTPUT") {
sessionRecord.transcript += String(message.data || "");
sendJson(sessionRecord.ws, { type: "output", data: String(message.data || "") });
return;
}
if (message.type === "SHELL_OPENED") {
sessionRecord.opened = true;
sessionRecord.agentConnectionId = ws.connectionId || sessionRecord.agentConnectionId || null;
clearShellOpenTimer(sessionRecord);
await markShellSessionOpened(sessionRecord.ws.sessionToken, ws.connectionId || null).catch(() => {});
sendJson(sessionRecord.ws, { type: "opened" });
return;
}
if (message.type === "SHELL_EXIT") {
const reason = String(message.reason || "agent_exit");
const parsedExitCode = Number(message.code);
const exitCode = Number.isFinite(parsedExitCode) ? parsedExitCode : 0;
const closeMessage = message.message ? String(message.message) : null;
clearShellOpenTimer(sessionRecord);
sendJson(sessionRecord.ws, {
type: "closed",
reason,
code: exitCode,
...(closeMessage ? { message: closeMessage } : {}),
});
await closeBrowserShellSession(sessionRecord, reason, {
message: closeMessage,
code: exitCode,
stage: sessionRecord.opened ? "shell_active" : "shell_start",
broker_connection_id: ws.connectionId || null,
});
browserShellSessions.delete(String(message.sessionId));
if (sessionRecord.ws.readyState < 2) {
sessionRecord.ws.close(1000, reason);
}
}
}
return; return;
} }
if (ws.sessionInfo) { if (message.type === "TELEMETRY") {
const sessionId = String(ws.sessionInfo.id); const payload = message.payload || {};
const agent = agents.get(String(ws.sessionInfo.gateway_id)); const ingested = await ingestTelemetry(String(ws.gatewayId), payload);
if (!agent || agent.readyState !== 1) { broadcastGatewayEvent(String(ws.gatewayId), {
return; type: "gateway.telemetry",
} gatewayId: String(ws.gatewayId),
if (message.type === "input") { telemetry: payload,
sendJson(agent, { gateway: ingested?.gateway || ingested || null,
type: "SHELL_INPUT", });
payload: { broadcastGatewayEvent(String(ws.gatewayId), {
sessionId, type: "stats.updated",
data: String(message.data || ""), gatewayId: String(ws.gatewayId),
}, statistics: ingested?.statistics || ingested || null,
}); });
return;
}
if (message.type === "resize") {
sendJson(agent, {
type: "RESIZE_ROOT_SHELL",
payload: {
sessionId,
cols: Number(message.cols || 0),
rows: Number(message.rows || 0),
},
});
return;
}
if (message.type === "close") {
sendJson(agent, {
type: "CLOSE_ROOT_SHELL",
payload: { sessionId },
});
}
return; return;
} }
if (ws.streamSessionInfo) { if (message.type === "TASK_EVENT") {
const sessionRecord = browserStreamSessions.get(String(ws.streamSessionInfo.id)); const operationId = Number(message.operationId ?? message.payload?.operation_id ?? 0);
if (!Number.isFinite(operationId) || operationId <= 0) {
return;
}
const operation = await ingestTaskEvent(String(ws.gatewayId), operationId, message.payload || {});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "task.updated",
gatewayId: String(ws.gatewayId),
operationId,
operation,
});
return;
}
if (message.type === "TASK_RESULT") {
const operationId = Number(message.operationId ?? message.payload?.operation_id ?? 0);
if (!Number.isFinite(operationId) || operationId <= 0) {
return;
}
const operation = await ingestTaskResult(String(ws.gatewayId), operationId, message.payload || {});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "task.updated",
gatewayId: String(ws.gatewayId),
operationId,
operation,
});
await syncGatewayBacklog(String(ws.gatewayId), ws).catch(() => {});
return;
}
if (message.type === "LOG_FRAME") {
const logEntry = await ingestLogEntry(String(ws.gatewayId), message.payload || {});
broadcastGatewayEvent(String(ws.gatewayId), {
type: "log.append",
gatewayId: String(ws.gatewayId),
entry: logEntry,
});
return;
}
if (["SHELL_OUTPUT", "SHELL_OPENED", "SHELL_EXIT"].includes(message.type)) {
const sessionRecord = browserShellSessions.get(String(message.sessionId));
if (!sessionRecord) { if (!sessionRecord) {
return; return;
} }
if (message.type === "SUBSCRIBE") { if (message.type === "SHELL_OUTPUT") {
for (const scope of parseScopes(message.scopes || message.subscriptions || [])) { sessionRecord.transcript += String(message.data || "");
sessionRecord.subscriptions.add(scope); sendJson(sessionRecord.ws, { type: "output", data: String(message.data || "") });
}
sendJson(sessionRecord.ws, {
type: "subscribed",
subscriptions: Array.from(sessionRecord.subscriptions.values()),
});
return; return;
} }
if (message.type === "UNSUBSCRIBE") { if (message.type === "SHELL_OPENED") {
for (const scope of parseScopes(message.scopes || message.subscriptions || [])) { await markShellSessionOpened(sessionRecord.ws.sessionToken, ws.connectionId || null).catch(() => {});
sessionRecord.subscriptions.delete(scope); sendJson(sessionRecord.ws, { type: "opened" });
}
sendJson(sessionRecord.ws, {
type: "unsubscribed",
subscriptions: Array.from(sessionRecord.subscriptions.values()),
});
return; return;
} }
if (message.type === "PING") { if (message.type === "SHELL_EXIT") {
sendJson(sessionRecord.ws, { type: "PONG" }); sendJson(sessionRecord.ws, { type: "closed", code: message.code ?? 0 });
await closeBrowserShellSession(sessionRecord, "agent_exit");
browserShellSessions.delete(String(message.sessionId));
if (sessionRecord.ws.readyState < 2) {
sessionRecord.ws.close();
}
} }
} }
} catch { return;
// Ignore stale gateway/session delivery errors without killing the broker process. }
if (ws.sessionInfo) {
const sessionId = String(ws.sessionInfo.id);
const agent = agents.get(String(ws.sessionInfo.gateway_id));
if (!agent || agent.readyState !== 1) {
return;
}
if (message.type === "input") {
sendJson(agent, {
type: "SHELL_INPUT",
payload: {
sessionId,
data: String(message.data || ""),
},
});
return;
}
if (message.type === "resize") {
sendJson(agent, {
type: "RESIZE_ROOT_SHELL",
payload: {
sessionId,
cols: Number(message.cols || 0),
rows: Number(message.rows || 0),
},
});
return;
}
if (message.type === "close") {
sendJson(agent, {
type: "CLOSE_ROOT_SHELL",
payload: { sessionId },
});
}
return;
}
if (ws.streamSessionInfo) {
const sessionRecord = browserStreamSessions.get(String(ws.streamSessionInfo.id));
if (!sessionRecord) {
return;
}
if (message.type === "SUBSCRIBE") {
for (const scope of parseScopes(message.scopes || message.subscriptions || [])) {
sessionRecord.subscriptions.add(scope);
}
sendJson(sessionRecord.ws, {
type: "subscribed",
subscriptions: Array.from(sessionRecord.subscriptions.values()),
});
return;
}
if (message.type === "UNSUBSCRIBE") {
for (const scope of parseScopes(message.scopes || message.subscriptions || [])) {
sessionRecord.subscriptions.delete(scope);
}
sendJson(sessionRecord.ws, {
type: "unsubscribed",
subscriptions: Array.from(sessionRecord.subscriptions.values()),
});
return;
}
if (message.type === "PING") {
sendJson(sessionRecord.ws, { type: "PONG" });
}
} }
}); });
ws.on("close", async (code, buffer) => { ws.on("close", async (_code, buffer) => {
const closeReason = buffer?.toString?.("utf8") || null; const closeReason = buffer?.toString?.("utf8") || null;
if (ws.gatewayId) { if (ws.gatewayId) {
@@ -1021,11 +727,7 @@ export function createBrokerServer(options = {}) {
} }
const sessionRecord = browserShellSessions.get(sessionId); const sessionRecord = browserShellSessions.get(sessionId);
if (sessionRecord) { if (sessionRecord) {
await closeBrowserShellSession(sessionRecord, sessionRecord.closedReason || "browser_closed", { await closeBrowserShellSession(sessionRecord, sessionRecord.closedReason || "browser_closed");
code,
stage: sessionRecord.closedStage || "browser_socket_close",
message: sessionRecord.closedMessage || null,
});
browserShellSessions.delete(sessionId); browserShellSessions.delete(sessionId);
} }
return; return;
@@ -1086,43 +788,3 @@ export function createBrokerServer(options = {}) {
}, },
}; };
} }
async function runBrokerFromCli() {
const broker = createBrokerServer();
let shuttingDown = false;
const shutdown = async (signal) => {
if (shuttingDown) {
return;
}
shuttingDown = true;
try {
await broker.close();
process.exit(0);
} catch (error) {
console.error(`Failed to shut down broker after ${signal}:`, error);
process.exit(1);
}
};
process.on("SIGINT", () => {
void shutdown("SIGINT");
});
process.on("SIGTERM", () => {
void shutdown("SIGTERM");
});
const requestedPort = Number(process.env.PORT || 4300);
const address = await broker.listen(Number.isFinite(requestedPort) ? requestedPort : 4300);
const normalizedPort =
typeof address === "object" && address !== null && "port" in address
? address.port
: requestedPort;
console.log(`TruckWash edge broker listening on ${normalizedPort}`);
}
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
runBrokerFromCli().catch((error) => {
console.error("TruckWash edge broker failed to start:", error);
process.exit(1);
});
}
+10 -563
View File
@@ -1,10 +1,15 @@
import test from "node:test"; import test from "node:test";
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import net from "node:net";
import WebSocket from "ws"; import WebSocket from "ws";
import { createBrokerServer } from "../server.mjs"; import { createBrokerServer } from "../server.mjs";
function waitForMessage(socket) {
return new Promise((resolve) => {
socket.once("message", (raw) => resolve(JSON.parse(raw.toString())));
});
}
function collectMessages(socket) { function collectMessages(socket) {
const messages = []; const messages = [];
socket.on("message", (raw) => { socket.on("message", (raw) => {
@@ -19,44 +24,6 @@ function waitForClose(socket) {
}); });
} }
function waitForCloseOrError(socket) {
return new Promise((resolve) => {
const onDone = () => {
socket.off("error", onDone);
socket.off("close", onDone);
resolve();
};
socket.once("error", onDone);
socket.once("close", onDone);
});
}
function rawUpgradeRequest(port, path) {
return new Promise((resolve, reject) => {
const socket = net.createConnection({ host: "127.0.0.1", port }, () => {
socket.write(
[
`GET ${path} HTTP/1.1`,
`Host: 127.0.0.1:${port}`,
"Connection: Upgrade",
"Upgrade: websocket",
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==",
"Sec-WebSocket-Version: 13",
"",
"",
].join("\r\n")
);
});
let response = "";
socket.setEncoding("utf8");
socket.on("data", (chunk) => {
response += chunk;
});
socket.on("end", () => resolve(response));
socket.on("error", reject);
});
}
async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, description = "condition" } = {}) { async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, description = "condition" } = {}) {
const deadline = Date.now() + timeoutMs; const deadline = Date.now() + timeoutMs;
@@ -71,100 +38,6 @@ async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, descripti
throw new Error(`Timed out waiting for ${description}`); throw new Error(`Timed out waiting for ${description}`);
} }
test("broker defaults to strict auth and fails closed when manager URL is missing", async () => {
const previousEnv = {
EDGE_AUTH_MODE: process.env.EDGE_AUTH_MODE,
EDGE_MANAGER_URL: process.env.EDGE_MANAGER_URL,
EDGE_PUBLIC_API_URL: process.env.EDGE_PUBLIC_API_URL,
};
delete process.env.EDGE_AUTH_MODE;
delete process.env.EDGE_MANAGER_URL;
delete process.env.EDGE_PUBLIC_API_URL;
let broker;
try {
broker = createBrokerServer({ sharedSecret: "secret" });
assert.equal(broker.state.authMode, "strict");
assert.equal(broker.state.managerUrl, "");
const address = await broker.listen(0);
const port = address.port;
const shellResponse = await rawUpgradeRequest(port, "/ws/browser-shell?token=session-token");
const agentResponse = await rawUpgradeRequest(port, "/ws/agent?gatewayId=701&token=agent-token");
assert.doesNotMatch(shellResponse, /101 Switching Protocols/);
assert.match(shellResponse, /^HTTP\/1\.1 401 Unauthorized/m);
assert.match(shellResponse, /"error_code":"shell_session_invalid"/);
assert.match(shellResponse, /"message":"Shell session could not be validated\."/);
assert.doesNotMatch(shellResponse, /Edge manager URL is not configured/);
assert.doesNotMatch(agentResponse, /101 Switching Protocols/);
assert.match(agentResponse, /^HTTP\/1\.1 503 Service Unavailable/m);
assert.match(agentResponse, /"error_code":"agent_validation_failed"/);
assert.match(agentResponse, /"stage":"agent_validate"/);
assert.match(agentResponse, /"message":"Gateway agent could not be validated\."/);
assert.doesNotMatch(agentResponse, /Edge manager URL is not configured/);
} finally {
if (broker) {
await broker.close();
}
for (const [key, value] of Object.entries(previousEnv)) {
if (value === undefined) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
}
});
test("broker rejects protected HTTP endpoints when shared secret is missing", async () => {
const broker = createBrokerServer({ authMode: "stub", sharedSecret: "", commandTimeoutMs: 2000 });
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
await new Promise((resolve) => agent.once("open", resolve));
const agentMessages = collectMessages(agent);
const commandResponse = await fetch(`http://127.0.0.1:${port}/api/gateways/701/commands`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
commandType: "SET_RELAY_STATE",
payload: { relayId: "M-7", on: true },
}),
});
const commandJson = await commandResponse.json();
assert.equal(commandResponse.status, 503);
assert.equal(commandJson.ok, false);
assert.equal(commandJson.shared_secret_required, true);
assert.match(commandJson.error, /shared secret is not configured/);
assert.equal(agentMessages.some((message) => message.type === "COMMAND"), false);
const diagnosticsResponse = await fetch(`http://127.0.0.1:${port}/api/diagnostics/shared-secret`, {
method: "POST",
});
const diagnosticsJson = await diagnosticsResponse.json();
assert.equal(diagnosticsResponse.status, 503);
assert.equal(diagnosticsJson.shared_secret_required, true);
const syncResponse = await fetch(`http://127.0.0.1:${port}/api/gateways/701/sync`, {
method: "POST",
});
const syncJson = await syncResponse.json();
assert.equal(syncResponse.status, 503);
assert.equal(syncJson.shared_secret_required, true);
agent.terminate();
await broker.close();
});
test("broker dispatches commands to connected agents", async () => { test("broker dispatches commands to connected agents", async () => {
const broker = createBrokerServer({ authMode: "stub", sharedSecret: "secret", commandTimeoutMs: 2000 }); const broker = createBrokerServer({ authMode: "stub", sharedSecret: "secret", commandTimeoutMs: 2000 });
const address = await broker.listen(0); const address = await broker.listen(0);
@@ -205,48 +78,6 @@ test("broker dispatches commands to connected agents", async () => {
await broker.close(); await broker.close();
}); });
test("broker exposes health and shared-secret diagnostics", async () => {
const broker = createBrokerServer({ authMode: "manager", sharedSecret: "secret", managerUrl: "http://manager.test" });
const address = await broker.listen(0);
const port = address.port;
const healthResponse = await fetch(`http://127.0.0.1:${port}/api/health`);
const healthJson = await healthResponse.json();
assert.equal(healthResponse.status, 200);
assert.equal(healthJson.ok, true);
assert.equal(healthJson.service, "edge-broker");
assert.equal(healthJson.auth_mode, "manager");
assert.equal(healthJson.manager_url_configured, true);
assert.equal(healthJson.shared_secret_configured, true);
const invalidSecretResponse = await fetch(`http://127.0.0.1:${port}/api/diagnostics/shared-secret`, {
method: "POST",
headers: {
"x-edge-broker-secret": "wrong-secret",
},
});
const invalidSecretJson = await invalidSecretResponse.json();
assert.equal(invalidSecretResponse.status, 403);
assert.equal(invalidSecretJson.ok, false);
assert.equal(invalidSecretJson.shared_secret_required, true);
const validSecretResponse = await fetch(`http://127.0.0.1:${port}/api/diagnostics/shared-secret`, {
method: "POST",
headers: {
"x-edge-broker-secret": "secret",
},
});
const validSecretJson = await validSecretResponse.json();
assert.equal(validSecretResponse.status, 200);
assert.equal(validSecretJson.ok, true);
assert.equal(validSecretJson.shared_secret_required, true);
await broker.close();
});
test("broker bridges browser shell sessions through the connected agent", async () => { test("broker bridges browser shell sessions through the connected agent", async () => {
const closedSessions = []; const closedSessions = [];
const broker = createBrokerServer({ const broker = createBrokerServer({
@@ -278,7 +109,7 @@ test("broker bridges browser shell sessions through the connected agent", async
assert.ok(browserMessages.some((message) => message.type === "opened")); assert.ok(browserMessages.some((message) => message.type === "opened"));
assert.ok(browserMessages.some((message) => message.type === "output" && /root@pi/.test(message.data))); assert.ok(browserMessages.some((message) => message.type === "output" && /root@pi/.test(message.data)));
assert.ok(browserMessages.some((message) => message.type === "closed" && message.reason === "agent_exit" && message.code === 0)); assert.ok(browserMessages.some((message) => message.type === "closed" && message.code === 0));
assert.equal(closedSessions.length, 1); assert.equal(closedSessions.length, 1);
assert.equal(closedSessions[0].reason, "agent_exit"); assert.equal(closedSessions[0].reason, "agent_exit");
@@ -301,10 +132,7 @@ test("broker forwards browser shell input, resize, and close events to the agent
const agentMessages = collectMessages(agent); const agentMessages = collectMessages(agent);
const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`); const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`);
await new Promise((resolve) => browser.once("open", resolve)); await new Promise((resolve) => browser.once("open", resolve));
await waitFor( await waitForMessage(agent);
() => agentMessages.some((message) => message.type === "OPEN_ROOT_SHELL" && message.payload.sessionId === "shell-2"),
{ description: "agent shell open request" }
);
browser.send(JSON.stringify({ type: "input", data: "ls\r" })); browser.send(JSON.stringify({ type: "input", data: "ls\r" }));
browser.send(JSON.stringify({ type: "resize", cols: 140, rows: 44 })); browser.send(JSON.stringify({ type: "resize", cols: 140, rows: 44 }));
@@ -349,124 +177,15 @@ test("broker closes browser shell sessions immediately when no agent is connecte
const port = address.port; const port = address.port;
const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`); const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`);
const browserMessages = collectMessages(browser);
await new Promise((resolve) => browser.once("open", resolve)); await new Promise((resolve) => browser.once("open", resolve));
await waitForClose(browser); await waitForClose(browser);
await waitFor(() => closedSessions.length === 1, { description: "offline shell session close callback" }); await waitFor(() => closedSessions.length === 1, { description: "offline shell session close callback" });
assert.ok(
browserMessages.some(
(message) =>
message.type === "closed" &&
message.reason === "agent_offline" &&
/not connected to the broker/i.test(message.message)
)
);
assert.deepEqual(closedSessions, [{ transcript: "", reason: "agent_offline" }]); assert.deepEqual(closedSessions, [{ transcript: "", reason: "agent_offline" }]);
await broker.close(); await broker.close();
}); });
test("broker rejects browser shell upgrades without a token using HTTP diagnostics", async () => {
const broker = createBrokerServer({ authMode: "stub" });
const address = await broker.listen(0);
const port = address.port;
const response = await rawUpgradeRequest(port, "/ws/browser-shell");
assert.match(response, /^HTTP\/1\.1 400 Bad Request/m);
assert.match(response, /"error_code":"shell_session_token_missing"/);
assert.match(response, /"message":"Missing shell session token\."/);
await broker.close();
});
test("broker rejects invalid browser shell upgrades without leaking the token", async () => {
const broker = createBrokerServer({
authMode: "stub",
validateShellSession: async () => {
const error = new Error("Shell session expired");
error.status = 401;
error.code = "shell_session_expired";
throw error;
},
});
const address = await broker.listen(0);
const port = address.port;
const rawToken = "session-token-secret";
const response = await rawUpgradeRequest(port, `/ws/browser-shell?token=${rawToken}`);
assert.match(response, /^HTTP\/1\.1 401 Unauthorized/m);
assert.match(response, /"error_code":"shell_session_expired"/);
assert.match(response, /"message":"Shell session could not be validated\."/);
assert.doesNotMatch(response, /Shell session expired/);
assert.doesNotMatch(response, new RegExp(rawToken));
await broker.close();
});
test("broker rejects websocket upgrade errors without exposing exception text", async () => {
const broker = createBrokerServer({
authMode: "stub",
validateBrowserStream: async () => {
throw new Error("UPSTREAM-SENSITIVE: redis://cache.internal:6379 timeout");
},
});
const address = await broker.listen(0);
const port = address.port;
const response = await rawUpgradeRequest(port, "/ws/browser-gateway-stream?token=session-token");
assert.match(response, /^HTTP\/1\.1 500 Internal Server Error/m);
assert.match(response, /"error_code":"websocket_upgrade_failed"/);
assert.match(response, /"message":"WebSocket upgrade failed\."/);
assert.doesNotMatch(response, /UPSTREAM-SENSITIVE/);
assert.doesNotMatch(response, /redis:\/\/cache\.internal/);
await broker.close();
});
test("broker closes browser shell sessions when the agent never reports shell opened", async () => {
const closedSessions = [];
const broker = createBrokerServer({
authMode: "stub",
shellOpenTimeoutMs: 30,
validateShellSession: async () => ({ id: "shell-timeout", gateway_id: "701", reason: "diagnostic" }),
closeShellSession: async (_id, _token, transcript, reason, details) => {
closedSessions.push({ transcript, reason, details });
},
});
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
const agentMessages = collectMessages(agent);
await new Promise((resolve) => agent.once("open", resolve));
const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`);
const browserMessages = collectMessages(browser);
await new Promise((resolve) => browser.once("open", resolve));
await waitFor(
() =>
agentMessages.some(
(message) => message.type === "OPEN_ROOT_SHELL" && message.payload.sessionId === "shell-timeout"
),
{ description: "agent shell open request before timeout" }
);
await waitForClose(browser);
await waitFor(() => closedSessions.length === 1, { description: "timeout shell session close callback" });
assert.ok(browserMessages.some((message) => message.type === "closed" && message.reason === "shell_open_timeout"));
assert.equal(closedSessions[0].reason, "shell_open_timeout");
assert.equal(closedSessions[0].details.stage, "shell_open");
assert.equal(closedSessions[0].details.code, 1011);
agent.terminate();
await broker.close();
});
test("broker closes browser shell sessions when the agent disconnects before shell open", async () => { test("broker closes browser shell sessions when the agent disconnects before shell open", async () => {
const closedSessions = []; const closedSessions = [];
const broker = createBrokerServer({ const broker = createBrokerServer({
@@ -484,148 +203,19 @@ test("broker closes browser shell sessions when the agent disconnects before she
const agentMessages = collectMessages(agent); const agentMessages = collectMessages(agent);
const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`); const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-shell?token=session-token`);
const browserMessages = collectMessages(browser);
await new Promise((resolve) => browser.once("open", resolve)); await new Promise((resolve) => browser.once("open", resolve));
await waitFor( await waitForMessage(agent);
() => agentMessages.some((message) => message.type === "OPEN_ROOT_SHELL" && message.payload.sessionId === "shell-4"),
{ description: "agent shell open request before disconnect" }
);
agent.terminate(); agent.terminate();
await waitForClose(browser); await waitForClose(browser);
await waitFor(() => closedSessions.length === 1, { description: "disconnect shell session close callback" }); await waitFor(() => closedSessions.length === 1, { description: "disconnect shell session close callback" });
assert.ok(agentMessages.some((message) => message.type === "OPEN_ROOT_SHELL" && message.payload.sessionId === "shell-4")); assert.ok(agentMessages.some((message) => message.type === "OPEN_ROOT_SHELL" && message.payload.sessionId === "shell-4"));
assert.ok(browserMessages.some((message) => message.type === "closed" && message.reason === "agent_disconnected"));
assert.deepEqual(closedSessions, [{ transcript: "", reason: "agent_disconnected" }]); assert.deepEqual(closedSessions, [{ transcript: "", reason: "agent_disconnected" }]);
await broker.close(); await broker.close();
}); });
test("broker defaults to strict auth when no validators are configured", async () => {
const broker = createBrokerServer();
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
await waitForCloseOrError(agent);
await broker.close();
});
test("broker sends an agent welcome before connection progress and backlog dispatch", async () => {
const broker = createBrokerServer({
authMode: "stub",
reportGatewayPresence: async () => ({}),
requestGatewayBacklog: async () => ({
dispatch: [
{
type: "TASK_DISPATCH",
taskType: "OPERATION",
operation: {
id: 91,
type: "DISCOVERY",
request: {},
},
},
],
}),
});
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
const messages = collectMessages(agent);
await new Promise((resolve) => agent.once("open", resolve));
await waitFor(
() => messages.some((message) => message.type === "TASK_DISPATCH" && message.operation?.id === 91),
{ description: "backlog dispatch after connection welcome" }
);
assert.equal(messages[0].type, "WELCOME");
assert.equal(messages[0].gatewayId, "701");
assert.equal(typeof messages[0].connectionId, "string");
assert.ok(messages[0].connectionId.length > 0);
const firstProgressIndex = messages.findIndex((message) => message.type === "CONNECTION_PROGRESS");
const dispatchIndex = messages.findIndex((message) => message.type === "TASK_DISPATCH");
assert.ok(firstProgressIndex > 0);
assert.ok(dispatchIndex > firstProgressIndex);
assert.ok(
messages.some(
(message) =>
message.type === "CONNECTION_PROGRESS" &&
message.stage === "presence" &&
message.status === "started"
)
);
assert.ok(
messages.some(
(message) =>
message.type === "CONNECTION_PROGRESS" &&
message.stage === "backlog" &&
message.status === "started"
)
);
assert.ok(
messages.some(
(message) =>
message.type === "CONNECTION_PROGRESS" &&
message.stage === "backlog" &&
message.status === "succeeded" &&
message.dispatchCount === 1
)
);
agent.terminate();
await broker.close();
});
test("broker sends connection errors to agents after the welcome message", async () => {
const broker = createBrokerServer({
authMode: "stub",
reportGatewayPresence: async () => {
throw new Error("presence callback unavailable");
},
requestGatewayBacklog: async () => {
throw new Error("backlog sync unavailable");
},
});
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
const messages = collectMessages(agent);
await new Promise((resolve) => agent.once("open", resolve));
await waitFor(
() => messages.filter((message) => message.type === "CONNECTION_ERROR").length >= 2,
{ description: "agent connection error notifications" }
);
assert.equal(messages[0].type, "WELCOME");
assert.ok(
messages.some(
(message) =>
message.type === "CONNECTION_ERROR" &&
message.stage === "presence" &&
message.error === "presence callback unavailable"
)
);
assert.ok(
messages.some(
(message) =>
message.type === "CONNECTION_ERROR" &&
message.stage === "backlog" &&
message.error === "backlog sync unavailable"
)
);
agent.terminate();
await broker.close();
});
test("broker syncs queued gateway backlog on agent connect and manual sync", async () => { test("broker syncs queued gateway backlog on agent connect and manual sync", async () => {
const backlogRequests = []; const backlogRequests = [];
const broker = createBrokerServer({ const broker = createBrokerServer({
@@ -684,7 +274,6 @@ test("broker syncs queued gateway backlog on agent connect and manual sync", asy
}); });
test("broker fans out telemetry, task, log, and presence updates to browser gateway streams", async () => { test("broker fans out telemetry, task, log, and presence updates to browser gateway streams", async () => {
const telemetryPayloads = [];
const broker = createBrokerServer({ const broker = createBrokerServer({
authMode: "stub", authMode: "stub",
validateAgent: async () => ({ id: "701", gateway_id: "701", label: "CPH Edge 01" }), validateAgent: async () => ({ id: "701", gateway_id: "701", label: "CPH Edge 01" }),
@@ -693,10 +282,7 @@ test("broker fans out telemetry, task, log, and presence updates to browser gate
gateway_id: "701", gateway_id: "701",
scopes: ["overview", "tasks", "logs", "statistics"], scopes: ["overview", "tasks", "logs", "statistics"],
}), }),
ingestTelemetry: async (_gatewayId, payload) => { ingestTelemetry: async (_gatewayId, payload) => ({ gateway: { id: 701, metadata: payload.metadata || {} } }),
telemetryPayloads.push(payload);
return { gateway: { id: 701, metadata: payload.metadata || {} } };
},
ingestTaskEvent: async (_gatewayId, operationId, payload) => ({ ingestTaskEvent: async (_gatewayId, operationId, payload) => ({
id: operationId, id: operationId,
status: "IN_PROGRESS", status: "IN_PROGRESS",
@@ -759,145 +345,6 @@ test("broker fans out telemetry, task, log, and presence updates to browser gate
assert.ok(browserMessages.some((message) => message.type === "stats.updated")); assert.ok(browserMessages.some((message) => message.type === "stats.updated"));
assert.ok(browserMessages.some((message) => message.type === "task.updated" && message.operationId === 41)); assert.ok(browserMessages.some((message) => message.type === "task.updated" && message.operationId === 41));
assert.ok(browserMessages.some((message) => message.type === "log.append" && /heartbeat/.test(message.entry?.message))); assert.ok(browserMessages.some((message) => message.type === "log.append" && /heartbeat/.test(message.entry?.message)));
assert.equal(telemetryPayloads[0]?.broker_connection_id?.length > 0, true);
browser.terminate();
agent.terminate();
await broker.close();
});
test("broker ingests self-serve machine signals from connected agents", async () => {
const machineSignals = [];
const broker = createBrokerServer({
authMode: "stub",
validateAgent: async () => ({ id: "701", gateway_id: "701", label: "CPH Edge 01" }),
ingestMachineSignal: async (gatewayId, payload) => {
machineSignals.push({ gatewayId, payload });
return { recorded: true, lane_id: payload.lane_id };
},
});
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
await new Promise((resolve) => agent.once("open", resolve));
agent.send(
JSON.stringify({
type: "MACHINE_SIGNAL",
payload: {
lane_id: 3,
relay_id: "machine-relay",
component: "input",
channel: 0,
event: "input.toggle_on",
state: true,
},
})
);
await waitFor(() => machineSignals.length === 1, { description: "machine signal ingestion" });
assert.equal(machineSignals[0].gatewayId, "701");
assert.deepEqual(machineSignals[0].payload, {
lane_id: 3,
relay_id: "machine-relay",
component: "input",
channel: 0,
event: "input.toggle_on",
state: true,
});
agent.terminate();
await broker.close();
});
test("broker survives telemetry ingestion failures for stale gateways", async () => {
const broker = createBrokerServer({
authMode: "stub",
validateAgent: async () => ({ id: "701", gateway_id: "701", label: "CPH Edge 01" }),
ingestTelemetry: async () => {
throw new Error("Edge gateway not found");
},
});
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
await new Promise((resolve) => agent.once("open", resolve));
agent.send(
JSON.stringify({
type: "TELEMETRY",
payload: {
status: "ONLINE",
},
})
);
await new Promise((resolve) => setTimeout(resolve, 100));
assert.equal(broker.server.listening, true);
agent.terminate();
await broker.close();
});
test("broker still fans out telemetry when manager ingestion fails", async () => {
const broker = createBrokerServer({
authMode: "stub",
validateAgent: async () => ({ id: "701", gateway_id: "701", label: "CPH Edge 01" }),
validateBrowserStream: async () => ({
id: "stream-telemetry-fallback",
gateway_id: "701",
scopes: ["overview", "statistics"],
}),
ingestTelemetry: async () => {
throw new Error("manager unavailable");
},
});
const address = await broker.listen(0);
const port = address.port;
const browser = new WebSocket(`ws://127.0.0.1:${port}/ws/browser-gateway-stream?token=stream-token`);
const browserMessages = collectMessages(browser);
await new Promise((resolve) => browser.once("open", resolve));
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
await new Promise((resolve) => agent.once("open", resolve));
agent.send(
JSON.stringify({
type: "TELEMETRY",
payload: {
status: "ONLINE",
metadata: {
system_metrics: {
cpu_usage_pct: 31,
},
container_health: {
state: "ONLINE",
summary: "6/6 containers healthy",
},
},
},
})
);
await waitFor(
() =>
browserMessages.some(
(message) => message.type === "gateway.telemetry" && message.error === "Telemetry ingestion failed"
),
{ description: "telemetry fanout after ingest failure" }
);
assert.ok(
browserMessages.every((message) => message.error !== "manager unavailable"),
"raw manager errors must not be sent to browser streams"
);
assert.ok(
browserMessages.some(
(message) => message.type === "stats.updated" && message.statistics?.system_metrics?.cpu_usage_pct === 31
)
);
browser.terminate(); browser.terminate();
agent.terminate(); agent.terminate();
+10 -41
View File
@@ -15,7 +15,6 @@ function readRequiredSource(...pathSegments) {
const baseComposeSource = readRequiredSource("docker-compose.yml"); const baseComposeSource = readRequiredSource("docker-compose.yml");
const exampleComposeSource = readRequiredSource("docker-compose.example.yml"); const exampleComposeSource = readRequiredSource("docker-compose.example.yml");
const standaloneProdComposeSource = readRequiredSource("docker-compose.prod.standalone.yml");
const traefikSource = [ const traefikSource = [
readRequiredSource("services", "traefik", "traefik.yml"), readRequiredSource("services", "traefik", "traefik.yml"),
readRequiredSource("services", "traefik", "traefik.prod.yml"), readRequiredSource("services", "traefik", "traefik.prod.yml"),
@@ -24,7 +23,7 @@ const traefikSource = [
function readComposeServiceBlock(composeSource, serviceName) { function readComposeServiceBlock(composeSource, serviceName) {
const escapedServiceName = serviceName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const escapedServiceName = serviceName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const servicePattern = new RegExp( const servicePattern = new RegExp(
`^[ ]{2}${escapedServiceName}:\\r?\\n([\\s\\S]*?)(?=^[ ]{2}[A-Za-z0-9_-]+:|^volumes:|^networks:|(?![\\s\\S]))`, `^\\s{2}${escapedServiceName}:\\n([\\s\\S]*?)(?=^\\s{2}[A-Za-z0-9_-]+:|^volumes:|^networks:|\\Z)`,
"m" "m"
); );
const match = composeSource.match(servicePattern); const match = composeSource.match(servicePattern);
@@ -36,50 +35,20 @@ test("traefik does not expose a dedicated public edge broker port", () => {
assert.doesNotMatch(traefikSource, /edge-broker:\s*\n\s*address:\s*":4300"/); assert.doesNotMatch(traefikSource, /edge-broker:\s*\n\s*address:\s*":4300"/);
}); });
test("base docker compose routes edge broker traffic through traefik", () => { test("base docker compose exposes the edge broker service on port 4300", () => {
const serviceBlock = readComposeServiceBlock(baseComposeSource, "edge-broker"); assert.match(baseComposeSource, /\bedge-broker:\b/);
assert.doesNotMatch(serviceBlock, /\n\s+ports:\s*\n[\s\S]*?\n\s+- "4300:4300"/); assert.match(baseComposeSource, /edge-broker:\s*\n[\s\S]*?\n\s+ports:\s*\n\s+- "4300:4300"/);
assert.match(serviceBlock, /EDGE_AUTH_MODE:\s*\$\{EDGE_AUTH_MODE:-strict\}/);
assert.match(serviceBlock, /EDGE_MANAGER_URL:\s*\$\{EDGE_MANAGER_URL:-http:\/\/caddy\}/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.priority=200/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.priority=200/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.rule=Host\(`api\.truckwash\.dk`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-io\.rule=Host\(`api\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-v2\.rule=Host\(`api-v2\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.rule=Host\(`localhost`\) && PathPrefix\(`\/api\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.middlewares\.edge-broker-strip\.stripPrefix\.prefixes=\/edge-broker/);
assert.match(serviceBlock, /traefik\.http\.middlewares\.edge-broker-strip-local\.stripPrefix\.prefixes=\/api\/edge-broker/);
assert.match(serviceBlock, /traefik\.http\.services\.edge-broker\.loadbalancer\.server\.port=4300/);
}); });
test("example docker compose routes edge broker traffic through traefik", () => { test("example docker compose exposes the edge broker service on port 4300", () => {
const serviceBlock = readComposeServiceBlock(exampleComposeSource, "edge-broker"); assert.match(exampleComposeSource, /\bedge-broker:\b/);
assert.doesNotMatch(serviceBlock, /\n\s+ports:\s*\n[\s\S]*?\n\s+- "4300:4300"/); assert.match(exampleComposeSource, /edge-broker:\s*\n[\s\S]*?\n\s+ports:\s*\n\s+- "4300:4300"/);
assert.match(serviceBlock, /EDGE_AUTH_MODE:\s*\$\{EDGE_AUTH_MODE:-strict\}/);
assert.match(serviceBlock, /EDGE_MANAGER_URL:\s*\$\{EDGE_MANAGER_URL:-http:\/\/caddy\}/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.rule=Host\(`api\.example\.com`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.rule=Host\(`localhost`\) && PathPrefix\(`\/api\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.services\.edge-broker\.loadbalancer\.server\.port=4300/);
}); });
test("standalone production compose routes edge broker traffic through traefik", () => { test("php services receive broker websocket environment defaults", () => {
const serviceBlock = readComposeServiceBlock(standaloneProdComposeSource, "edge-broker");
assert.doesNotMatch(serviceBlock, /\n\s+ports:\s*\n[\s\S]*?\n\s+- "4300:4300"/);
assert.match(serviceBlock, /EDGE_AUTH_MODE:\s*\$\{EDGE_AUTH_MODE:-manager\}/);
assert.match(serviceBlock, /EDGE_MANAGER_URL:\s*\$\{EDGE_MANAGER_URL:-http:\/\/caddy\}/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.priority=200/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.priority=200/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.rule=Host\(`api\.truckwash\.dk`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-io\.rule=Host\(`api\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-v2\.rule=Host\(`api-v2\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.rule=Host\(`localhost`\) && PathPrefix\(`\/api\/edge-broker`\)/);
assert.match(serviceBlock, /traefik\.http\.services\.edge-broker\.loadbalancer\.server\.port=4300/);
});
test("compose config does not provide insecure broker secret defaults", () => {
for (const composeSource of [baseComposeSource, exampleComposeSource]) { for (const composeSource of [baseComposeSource, exampleComposeSource]) {
assert.match(composeSource, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/); assert.match(composeSource, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/);
assert.match(composeSource, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:\?set EDGE_BROKER_SHARED_SECRET in \.env\}/); assert.match(composeSource, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev\}/);
} }
}); });
@@ -88,6 +57,6 @@ test("base docker compose wires the broker into each php worker", () => {
const serviceBlock = readComposeServiceBlock(baseComposeSource, serviceName); const serviceBlock = readComposeServiceBlock(baseComposeSource, serviceName);
assert.match(serviceBlock, /\n\s+depends_on:\s*\n[\s\S]*?\n\s+- edge-broker/); assert.match(serviceBlock, /\n\s+depends_on:\s*\n[\s\S]*?\n\s+- edge-broker/);
assert.match(serviceBlock, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/); assert.match(serviceBlock, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/);
assert.match(serviceBlock, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:\?set EDGE_BROKER_SHARED_SECRET in \.env\}/); assert.match(serviceBlock, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev\}/);
} }
}); });
@@ -1,62 +0,0 @@
import test from "node:test";
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
const brokerEntryPath = path.resolve(testDirectory, "../server.mjs");
test("server entrypoint starts the broker and stays alive until terminated", async () => {
const child = spawn(process.execPath, [brokerEntryPath], {
env: {
...process.env,
PORT: "0",
EDGE_AUTH_MODE: "stub",
},
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`Broker entrypoint did not report readiness.\nstdout:\n${stdout}\nstderr:\n${stderr}`));
}, 10_000);
child.once("exit", (code, signal) => {
clearTimeout(timeout);
reject(new Error(`Broker entrypoint exited early with code=${code} signal=${signal}.\nstdout:\n${stdout}\nstderr:\n${stderr}`));
});
const poll = () => {
if (/TruckWash edge broker listening on \d+/.test(stdout)) {
clearTimeout(timeout);
resolve();
return;
}
setTimeout(poll, 25);
};
poll();
});
assert.equal(child.exitCode, null, `Broker exited unexpectedly.\nstdout:\n${stdout}\nstderr:\n${stderr}`);
const exitResult = await new Promise((resolve, reject) => {
child.once("exit", (code, signal) => resolve({ code, signal }));
child.kill("SIGTERM");
setTimeout(() => reject(new Error("Broker did not exit after SIGTERM.")), 10_000);
});
const exitedCleanly = exitResult.code === 0 || exitResult.signal === "SIGTERM";
assert.equal(exitedCleanly, true, `Broker exited unsuccessfully.\nstdout:\n${stdout}\nstderr:\n${stderr}`);
});
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -1,110 +0,0 @@
<?php
namespace classes;
use RuntimeException;
class application_write_freeze
{
public static function freeze(string $reason, string $owner, int $ttlSeconds = 300): void
{
$payload = [
'reason' => $reason,
'owner' => $owner,
'created_at' => date('c'),
'expires_at' => date('c', time() + max(30, $ttlSeconds)),
];
self::writeState($payload);
}
public static function unfreeze(?string $owner = null): void
{
$state = self::state();
if ($owner !== null && isset($state['owner']) && $state['owner'] !== $owner) {
return;
}
$path = self::statePath();
if (is_file($path)) {
@unlink($path);
}
}
public static function state(): array
{
$path = self::statePath();
if (!is_file($path)) {
return [];
}
$state = json_decode((string)file_get_contents($path), true);
if (!is_array($state)) {
@unlink($path);
return [];
}
$expiresAt = strtotime((string)($state['expires_at'] ?? ''));
if ($expiresAt !== false && $expiresAt < time()) {
@unlink($path);
return [];
}
return $state;
}
public static function isFrozen(): bool
{
return self::state() !== [];
}
public static function shouldBlock(string $method, string $uri, bool $isCronOrCli): bool
{
if (!self::isFrozen()) {
return false;
}
if ($isCronOrCli) {
return true;
}
$method = strtoupper($method);
if (in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return false;
}
$path = parse_url($uri, PHP_URL_PATH) ?: '';
return !str_starts_with($path, '/superuser/replication');
}
private static function writeState(array $state): void
{
$path = self::statePath();
$dir = dirname($path);
if (!is_dir($dir) && !mkdir($dir, 0770, true) && !is_dir($dir)) {
throw new RuntimeException('Could not create write-freeze directory.');
}
$tempPath = tempnam($dir, 'write-freeze-');
if ($tempPath === false) {
throw new RuntimeException('Could not create write-freeze temp file.');
}
try {
file_put_contents($tempPath, json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL, LOCK_EX);
if (!rename($tempPath, $path)) {
throw new RuntimeException('Could not atomically replace write-freeze state.');
}
} finally {
if (is_file($tempPath)) {
@unlink($tempPath);
}
}
}
private static function statePath(): string
{
$root = defined('WD') ? WD : dirname(__DIR__);
return $root . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'application-write-freeze.json';
}
}
+1 -7
View File
@@ -133,14 +133,8 @@ class attachments implements attachments_i
protected function fetchAttachmentRows(string $type, array $object_ids, array $options = []): array protected function fetchAttachmentRows(string $type, array $object_ids, array $options = []): array
{ {
$options = $this->normalizeAttachmentOptions($options); $options = $this->normalizeAttachmentOptions($options);
$rawType = trim($type, '`');
$objectTypes = array_values(array_unique([
$rawType,
'`' . $rawType . '`',
]));
return (new object_attachments_o())->getFieldsWhereIn([ return (new object_attachments_o())->getFieldsWhereIn([
'object_type' => $objectTypes, 'object_type' => $type,
'object_id' => $object_ids, 'object_id' => $object_ids,
'deleted_at' => null 'deleted_at' => null
], $options); ], $options);
+12 -15
View File
@@ -6,7 +6,6 @@ use classes\totp;
use Exception; use Exception;
use interfaces\authentication_i; use interfaces\authentication_i;
use objects\plate_scanners_o; use objects\plate_scanners_o;
use objects\subuser_grants_o;
use objects\tokens_o; use objects\tokens_o;
use objects\users_o; use objects\users_o;
use objects\subusers_o; use objects\subusers_o;
@@ -126,13 +125,9 @@ class authentication implements authentication_i
public function validate_token(string $token): bool public function validate_token(string $token): bool
{ {
// First: try validating as a classic user auth token // First: try validating as a classic user auth token
try { $dbToken = (new tokens_o())->getToken($token);
$dbToken = (new tokens_o())->getToken($token); if ($dbToken && $dbToken->id) {
if ($dbToken && $dbToken->id && $dbToken->type->value() === 'AUTH_TOKEN') { return true;
return true;
}
} catch (Exception) {
// Ignore and continue to subuser session validation
} }
// Fallback: try validating as a subuser session token // Fallback: try validating as a subuser session token
$subuser = (new subusers_o())->getSubuserBySessionToken($token); $subuser = (new subusers_o())->getSubuserBySessionToken($token);
@@ -159,17 +154,19 @@ class authentication implements authentication_i
// Strip the Bearer prefix // Strip the Bearer prefix
$rawToken = str_replace('Bearer ', '', $rawToken); $rawToken = str_replace('Bearer ', '', $rawToken);
// Get the token from the database // Get the token from the database
try { $token = (new tokens_o())->getToken($rawToken);
$token = (new tokens_o())->getToken($rawToken);
} catch (Exception) {
return false;
}
// Check if the token exists // Check if the token exists
if (!$token->id) { if (!$token->id) {
return false; return false;
} }
if ($token->type->value() !== 'AUTH_TOKEN') { if ($token->type->value() === "AUTH_TOKEN_SUBUSER") {
return false; // Get the customer number from the headers
if (!isset($headers['X-Customer-Number'])) {
return false;
}
$customer_number = (int)$headers['X-Customer-Number'];
// Get the user by the customer number
return (new users_o())->getUserByCustomerNumber($customer_number);
} }
// Get the user from the database // Get the user from the database
$user = (new users_o())->getUserById($token->user_id->value()); $user = (new users_o())->getUserById($token->user_id->value());
@@ -9,11 +9,7 @@ use interfaces\shelly_transport_i;
class cloud_shelly_transport implements shelly_transport_i class cloud_shelly_transport implements shelly_transport_i
{ {
public function __construct( public function __construct(private readonly ?shelly $client = null)
private readonly ?shelly $client = null,
private readonly bool $logRelaySignals = true,
private readonly ?edge_gateway_manager $manager = null
)
{ {
} }
@@ -34,47 +30,6 @@ class cloud_shelly_transport implements shelly_transport_i
public function sendPostRequest(string $endpoint, array $data, ?int $department_id = null): array|object|null public function sendPostRequest(string $endpoint, array $data, ?int $department_id = null): array|object|null
{ {
try { return $this->client()->sendPostRequest($endpoint, $data);
$response = $this->client()->sendPostRequest($endpoint, $data);
$this->logRelaySignal($endpoint, $data, $department_id, $response, null);
return $response;
} catch (\Throwable $exception) {
$this->logRelaySignal($endpoint, $data, $department_id, null, $exception);
throw $exception;
}
}
private function logRelaySignal(
string $endpoint,
array $data,
?int $department_id,
array|object|null $response,
?\Throwable $exception
): void {
if (!$this->logRelaySignals || $department_id === null || $department_id <= 0 || !$this->isRelayEndpoint($endpoint)) {
return;
}
try {
$this->manager()->appendRelayTransportLog(
$department_id,
$endpoint,
$data,
$response,
'cloud',
$exception?->getMessage()
);
} catch (\Throwable) {
}
}
private function isRelayEndpoint(string $endpoint): bool
{
return in_array($endpoint, ['/v2/devices/api/get', '/v2/devices/api/set/switch'], true);
}
private function manager(): edge_gateway_manager
{
return $this->manager ?? new edge_gateway_manager();
} }
} }
-39
View File
@@ -1,39 +0,0 @@
<?php
namespace classes;
require_once WD . '/interfaces/universal_module_i.php';
require_once WD . '/modules/coolify/coolify_c.php';
use Exception;
use interfaces\universal_module_i;
use modules\coolify\coolify_c;
class coolify implements universal_module_i
{
public coolify_c $config;
public function __construct()
{
$this->config = new coolify_c();
}
/**
* @throws Exception
*/
public function requireModuleEnabled(): void
{
if (!$this->config->enabled->isTrue()) {
throw new Exception('The Coolify module is not enabled');
}
}
public function isEnabled(): bool
{
try {
return $this->config->enabled->isTrue();
} catch (Exception) {
return false;
}
}
}
@@ -1,279 +0,0 @@
<?php
namespace classes;
use RuntimeException;
class coolify_api_client
{
private string $baseUrl;
private string $token;
private int $timeoutSeconds;
public function __construct(string $baseUrl, string $token, int $timeoutSeconds = 4)
{
$this->baseUrl = self::normalizeBaseUrl($baseUrl);
$this->token = trim($token);
$this->timeoutSeconds = max(1, $timeoutSeconds);
if ($this->baseUrl === '' || $this->token === '') {
throw new RuntimeException('Coolify base URL and API token are required.');
}
}
public static function normalizeBaseUrl(string $baseUrl): string
{
$baseUrl = rtrim(trim($baseUrl), '/');
if ($baseUrl === '') {
return '';
}
if (preg_match('#/api/v[0-9]+$#i', $baseUrl) === 1) {
return $baseUrl;
}
return $baseUrl . '/api/v1';
}
public function healthcheck(): array
{
return $this->request('GET', '/health', null, false);
}
public function version(): array
{
return $this->request('GET', '/version');
}
public function listServers(): array
{
return $this->request('GET', '/servers');
}
public function listProjects(): array
{
return $this->request('GET', '/projects');
}
public function listProjectEnvironments(string $projectUuid): array
{
return $this->request('GET', '/projects/' . rawurlencode($projectUuid) . '/environments');
}
public function listServices(): array
{
return $this->request('GET', '/services');
}
public function listGithubApps(): array
{
return $this->request('GET', '/github-apps');
}
public function getService(string $uuid): array
{
return $this->request('GET', '/services/' . rawurlencode($uuid));
}
public function createService(array $payload): array
{
return $this->request('POST', '/services', $payload);
}
public function createPrivateGithubAppApplication(array $payload): array
{
return $this->request('POST', '/applications/private-github-app', $payload);
}
public function getApplication(string $uuid): array
{
return $this->request('GET', '/applications/' . rawurlencode($uuid));
}
public function updateApplication(string $uuid, array $payload): array
{
return $this->request('PATCH', '/applications/' . rawurlencode($uuid), $payload);
}
public function updateService(string $uuid, array $payload): array
{
return $this->request('PATCH', '/services/' . rawurlencode($uuid), $payload);
}
public function updateServiceEnvsBulk(string $uuid, array $env): array
{
if ($env === []) {
return [];
}
return $this->request('PATCH', '/services/' . rawurlencode($uuid) . '/envs/bulk', [
'data' => self::bulkEnvData($env),
]);
}
public function updateApplicationEnvsBulk(string $uuid, array $env): array
{
if ($env === []) {
return [];
}
return $this->request('PATCH', '/applications/' . rawurlencode($uuid) . '/envs/bulk', [
'data' => self::bulkEnvData($env),
]);
}
public function listApplicationEnvs(string $uuid): array
{
return $this->request('GET', '/applications/' . rawurlencode($uuid) . '/envs');
}
public function deleteApplicationEnv(string $uuid, string $envUuid): array
{
return $this->request('DELETE', '/applications/' . rawurlencode($uuid) . '/envs/' . rawurlencode($envUuid));
}
private static function bulkEnvData(array $env): array
{
$data = [];
foreach ($env as $key => $value) {
$data[] = [
'key' => (string)$key,
'value' => (string)$value,
'is_preview' => false,
'is_literal' => true,
'is_multiline' => str_contains((string)$value, "\n"),
'is_shown_once' => false,
];
}
return $data;
}
public function deployResource(string $uuid, bool $force = false): array
{
$path = '/deploy?uuid=' . rawurlencode($uuid) . '&force=' . ($force ? 'true' : 'false');
return $this->request('GET', $path);
}
public function startService(string $uuid): array
{
return $this->request('GET', '/services/' . rawurlencode($uuid) . '/start');
}
public function restartService(string $uuid): array
{
return $this->request('GET', '/services/' . rawurlencode($uuid) . '/restart');
}
public function restartApplication(string $uuid): array
{
return $this->request('GET', '/applications/' . rawurlencode($uuid) . '/restart');
}
public function stopApplication(string $uuid): array
{
return $this->request('GET', '/applications/' . rawurlencode($uuid) . '/stop');
}
public function deleteService(string $uuid): array
{
return $this->request('DELETE', '/services/' . rawurlencode($uuid));
}
public function listDeployments(): array
{
return $this->request('GET', '/deployments');
}
protected function request(string $method, string $path, ?array $payload = null, bool $versionedApi = true): array
{
$url = ($versionedApi ? $this->baseUrl : $this->apiRootUrl()) . '/' . ltrim($path, '/');
$curl = curl_init($url);
if ($curl === false) {
throw new RuntimeException('Could not initialize Coolify API request.');
}
$headers = [
'Accept: application/json',
'Authorization: Bearer ' . $this->token,
];
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, min(2, $this->timeoutSeconds));
curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeoutSeconds);
curl_setopt($curl, CURLOPT_NOSIGNAL, true);
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
if ($payload !== null) {
$body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($body === false) {
throw new RuntimeException('Could not encode Coolify API payload.');
}
$headers[] = 'Content-Type: application/json';
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$raw = curl_exec($curl);
$error = curl_error($curl);
$status = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($raw === false) {
throw new RuntimeException('Coolify API request failed: ' . $error);
}
$decoded = null;
if (trim((string)$raw) !== '') {
$decoded = json_decode((string)$raw, true);
if (!is_array($decoded)) {
$decoded = ['raw' => (string)$raw];
}
}
if ($status < 200 || $status >= 300) {
$message = is_array($decoded)
? (string)($decoded['message'] ?? $decoded['error'] ?? ('HTTP ' . $status))
: ('HTTP ' . $status);
if (is_array($decoded)) {
$details = self::validationErrorSummary($decoded);
if ($details !== '') {
$message .= ': ' . $details;
}
}
throw new RuntimeException('Coolify API request failed: ' . $message);
}
return is_array($decoded) ? $decoded : [];
}
private static function validationErrorSummary(array $decoded): string
{
$errors = $decoded['errors'] ?? $decoded['data']['errors'] ?? null;
if (!is_array($errors)) {
return '';
}
$parts = [];
foreach ($errors as $field => $messages) {
$fieldName = trim((string)$field);
$fieldPrefix = $fieldName !== '' ? $fieldName . ': ' : '';
if (is_array($messages)) {
$messages = implode(', ', array_filter(array_map(static fn(mixed $message): string => trim((string)$message), $messages)));
} else {
$messages = trim((string)$messages);
}
if ($messages !== '') {
$parts[] = $fieldPrefix . $messages;
}
}
return implode('; ', array_slice($parts, 0, 5));
}
private function apiRootUrl(): string
{
return preg_replace('#/v[0-9]+$#i', '', $this->baseUrl) ?: $this->baseUrl;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,243 +0,0 @@
<?php
namespace classes;
/**
* Additive schema bootstrap for the Coolify infrastructure integration.
*
* The legacy stack has no centralized migration runner, so this class must be
* safe to call from request handlers, cron, and tests.
*/
class coolify_schema_bootstrap
{
private static bool $initialized = false;
private static ?bool $tablesExist = null;
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
$queries = [
"CREATE TABLE IF NOT EXISTS coolify_instances (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
label VARCHAR(128) NOT NULL,
base_url VARCHAR(512) NOT NULL,
api_token_secret TEXT NOT NULL,
default_project_uuid VARCHAR(128) NULL,
default_environment_uuid VARCHAR(128) NULL,
default_environment_name VARCHAR(128) NULL,
default_server_uuid VARCHAR(128) NULL,
default_destination_uuid VARCHAR(128) NULL,
status VARCHAR(32) NOT NULL DEFAULT 'unknown',
last_checked_at DATETIME NULL,
last_error TEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
INDEX idx_coolify_instances_status (status),
INDEX idx_coolify_instances_deleted_at (deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS coolify_targets (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
instance_id BIGINT UNSIGNED NOT NULL,
replication_host_id BIGINT UNSIGNED NULL,
kind VARCHAR(16) NOT NULL,
label VARCHAR(128) NOT NULL,
role VARCHAR(16) NOT NULL DEFAULT 'replica',
server_uuid VARCHAR(128) NULL,
project_uuid VARCHAR(128) NULL,
environment_uuid VARCHAR(128) NULL,
environment_name VARCHAR(128) NULL,
destination_uuid VARCHAR(128) NULL,
resource_uuid VARCHAR(128) NULL,
resource_type VARCHAR(32) NOT NULL DEFAULT 'service',
resource_name VARCHAR(128) NULL,
deployment_status VARCHAR(32) NOT NULL DEFAULT 'pending',
availability_state VARCHAR(32) NOT NULL DEFAULT 'degraded',
desired_compose_hash CHAR(64) NULL,
last_reconcile_status VARCHAR(32) NULL,
last_reconcile_json LONGTEXT NULL,
last_reconciled_at DATETIME NULL,
options_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
INDEX idx_coolify_targets_instance (instance_id),
INDEX idx_coolify_targets_replication_host (replication_host_id),
INDEX idx_coolify_targets_kind_status (kind, deployment_status),
INDEX idx_coolify_targets_availability (availability_state),
INDEX idx_coolify_targets_resource_uuid (resource_uuid),
INDEX idx_coolify_targets_deleted_at (deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS coolify_operations (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
target_id BIGINT UNSIGNED NULL,
instance_id BIGINT UNSIGNED NULL,
operation VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'running',
guarded TINYINT(1) NOT NULL DEFAULT 1,
message VARCHAR(512) NULL,
error_message TEXT NULL,
context_json LONGTEXT NULL,
actor_user_id INT NULL,
started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
completed_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_coolify_operations_target (target_id),
INDEX idx_coolify_operations_instance (instance_id),
INDEX idx_coolify_operations_status (status),
INDEX idx_coolify_operations_operation (operation)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS coolify_audit_logs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
target_id BIGINT UNSIGNED NULL,
instance_id BIGINT UNSIGNED NULL,
replication_host_id BIGINT UNSIGNED NULL,
action VARCHAR(64) NOT NULL,
actor_user_id INT NULL,
severity VARCHAR(16) NOT NULL DEFAULT 'info',
context_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_coolify_audit_target (target_id),
INDEX idx_coolify_audit_instance (instance_id),
INDEX idx_coolify_audit_replication_host (replication_host_id),
INDEX idx_coolify_audit_action (action),
INDEX idx_coolify_audit_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS coolify_instance_gateways (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
instance_id BIGINT UNSIGNED NULL,
hostname VARCHAR(255) NOT NULL,
target_ip VARCHAR(64) NOT NULL,
enabled TINYINT(1) NOT NULL DEFAULT 1,
priority INT NOT NULL DEFAULT 100,
health_state VARCHAR(32) NOT NULL DEFAULT 'unknown',
lb_state VARCHAR(32) NOT NULL DEFAULT 'unknown',
last_probe_json LONGTEXT NULL,
last_probed_at DATETIME NULL,
last_reconciled_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
UNIQUE KEY uniq_coolify_instance_gateways_target_ip (target_ip),
INDEX idx_coolify_instance_gateways_instance (instance_id),
INDEX idx_coolify_instance_gateways_enabled (enabled),
INDEX idx_coolify_instance_gateways_lb_state (lb_state),
INDEX idx_coolify_instance_gateways_deleted_at (deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
];
foreach ($queries as $sql) {
$db->query($sql);
}
self::ensureColumn('coolify_instances', 'default_destination_uuid', 'VARCHAR(128) NULL');
self::ensureColumn('coolify_targets', 'availability_state', "VARCHAR(32) NOT NULL DEFAULT 'degraded'");
self::ensureColumn('coolify_targets', 'desired_compose_hash', 'CHAR(64) NULL');
self::ensureColumn('coolify_targets', 'last_reconcile_json', 'LONGTEXT NULL');
self::ensureColumn('coolify_operations', 'guarded', 'TINYINT(1) NOT NULL DEFAULT 1');
self::ensureColumn('coolify_instance_gateways', 'last_reconciled_at', 'DATETIME NULL');
self::ensureModuleConfigDefault('Coolify', 'enabled', 'false', 'bool');
self::ensureModuleConfigDefault('Coolify', 'lb_automation_enabled', 'false', 'bool');
self::ensureModuleConfigDefault('Coolify', 'lb_automation_mode', 'report_only', 'string');
self::ensureModuleConfigDefault('Coolify', 'hetzner_load_balancer_id', '', 'string');
self::ensureModuleConfigDefault('Coolify', 'hetzner_cloud_api_token', '', 'string');
self::ensureModuleConfigDefault('Coolify', 'public_gateway_host', 'api-v2.truckwash.io', 'string');
self::ensureModuleConfigDefault('Coolify', 'public_gateway_probe_path', '', 'string');
self::ensureModuleConfigDefault('Coolify', 'github_runner_token', '', 'string');
self::ensureModuleConfigDefault('Coolify', 'github_runner_service_uuid', '', 'string');
self::ensureModuleConfigDefault('Coolify', 'github_runner_frontend_repository', 'copenhagentruckwash/pleno-vue', 'string');
self::ensureModuleConfigDefault('Coolify', 'github_runner_backend_repository', 'copenhagentruckwash/api', 'string');
self::ensureModuleConfigDefault('Coolify', 'github_runner_labels', 'self-hosted,Linux,X64,default', 'string');
self::ensureModuleConfigDefault('Coolify', 'github_runner_count_per_repo', '1', 'int');
self::ensureDefaultGateway('node1.truckwash.io', '94.130.142.41', 10);
self::ensureDefaultGateway('node2.truckwash.io', '65.21.214.30', 20);
self::ensureDefaultGateway('node3.truckwash.io', '23.88.23.183', 30);
self::$initialized = true;
self::$tablesExist = true;
}
public static function tablesExist(): bool
{
if (self::$tablesExist !== null) {
return self::$tablesExist;
}
global $db;
foreach (['coolify_instances', 'coolify_targets', 'coolify_operations', 'coolify_audit_logs', 'coolify_instance_gateways'] as $table) {
$tableSql = $db->escape_string($table);
$result = $db->query("SHOW TABLES LIKE '$tableSql'");
if ($result === false || $result->num_rows === 0) {
self::$tablesExist = false;
return false;
}
}
self::$tablesExist = true;
return self::$tablesExist;
}
private static function ensureColumn(string $table, string $column, string $definition): void
{
global $db;
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
$column = preg_replace('/[^a-zA-Z0-9_]/', '', $column);
if ($table === '' || $column === '') {
return;
}
$result = $db->query("SHOW COLUMNS FROM `$table` LIKE '$column'");
if ($result !== false && $result->num_rows > 0) {
return;
}
$db->query("ALTER TABLE `$table` ADD COLUMN `$column` $definition");
}
private static function ensureModuleConfigDefault(string $module, string $variable, string $value, string $type): void
{
global $db;
$moduleSql = $db->escape_string($module);
$variableSql = $db->escape_string($variable);
$result = $db->query("SELECT value FROM module_config WHERE module = '$moduleSql' AND variable = '$variableSql' LIMIT 1");
if ($result !== false && $result->num_rows > 0) {
return;
}
$valueSql = $db->escape_string($value);
$typeSql = $db->escape_string($type);
$db->query("INSERT INTO module_config (module, variable, value, type) VALUES ('$moduleSql', '$variableSql', '$valueSql', '$typeSql')");
}
private static function ensureDefaultGateway(string $hostname, string $targetIp, int $priority): void
{
global $db;
$targetIpSql = $db->escape_string($targetIp);
$result = $db->query("SELECT id FROM coolify_instance_gateways WHERE target_ip = '$targetIpSql' LIMIT 1");
if ($result !== false && $result->num_rows > 0) {
return;
}
$hostnameSql = $db->escape_string($hostname);
$db->query(
"INSERT INTO coolify_instance_gateways (hostname, target_ip, enabled, priority)
VALUES ('$hostnameSql', '$targetIpSql', 1, " . (int)$priority . ")"
);
}
}
-194
View File
@@ -1,194 +0,0 @@
<?php
namespace classes;
class cors_policy
{
public const ALLOWED_HEADERS = 'Content-Type, Authorization, X-Customer-Number, X-Release-Trace, X-Release-Channel, X-Frontend-Version, Cache-Control, Pragma, *';
public const ALLOWED_METHODS = 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
public const EXPOSED_HEADERS = 'Server-Timing';
public const MAX_AGE_SECONDS = '86400';
private const REQUIRED_ALLOWED_ORIGINS = [
'https://truckwash.io',
'https://www.truckwash.io',
'https://api.truckwash.io',
'https://api.truckwash.io:4433',
'https://api-v2.truckwash.io',
'https://web.truckwash.dk',
'https://api.truckwash.dk',
'https://truckwash.dk',
'https://www.truckwash.dk',
'https://staging.truckwash.io',
'http://localhost',
'https://localhost',
'http://localhost:4433',
'https://localhost:4433',
'https://twdev.jeppeb.dk',
'http://localhost:5173',
'http://localhost:5174',
'http://127.0.0.1:5173',
'http://127.0.0.1:5174',
];
public static function normalizeOrigin(?string $value): string
{
$value = trim((string)$value);
if ($value === '' || $value === '*') {
return $value;
}
if (preg_match('#^https?://#i', $value) !== 1) {
return '';
}
$parts = parse_url($value);
if (!is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
return '';
}
$scheme = strtolower((string)$parts['scheme']);
if (!in_array($scheme, ['http', 'https'], true)) {
return '';
}
$host = strtolower((string)$parts['host']);
$port = isset($parts['port']) ? ':' . (int)$parts['port'] : '';
return $scheme . '://' . $host . $port;
}
/**
* @return array<int,string>
*/
public static function requiredAllowedOrigins(): array
{
return self::REQUIRED_ALLOWED_ORIGINS;
}
/**
* @return array<int,string>
*/
public static function allowedOrigins(string $corsConfig): array
{
$origins = [];
foreach (self::splitOrigins($corsConfig) as $configuredOrigin) {
if ($configuredOrigin === '*') {
return ['*'];
}
$origin = self::normalizeOrigin($configuredOrigin);
if ($origin !== '') {
$origins[$origin] = true;
}
}
foreach (self::REQUIRED_ALLOWED_ORIGINS as $requiredOrigin) {
$origin = self::normalizeOrigin($requiredOrigin);
if ($origin !== '') {
$origins[$origin] = true;
}
}
return array_keys($origins);
}
public static function withRequiredOrigins(string $corsConfig): string
{
$allowedOrigins = self::allowedOrigins($corsConfig);
if ($allowedOrigins === ['*']) {
return '*';
}
return implode(',', $allowedOrigins);
}
public static function isOriginAllowed(?string $origin, string $corsConfig): bool
{
$origin = self::normalizeOrigin($origin);
if ($origin === '' || $origin === '*') {
return false;
}
$allowedOrigins = self::allowedOrigins($corsConfig);
return in_array('*', $allowedOrigins, true) || in_array($origin, $allowedOrigins, true);
}
/**
* @return array<string,string>
*/
public static function responseHeaders(?string $origin, string $corsConfig): array
{
$origin = self::normalizeOrigin($origin);
if ($origin === '' || !self::isOriginAllowed($origin, $corsConfig)) {
return [];
}
return [
'Access-Control-Allow-Origin' => $origin,
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Headers' => self::ALLOWED_HEADERS,
'Access-Control-Allow-Methods' => self::ALLOWED_METHODS,
'Access-Control-Expose-Headers' => self::EXPOSED_HEADERS,
'Access-Control-Max-Age' => self::MAX_AGE_SECONDS,
'Timing-Allow-Origin' => $origin,
'Vary' => 'Origin',
];
}
/**
* @return array{allowed:bool,status:int,headers:array<string,string>,body:string}
*/
public static function preflightResponse(?string $origin, string $corsConfig): array
{
$headers = self::responseHeaders($origin, $corsConfig);
if ($headers === []) {
return [
'allowed' => false,
'status' => 403,
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['success' => false, 'message' => 'CORS origin not allowed']) ?: '',
];
}
$headers['Content-Type'] = 'application/json';
return [
'allowed' => true,
'status' => 200,
'headers' => $headers,
'body' => '',
];
}
public static function applyResponseHeaders(string $corsConfig, ?string $origin = null): bool
{
$headers = self::responseHeaders($origin ?? ($_SERVER['HTTP_ORIGIN'] ?? ''), $corsConfig);
if ($headers === []) {
return false;
}
self::emitHeaders($headers);
return true;
}
/**
* @param array<string,string> $headers
*/
public static function emitHeaders(array $headers): void
{
foreach ($headers as $name => $value) {
header($name . ': ' . $value, strtolower((string)$name) !== 'vary');
}
}
/**
* @return array<int,string>
*/
private static function splitOrigins(string $corsConfig): array
{
return array_values(array_filter(
array_map('trim', explode(',', $corsConfig)),
static fn(string $origin): bool => $origin !== ''
));
}
}
@@ -1,501 +0,0 @@
<?php
namespace classes;
use objects\logs_o;
use objects\users_o;
class customer_mass_import_service
{
/**
* Import or create a company customer from one normalized spreadsheet row.
*
* @throws \RuntimeException
*/
public function import(array $payload): array
{
$normalized = $this->normalizePayload($payload);
$this->assertValidNormalizedPayload($normalized);
$customerNumber = (int)$normalized['customer_number'];
$cvr = (string)$normalized['cvr'];
$warnings = [];
$economicCustomers = $this->searchEconomicCustomersByCvr($cvr);
$localUserExistsBefore = $this->localCustomerNumberExists($customerNumber);
$localUser = $localUserExistsBefore ? $this->loadLocalCustomerByNumber($customerNumber) : null;
$matchingEconomicCustomer = $this->findEconomicCustomerByNumber($economicCustomers, $customerNumber);
if ($matchingEconomicCustomer !== null) {
$customer = $this->resolveLocalCustomer($customerNumber, $localUserExistsBefore, $localUser);
$this->syncLocalCustomer($customer, $normalized, $warnings);
[$action, $message] = $this->resolveExistingCustomerOutcome($localUserExistsBefore, $this->hasLocalAccount($customer));
return $this->buildSuccessResult(
$normalized,
$customer,
$action,
$message,
$localUserExistsBefore,
true,
false,
$warnings
);
}
if (count($economicCustomers) > 0) {
$existingEconomicCustomerNumber = $this->extractEconomicCustomerNumber($economicCustomers[0]);
$this->logIssue('CUSTOMER_MASS_IMPORT_CONFLICT', [
'phase' => 'search',
'cvr' => $cvr,
'requestedCustomerNumber' => $customerNumber,
'existingCustomerNumber' => $existingEconomicCustomerNumber,
]);
throw new \RuntimeException(
'CVR already registered under customer number '
. $existingEconomicCustomerNumber
. '. The submitted phone number must match the customer id. Manual cleanup or reassignment is required before retrying.',
409
);
}
$normalized['name'] = $this->resolveCreateName($normalized);
$normalized['email'] = $this->resolveCreateEmail($normalized, $warnings);
$createResponse = $this->createEconomicCustomer($normalized);
$createdCustomerNumber = $this->extractEconomicCustomerNumber($createResponse);
if ($createdCustomerNumber !== $customerNumber) {
$this->logIssue('CUSTOMER_MASS_IMPORT_CONFLICT', [
'phase' => 'create',
'cvr' => $cvr,
'requestedCustomerNumber' => $customerNumber,
'createdCustomerNumber' => $createdCustomerNumber,
'response' => $createResponse,
]);
throw new \RuntimeException(
'E-conomic created the customer under customer number '
. $createdCustomerNumber
. ' instead of the submitted phone number '
. $customerNumber
. '. Manual cleanup or reassignment is required before retrying.',
409
);
}
$customer = $this->resolveLocalCustomer($customerNumber, $localUserExistsBefore, $localUser);
$this->syncLocalCustomer($customer, $normalized, $warnings);
[$action, $message] = $this->resolveCreatedCustomerOutcome($localUserExistsBefore, $this->hasLocalAccount($customer));
return $this->buildSuccessResult(
$normalized,
$customer,
$action,
$message,
$localUserExistsBefore,
false,
true,
$warnings
);
}
protected function normalizePayload(array $payload): array
{
return [
'customer_number' => $this->normalizePositiveInt($payload['customer_number'] ?? $payload['phone'] ?? null),
'phone' => $this->normalizePositiveInt($payload['phone'] ?? $payload['customer_number'] ?? null),
'cvr' => $this->normalizeDigitString($payload['cvr'] ?? null),
'name' => $this->normalizeText($payload['name'] ?? $payload['company_name'] ?? null),
'email' => $this->normalizeEmail($payload['email'] ?? null),
'ean' => $this->normalizeDigitString($payload['ean'] ?? null),
];
}
protected function assertValidNormalizedPayload(array $normalized): void
{
$customerNumber = $normalized['customer_number'];
$cvr = $normalized['cvr'];
if ($customerNumber === null) {
throw new \RuntimeException('Phone number is required.', 400);
}
$customerNumberLength = strlen((string)$customerNumber);
if ($customerNumberLength < 8 || $customerNumberLength > 10) {
throw new \RuntimeException('Phone number must be between 8 and 10 digits.', 400);
}
if ($cvr === null) {
throw new \RuntimeException('CVR is required.', 400);
}
$cvrLength = strlen($cvr);
if ($cvrLength < 8 || $cvrLength > 20) {
throw new \RuntimeException('CVR must be between 8 and 20 digits.', 400);
}
if ($normalized['ean'] !== null && strlen((string)$normalized['ean']) > 13) {
throw new \RuntimeException('EAN must be at most 13 digits.', 400);
}
}
protected function normalizePositiveInt(mixed $value): ?int
{
$digits = $this->normalizeDigitString($value);
if ($digits === null) {
return null;
}
$normalized = (int)$digits;
return $normalized > 0 ? $normalized : null;
}
protected function normalizeDigitString(mixed $value): ?string
{
if ($value === null) {
return null;
}
$digits = preg_replace('/\D+/', '', (string)$value);
if (!is_string($digits)) {
return null;
}
$digits = trim($digits);
return $digits !== '' ? $digits : null;
}
protected function normalizeText(mixed $value): ?string
{
if ($value === null) {
return null;
}
$normalized = trim((string)$value);
return $normalized !== '' ? $normalized : null;
}
protected function normalizeEmail(mixed $value): ?string
{
$email = $this->normalizeText($value);
if ($email === null) {
return null;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \RuntimeException('Invalid email address.', 400);
}
return $email;
}
protected function resolveCreateName(array $normalized): string
{
if ($normalized['name'] !== null) {
return $normalized['name'];
}
$name = trim($this->fetchCompanyNameByCvr((string)$normalized['cvr']));
if ($name === '') {
throw new \RuntimeException('Customer name is required to create a new company.', 400);
}
return $name;
}
protected function resolveCreateEmail(array $normalized, array &$warnings): string
{
if ($normalized['email'] !== null) {
return $normalized['email'];
}
$warnings[] = 'No email was provided, defaulted to jb@truckwash.dk for the new e-conomic customer.';
return 'jb@truckwash.dk';
}
protected function searchEconomicCustomersByCvr(string $cvr): array
{
$response = (new economic())->customers->customers->search([
'corporateIdentificationNumber' => $cvr,
], [
'skipPages' => 0,
'pageSize' => 1000,
])->collection ?? [];
return is_array($response) ? $response : [];
}
protected function createEconomicCustomer(array $normalized): object
{
$payload = [
'customerNumber' => (int)$normalized['customer_number'],
'corporateIdentificationNumber' => (string)$normalized['cvr'],
'customerGroup' => [
'customerGroupNumber' => 1,
],
'paymentTerms' => [
'paymentTermsNumber' => 12,
],
'name' => (string)$normalized['name'],
'email' => (string)$normalized['email'],
'phone' => (int)$normalized['phone'],
'telephoneAndFaxNumber' => (string)$normalized['phone'],
'mobilePhone' => (string)$normalized['phone'],
'currency' => 'DKK',
'vatZone' => [
'vatZoneNumber' => 1,
],
];
if ($normalized['ean'] !== null) {
$payload['ean'] = (string)$normalized['ean'];
}
return (new economic())->customers->customers->create($payload);
}
protected function localCustomerNumberExists(int $customerNumber): bool
{
$rows = (new users_o())->getFieldsWhere([
'customer_number' => (string)$customerNumber,
], ['id']);
return count($rows) > 0;
}
protected function loadLocalCustomerByNumber(int $customerNumber): ?object
{
$customer = (new users_o())->getUserByCustomerNumber($customerNumber);
return $this->localUserExists($customer) ? $customer : null;
}
protected function resolveLocalCustomer(int $customerNumber, bool $localUserExistsBefore, ?object $localUser): object
{
if ($localUserExistsBefore && $this->localUserExists($localUser)) {
return $localUser;
}
return $this->bootstrapLocalCustomerOrFail($customerNumber);
}
protected function bootstrapLocalCustomerOrFail(int $customerNumber): object
{
$customer = (new users_o())->getUserByCustomerNumber($customerNumber);
if ($this->localUserExists($customer)) {
return $customer;
}
$this->logIssue('CUSTOMER_MASS_IMPORT_LOCAL_BOOTSTRAP_FAILED', [
'customerNumber' => $customerNumber,
]);
throw new \RuntimeException('Customer was created in e-conomic but could not be imported locally.', 500);
}
protected function fetchCompanyNameByCvr(string $cvr): string
{
return (string)((new virkdata())->getCompanyInformation($cvr, '', [])->name ?? '');
}
protected function logIssue(string $action, array $context): void
{
$message = json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($message === false) {
$message = 'Unable to encode customer mass import context';
}
(new logs_o())->add('customers', 'global', 0, 0, $action, $message);
}
protected function findEconomicCustomerByNumber(array $customers, int $customerNumber): ?object
{
foreach ($customers as $customer) {
if (!is_object($customer)) {
continue;
}
if ($this->extractEconomicCustomerNumber($customer) === $customerNumber) {
return $customer;
}
}
return null;
}
protected function extractEconomicCustomerNumber(object $customer): int
{
if (!isset($customer->customerNumber) || !is_numeric($customer->customerNumber)) {
return 0;
}
return (int)$customer->customerNumber;
}
protected function resolveExistingCustomerOutcome(bool $localUserExistsBefore, bool $hasAccount): array
{
if ($localUserExistsBefore && $hasAccount) {
return [
'account_already_exists',
'Customer already exists locally and already has a login account.',
];
}
if ($localUserExistsBefore) {
return [
'customer_already_exists',
'Customer already exists locally but does not have a login password yet.',
];
}
return [
'imported_existing_customer',
'Imported an existing e-conomic customer into the local customer database.',
];
}
protected function resolveCreatedCustomerOutcome(bool $localUserExistsBefore, bool $hasAccount): array
{
if ($localUserExistsBefore && $hasAccount) {
return [
'economic_customer_created_for_existing_account',
'Created the e-conomic customer for an existing local login account.',
];
}
if ($localUserExistsBefore) {
return [
'economic_customer_created_for_existing_customer',
'Created the e-conomic customer for an existing local customer record.',
];
}
return [
'created_customer',
'Created the customer in e-conomic and imported it locally.',
];
}
protected function buildSuccessResult(
array $normalized,
object $customer,
string $action,
string $message,
bool $existingLocalCustomer,
bool $existingEconomicCustomer,
bool $createdEconomicCustomer,
array $warnings
): array {
$customerName = $this->extractLocalUserDisplayName($customer) ?? $normalized['name'];
return [
'customer_number' => (int)$normalized['customer_number'],
'cvr' => (string)$normalized['cvr'],
'name' => $customerName,
'email' => $normalized['email'],
'ean' => $normalized['ean'],
'action' => $action,
'message' => $message,
'user_id' => $this->extractLocalUserId($customer),
'has_account' => $this->hasLocalAccount($customer),
'existing_local_customer' => $existingLocalCustomer,
'existing_economic_customer' => $existingEconomicCustomer,
'created_economic_customer' => $createdEconomicCustomer,
'warnings' => array_values(array_filter($warnings, static fn(mixed $warning): bool => is_string($warning) && trim($warning) !== '')),
];
}
protected function syncLocalCustomer(object $customer, array $normalized, array &$warnings): void
{
if (!$customer instanceof users_o || !$customer->exists()) {
return;
}
$name = $normalized['name'] ?? null;
$email = $normalized['email'] ?? null;
$phone = $normalized['phone'] ?? null;
$displayName = trim((string)($customer->display_name->value() ?? ''));
if ($name !== null && ($displayName === '' || strtolower($displayName) === 'unnamed')) {
$customer->display_name->set($name);
}
if ($email !== null && trim((string)($customer->email->value() ?? '')) === '') {
try {
$customer->setEmail($email);
} catch (\Throwable $throwable) {
$warnings[] = 'Unable to update local email: ' . $throwable->getMessage();
}
}
if ($phone !== null && empty($customer->phone->value())) {
try {
$customer->setPhoneNumber((int)$phone);
} catch (\Throwable $throwable) {
$warnings[] = 'Unable to update local phone number: ' . $throwable->getMessage();
}
}
}
protected function localUserExists(?object $user): bool
{
if (!is_object($user)) {
return false;
}
if (method_exists($user, 'exists')) {
try {
return (bool)$user->exists();
} catch (\Throwable) {
return false;
}
}
return isset($user->id) && is_numeric($user->id) && (int)$user->id > 0;
}
protected function hasLocalAccount(?object $user): bool
{
if (!$this->localUserExists($user)) {
return false;
}
if (method_exists($user, 'hasPassword')) {
try {
return (bool)$user->hasPassword();
} catch (\Throwable) {
return false;
}
}
return (bool)($user->has_password ?? false);
}
protected function extractLocalUserId(?object $user): ?int
{
if (!is_object($user) || !isset($user->id) || !is_numeric($user->id)) {
return null;
}
$userId = (int)$user->id;
return $userId > 0 ? $userId : null;
}
protected function extractLocalUserDisplayName(?object $user): ?string
{
if (!is_object($user)) {
return null;
}
if ($user instanceof users_o) {
$name = trim((string)($user->display_name->value() ?? ''));
return $name !== '' ? $name : null;
}
$name = trim((string)($user->display_name ?? $user->name ?? ''));
return $name !== '' ? $name : null;
}
}
@@ -1,94 +0,0 @@
<?php
namespace classes;
class customer_name_cache_payload_builder
{
/**
* @return array{name:string}|null
*/
public static function build(mixed $cached_name, ?string $fallback_name): ?array
{
$cached_name = self::normalizePayload($cached_name);
$name = self::extractName($cached_name);
if ($name !== null) {
return ['name' => $name];
}
$fallback_name = self::normalizeName($fallback_name);
if ($fallback_name !== null) {
return ['name' => $fallback_name];
}
return null;
}
private static function normalizePayload(mixed $payload): mixed
{
if (!is_string($payload)) {
return $payload;
}
$trimmed = trim($payload);
if ($trimmed === '') {
return null;
}
$decoded = json_decode($trimmed);
if (json_last_error() === JSON_ERROR_NONE) {
return $decoded;
}
return $trimmed;
}
private static function extractName(mixed $payload): ?string
{
if (is_string($payload)) {
return self::normalizeName($payload);
}
if (!is_object($payload) && !is_array($payload)) {
return null;
}
foreach (['name', 'customerName', 'customer_name', 'displayName', 'display_name'] as $key) {
$name = self::normalizeName(self::payloadValue($payload, $key));
if ($name !== null) {
return $name;
}
}
foreach (['customer', 'data', 'economic_customer'] as $key) {
$name = self::extractName(self::payloadValue($payload, $key));
if ($name !== null) {
return $name;
}
}
return null;
}
private static function payloadValue(mixed $payload, string $key): mixed
{
if (is_object($payload) && property_exists($payload, $key)) {
return $payload->{$key};
}
if (is_array($payload) && array_key_exists($key, $payload)) {
return $payload[$key];
}
return null;
}
private static function normalizeName(mixed $name): ?string
{
if (!is_string($name)) {
return null;
}
$name = trim($name);
return $name === '' ? null : $name;
}
}
@@ -1,98 +0,0 @@
<?php
namespace classes;
use RuntimeException;
class customer_order_product_policy
{
public const ONLY_TANKCLEANING_ATTRIBUTE = 'onlyTankCleaning';
public const ONLY_TANKCLEANING_MESSAGE = 'Only tankcleaning customers can only have tankcleaning products in their orders.';
public static function assertOrderAllowsProduct(int $orderId, int $productId): void
{
$message = self::orderProductViolationMessage($orderId, $productId);
if ($message !== null) {
throw new RuntimeException($message);
}
}
public static function orderProductViolationMessage(int $orderId, int $productId): ?string
{
$context = self::loadOrderProductContext($orderId, $productId);
if ($context === null) {
return null;
}
if ((int)($context['product_id'] ?? 0) < 1) {
return null;
}
return self::onlyTankCleaningViolation((bool)((int)($context['has_only_tank_cleaning'] ?? 0)), $context)
? self::ONLY_TANKCLEANING_MESSAGE
: null;
}
public static function onlyTankCleaningViolation(bool $customerHasOnlyTankCleaning, array $productRow): bool
{
return $customerHasOnlyTankCleaning && !self::isTankCleaningProductRow($productRow);
}
public static function isTankCleaningProductRow(array $row): bool
{
return (int)($row['product_category'] ?? $row['category'] ?? 0) === 5
|| self::rowMatchesProductTerms($row, ['tank cleaning', 'tankcleaning', 'tankrens']);
}
private static function loadOrderProductContext(int $orderId, int $productId): ?array
{
global $db;
if ($orderId < 1 || $productId < 1) {
return null;
}
$sql = "
SELECT
o.id AS order_id,
o.customer_id AS customer_number,
p.id AS product_id,
p.name AS product_name,
p.category AS product_category,
c.name AS category_name,
MAX(CASE WHEN ca.attribute = '" . self::ONLY_TANKCLEANING_ATTRIBUTE . "' THEN 1 ELSE 0 END) AS has_only_tank_cleaning
FROM orders o
LEFT JOIN products p ON p.id = {$productId}
LEFT JOIN categories c ON c.id = p.category
LEFT JOIN users u ON u.customer_number = o.customer_id
LEFT JOIN customer_attributes ca ON ca.user_id = u.id
AND ca.attribute = '" . self::ONLY_TANKCLEANING_ATTRIBUTE . "'
WHERE o.id = {$orderId}
GROUP BY o.id, o.customer_id, p.id, p.name, p.category, c.name
LIMIT 1
";
$result = $db->query($sql);
if (!$result || $result->num_rows < 1) {
return null;
}
$row = $result->fetch_assoc();
return is_array($row) ? $row : null;
}
private static function rowMatchesProductTerms(array $row, array $terms): bool
{
$haystack = strtolower(trim(
(string)($row['product_name'] ?? $row['name'] ?? '') . ' ' .
(string)($row['category_name'] ?? '')
));
foreach ($terms as $term) {
if ($term !== '' && str_contains($haystack, strtolower($term))) {
return true;
}
}
return false;
}
}
@@ -1,158 +0,0 @@
<?php
namespace classes;
use objects\orders_o;
use objects\products_o;
use objects\users_o;
class customer_product_rule_service
{
public const BLOCK_MESSAGE = 'This product is not allowed for the selected customer';
private const ADDON_CATEGORY_ID = 4;
private const TANK_CLEANING_CATEGORY_ID = 5;
/**
* @return array{rule:string,message:string}|null
*/
public function firstViolationForOrderItem(int $orderId, int $productId, ?int $relatedItemId): ?array
{
$order = (new orders_o())->getOrderById($orderId);
if (!$order->exists()) {
return null;
}
$product = (new products_o())->getProductById($productId);
if (!$product->exists()) {
return null;
}
$customer = (new users_o())->getUserByCustomerNumber((int)$order->customer_id->value());
if (!$customer->exists()) {
return null;
}
$categoryId = (int)$product->category->value();
$categoryName = $this->categoryName($categoryId);
$searchableProduct = $this->searchableProductText($product, $categoryName);
$isTankCleaningProduct = $this->isTankCleaningProduct($categoryId, $searchableProduct);
if ($customer->doesUserHaveAttribute('restrictAdditionalServices')
&& $this->isAdditionalServiceProduct($orderId, $relatedItemId, $categoryId, $searchableProduct)) {
return $this->violation('restrictAdditionalServices');
}
if ($customer->doesUserHaveAttribute('restrictTankCleaning') && $isTankCleaningProduct) {
return $this->violation('restrictTankCleaning');
}
if ($customer->doesUserHaveAttribute('onlyTankCleaning') && !$isTankCleaningProduct) {
return $this->violation('onlyTankCleaning');
}
if ($customer->doesUserHaveAttribute('restrictSpotFree')
&& $this->containsAny($searchableProduct, ['spot free', 'spotfree'])) {
return $this->violation('restrictSpotFree');
}
if ($customer->doesUserHaveAttribute('restrictInteriorCleaning')
&& $this->containsAny($searchableProduct, ['interior', 'indvendig'])) {
return $this->violation('restrictInteriorCleaning');
}
return null;
}
/**
* @return array{rule:string,message:string}
*/
private function violation(string $rule): array
{
return [
'rule' => $rule,
'message' => self::BLOCK_MESSAGE,
];
}
private function isAdditionalServiceProduct(int $orderId, ?int $relatedItemId, int $categoryId, string $searchableProduct): bool
{
if ($relatedItemId !== null && $relatedItemId > 0) {
return true;
}
if ($categoryId === self::ADDON_CATEGORY_ID) {
return true;
}
if ($this->containsAny($searchableProduct, ['add-on', 'add on', 'addon', 'tilvalg'])) {
return true;
}
return $this->countStandaloneOrderItems($orderId) > 0;
}
private function isTankCleaningProduct(int $categoryId, string $searchableProduct): bool
{
if ($categoryId === self::TANK_CLEANING_CATEGORY_ID) {
return true;
}
return $this->containsAny($searchableProduct, ['tank cleaning', 'tankcleaning', 'tankrens', 'tank rens']);
}
private function searchableProductText(products_o $product, string $categoryName): string
{
return strtolower(trim((string)$product->name->value() . ' ' . $categoryName));
}
/**
* @param array<int, string> $terms
*/
private function containsAny(string $value, array $terms): bool
{
foreach ($terms as $term) {
if ($term !== '' && str_contains($value, $term)) {
return true;
}
}
return false;
}
private function categoryName(int $categoryId): string
{
global $db;
if ($categoryId <= 0) {
return '';
}
$result = $db->query('SELECT name FROM categories WHERE id = ' . $categoryId . ' LIMIT 1');
if (!$result || $result->num_rows === 0) {
return '';
}
$row = $result->fetch_assoc();
return strtolower((string)($row['name'] ?? ''));
}
private function countStandaloneOrderItems(int $orderId): int
{
global $db;
$result = $db->query(
'SELECT COUNT(*) AS item_count
FROM order_items
WHERE order_id = ' . $orderId . '
AND deleted_at IS NULL
AND (related_item_id IS NULL OR related_item_id = 0)'
);
if (!$result) {
return 0;
}
$row = $result->fetch_assoc();
return (int)($row['item_count'] ?? 0);
}
}
+1 -8
View File
@@ -82,14 +82,7 @@ class db
public function close(): void public function close(): void
{ {
if (!isset($this->conn)) { $this->conn->close();
return;
}
try {
$this->conn->close();
} catch (\Throwable) {
}
} }
public function get(string $table, int $id) public function get(string $table, int $id)
@@ -76,13 +76,11 @@ class department_daily_report_complaints_schema_bootstrap
dimension INT NOT NULL DEFAULT 0, dimension INT NOT NULL DEFAULT 0,
branding INT NOT NULL DEFAULT 0, branding INT NOT NULL DEFAULT 0,
visible TINYINT(1) NOT NULL DEFAULT 1, visible TINYINT(1) NOT NULL DEFAULT 1,
archived TINYINT(1) NOT NULL DEFAULT 0,
longitude DECIMAL(10,7) NOT NULL DEFAULT 0, longitude DECIMAL(10,7) NOT NULL DEFAULT 0,
latitude DECIMAL(10,7) NOT NULL DEFAULT 0, latitude DECIMAL(10,7) NOT NULL DEFAULT 0,
order_priority INT NOT NULL DEFAULT 0, order_priority INT NOT NULL DEFAULT 0,
created_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP, created_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, updated_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
KEY idx_departments_archived (archived)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
); );
@@ -1,97 +0,0 @@
<?php
namespace classes;
/**
* Ensures additive schema for department lifecycle metadata.
*/
class departments_schema_bootstrap
{
private static bool $initialized = false;
private const ARCHIVED_INDEX = 'idx_departments_archived';
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
return;
}
if (!self::tableExists($db, 'departments')) {
return;
}
if (!self::columnExists($db, 'departments', 'archived')) {
$db->query(
"ALTER TABLE departments
ADD COLUMN archived TINYINT(1) NOT NULL DEFAULT 0
AFTER visible"
);
}
if (!self::columnExists($db, 'departments', 'custom_pricing_only')) {
$db->query(
"ALTER TABLE departments
ADD COLUMN custom_pricing_only TINYINT(1) NOT NULL DEFAULT 0
AFTER archived"
);
}
if (!self::indexExists($db, 'departments', self::ARCHIVED_INDEX)) {
$db->query(
"ALTER TABLE departments
ADD INDEX " . self::ARCHIVED_INDEX . " (archived)"
);
}
self::$initialized = true;
}
private static function tableExists(object $db, string $table): bool
{
$table = self::escapeIdentifier($table);
$result = $db->query("SHOW TABLES LIKE '{$table}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function columnExists(object $db, string $table, string $column): bool
{
$table = self::escapeIdentifier($table);
$column = self::escapeIdentifier($column);
$result = $db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function indexExists(object $db, string $table, string $index): bool
{
$table = self::escapeIdentifier($table);
$index = self::escapeIdentifier($index);
$result = $db->query("SHOW INDEX FROM `{$table}` WHERE Key_name = '{$index}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function escapeIdentifier(string $value): string
{
return str_replace(['\\', "'", '`'], ['\\\\', "\\'", ''], $value);
}
}
+3 -97
View File
@@ -30,7 +30,6 @@ use interfaces\economic_i;
class economic implements economic_i class economic implements economic_i
{ {
public const DRAFT_CUSTOMER_EXPORT_BLOCKED_MESSAGE = 'Transactions for the configured draft customer cannot be exported to e-conomic.'; public const DRAFT_CUSTOMER_EXPORT_BLOCKED_MESSAGE = 'Transactions for the configured draft customer cannot be exported to e-conomic.';
public const DEFAULT_DISTRIBUTION_DEPARTMENT_ID = 1;
/** /**
* Configuration of the economic module * Configuration of the economic module
@@ -134,14 +133,6 @@ class economic implements economic_i
return $customer_number > 0 ? $customer_number : null; return $customer_number > 0 ? $customer_number : null;
} }
public function getDefaultDistributionDepartmentId(): int
{
$value = $this->config->default_department_id->getVariableValue();
$department_id = (int)$value;
return $department_id > 0 ? $department_id : self::DEFAULT_DISTRIBUTION_DEPARTMENT_ID;
}
public function isDraftCustomerNumber(?int $customer_number): bool public function isDraftCustomerNumber(?int $customer_number): bool
{ {
$configured_customer_number = $this->getTransactionDraftCustomerNumber(); $configured_customer_number = $this->getTransactionDraftCustomerNumber();
@@ -165,18 +156,9 @@ class economic implements economic_i
/** /**
* Create a customer in e-conomic and return the raw upstream payload. * Create a customer in e-conomic and return the raw upstream payload.
*/ */
public function createCustomer( public function createCustomer(int $customer_number, string $name, int $cvr_number, string $email, int $phone): object
int $customer_number,
string $name,
int $cvr_number,
string $email,
int $phone,
?int $mobile_phone = null,
object|array|null $company_information = null,
?string $ean = null
): object
{ {
$payload = [ return $this->customers->customers->create([
'customerNumber' => $customer_number, 'customerNumber' => $customer_number,
'corporateIdentificationNumber' => (string)$cvr_number, 'corporateIdentificationNumber' => (string)$cvr_number,
'customerGroup' => [ 'customerGroup' => [
@@ -188,87 +170,11 @@ class economic implements economic_i
'name' => $name, 'name' => $name,
'email' => $email, 'email' => $email,
'phone' => $phone, 'phone' => $phone,
'telephoneAndFaxNumber' => (string)$phone,
'mobilePhone' => (string)($mobile_phone ?? $phone),
'currency' => 'DKK', 'currency' => 'DKK',
'vatZone' => [ 'vatZone' => [
'vatZoneNumber' => 1, 'vatZoneNumber' => 1,
] ]
]; ]);
$payload = array_replace($payload, $this->buildCustomerPayloadFromCompanyInformation($company_information));
$normalized_ean = self::normalizeCustomerEan($ean);
if ($normalized_ean !== null) {
$payload['ean'] = $normalized_ean;
}
return $this->customers->customers->create($payload);
}
public static function normalizeCustomerEan(mixed $value): ?string
{
if ($value === null) {
return null;
}
$digits = preg_replace('/\D+/', '', (string)$value);
if (!is_string($digits)) {
return null;
}
$digits = trim($digits);
if ($digits === '') {
return null;
}
if (strlen($digits) > 13) {
throw new \InvalidArgumentException('EAN must be at most 13 digits.');
}
return $digits;
}
private function buildCustomerPayloadFromCompanyInformation(object|array|null $company_information): array
{
if ($company_information === null) {
return [];
}
$payload = [];
$field_map = [
'address' => 'address',
'zipcode' => 'zip',
'city' => 'city',
'website' => 'website',
];
foreach ($field_map as $source_field => $economic_field) {
$value = $this->companyInformationValue($company_information, $source_field);
if ($value === null) {
continue;
}
$payload[$economic_field] = $value;
}
return $payload;
}
private function companyInformationValue(object|array $company_information, string $field): ?string
{
if (is_array($company_information)) {
$value = $company_information[$field] ?? null;
} else {
$value = $company_information->{$field} ?? null;
}
if ($value === null) {
return null;
}
$normalized = trim((string)$value);
return $normalized !== '' ? $normalized : null;
} }
/** /**
@@ -240,13 +240,7 @@ class economic_transfer_executor
'Queued transfer processed successfully for collected invoice #' . $collected_invoice_id 'Queued transfer processed successfully for collected invoice #' . $collected_invoice_id
); );
$result = $collected_order_invoices->asArray(); return $collected_order_invoices->asArray();
$transfer_metrics = $collected_order_invoices->getLastEconomicTransferMetrics();
if ($transfer_metrics !== null) {
$result['economic_transfer_metrics'] = $transfer_metrics;
}
return $result;
} }
/** /**
@@ -41,7 +41,7 @@ class economic_transfer_queue
$transfer_type = $this->validateTransferType($transfer_type); $transfer_type = $this->validateTransferType($transfer_type);
$payload = $this->normalizePayloadForTransferType($transfer_type, $payload, $created_by); $payload = $this->normalizePayloadForTransferType($transfer_type, $payload, $created_by);
$active_job = $this->findActiveJobByTarget($transfer_type, $payload, $created_by); $active_job = $this->findActiveJobByTarget($transfer_type, $payload);
if ($active_job !== null) { if ($active_job !== null) {
$target_label = $this->buildTargetLabel($transfer_type, $payload); $target_label = $this->buildTargetLabel($transfer_type, $payload);
$this->logQueueEvent( $this->logQueueEvent(
@@ -135,89 +135,6 @@ class economic_transfer_queue
return $jobs; return $jobs;
} }
public function getJobByIdForUser(int $job_id, int $created_by): ?array
{
global $db;
$job_id = max(0, $job_id);
$created_by = max(0, $created_by);
if ($job_id < 1 || $created_by < 1) {
return null;
}
$stmt = $db->prepare("SELECT * FROM economic_transfer_queue_jobs WHERE id = ? AND created_by = ? LIMIT 1");
if (!$stmt) {
return null;
}
$stmt->bind_param('ii', $job_id, $created_by);
if (!$stmt->execute()) {
$stmt->close();
return null;
}
$result = $stmt->get_result();
$row = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
$stmt->close();
if (!$row) {
return null;
}
return $this->normalizeJobRow($row);
}
public function listJobsForCreatedBy(array $statuses = [], int $limit = 50, int $offset = 0, ?string $transfer_type = null, int $created_by = 0): array
{
global $db;
$created_by = max(0, $created_by);
if ($created_by < 1) {
return [];
}
$limit = max(1, min(500, $limit));
$offset = max(0, $offset);
$where = $this->buildListJobsWhereClause($statuses, $transfer_type);
$where .= $where === '' ? 'WHERE created_by = ' . $created_by : ' AND created_by = ' . $created_by;
$sql = "SELECT * FROM economic_transfer_queue_jobs $where ORDER BY id DESC LIMIT $limit OFFSET $offset";
$result = $db->query($sql);
if (!$result instanceof mysqli_result) {
return [];
}
$jobs = [];
while ($row = $result->fetch_assoc()) {
$jobs[] = $this->normalizeJobRow($row);
}
return $jobs;
}
public function countJobsForCreatedBy(array $statuses = [], ?string $transfer_type = null, int $created_by = 0): int
{
global $db;
$created_by = max(0, $created_by);
if ($created_by < 1) {
return 0;
}
$where = $this->buildListJobsWhereClause($statuses, $transfer_type);
$where .= $where === '' ? 'WHERE created_by = ' . $created_by : ' AND created_by = ' . $created_by;
$sql = "SELECT COUNT(*) AS total FROM economic_transfer_queue_jobs $where";
$result = $db->query($sql);
if (!$result instanceof mysqli_result) {
return 0;
}
$row = $result->fetch_assoc();
if (!is_array($row) || !isset($row['total'])) {
return 0;
}
return max(0, (int)$row['total']);
}
public function countJobs(array $statuses = [], ?string $transfer_type = null): int public function countJobs(array $statuses = [], ?string $transfer_type = null): int
{ {
global $db; global $db;
@@ -237,155 +154,14 @@ class economic_transfer_queue
return max(0, (int)$row['total']); return max(0, (int)$row['total']);
} }
public function listMonitorJobsForUser(int $user_id, int $limit = 50, ?string $transfer_type = null): array
{
global $db;
$user_id = max(0, $user_id);
$limit = max(1, min(100, $limit));
try {
$normalized_transfer_type = $transfer_type !== null && trim($transfer_type) !== ''
? $this->validateTransferType($transfer_type)
: null;
} catch (Exception) {
return [];
}
$transfer_condition = '';
if ($normalized_transfer_type !== null) {
$transfer_condition = "AND q.transfer_type = '" . $db->escape_string($normalized_transfer_type) . "'";
}
$sql = "SELECT q.*
FROM economic_transfer_queue_jobs q
LEFT JOIN economic_transfer_queue_job_dismissals d
ON d.queue_job_id = q.id
AND d.user_id = $user_id
AND d.dismissed_status = q.status
WHERE q.created_by = $user_id
$transfer_condition
AND (
q.status IN ('" . self::STATUS_QUEUED . "', '" . self::STATUS_PROCESSING . "')
OR d.queue_job_id IS NULL
)
ORDER BY
CASE WHEN q.status IN ('" . self::STATUS_QUEUED . "', '" . self::STATUS_PROCESSING . "') THEN 0 ELSE 1 END,
q.id DESC
LIMIT $limit";
$result = $db->query($sql);
if (!$result instanceof mysqli_result) {
return [];
}
$jobs = [];
while ($row = $result->fetch_assoc()) {
$jobs[] = $this->normalizeJobRow($row);
}
return $jobs;
}
/**
* @throws Exception
*/
public function dismissTerminalJobForUser(int $job_id, int $user_id): array
{
global $db;
$job_id = max(0, $job_id);
$user_id = max(0, $user_id);
if ($job_id < 1 || $user_id < 1) {
throw new Exception('Queue job and user are required');
}
$job = $this->getJobByIdForUser($job_id, $user_id);
if ($job === null) {
throw new Exception('Queue job not found');
}
$status = strtoupper((string)($job['status'] ?? ''));
if (!in_array($status, [self::STATUS_COMPLETED, self::STATUS_FAILED], true)) {
throw new Exception('Only completed or failed queue jobs can be dismissed');
}
$stmt = $db->prepare(
"INSERT INTO economic_transfer_queue_job_dismissals (queue_job_id, user_id, dismissed_status, dismissed_at)
VALUES (?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE dismissed_status = VALUES(dismissed_status), dismissed_at = NOW()"
);
if (!$stmt) {
throw new Exception('Failed to prepare queue dismissal statement');
}
$stmt->bind_param('iis', $job_id, $user_id, $status);
if (!$stmt->execute()) {
$stmt->close();
throw new Exception('Failed to dismiss queue job');
}
$stmt->close();
return $job;
}
public function dismissTerminalJobsForUser(int $user_id, ?string $transfer_type = null): int
{
global $db;
$user_id = max(0, $user_id);
if ($user_id < 1) {
return 0;
}
try {
$normalized_transfer_type = $transfer_type !== null && trim($transfer_type) !== ''
? $this->validateTransferType($transfer_type)
: null;
} catch (Exception) {
return 0;
}
$transfer_condition = '';
if ($normalized_transfer_type !== null) {
$transfer_condition = "AND q.transfer_type = '" . $db->escape_string($normalized_transfer_type) . "'";
}
$sql = "INSERT INTO economic_transfer_queue_job_dismissals (queue_job_id, user_id, dismissed_status, dismissed_at)
SELECT q.id, $user_id, q.status, NOW()
FROM economic_transfer_queue_jobs q
LEFT JOIN economic_transfer_queue_job_dismissals d
ON d.queue_job_id = q.id
AND d.user_id = $user_id
AND d.dismissed_status = q.status
WHERE q.created_by = $user_id
AND q.status IN ('" . self::STATUS_COMPLETED . "', '" . self::STATUS_FAILED . "')
$transfer_condition
AND d.queue_job_id IS NULL
ON DUPLICATE KEY UPDATE dismissed_status = VALUES(dismissed_status), dismissed_at = NOW()";
$db->query($sql);
return max(0, (int)($db->affected_rows ?? 0));
}
/** /**
* @throws Exception * @throws Exception
*/ */
public function retryJob(int $job_id): array public function retryJob(int $job_id): array
{
return $this->retryJobInternal($job_id);
}
public function retryJobForUser(int $job_id, int $created_by): array
{
return $this->retryJobInternal($job_id, $created_by);
}
private function retryJobInternal(int $job_id, ?int $created_by = null): array
{ {
global $db; global $db;
$job_id = max(0, $job_id); $existing_job = $this->getJobById($job_id);
$created_by = $created_by === null ? null : max(0, $created_by);
$existing_job = $created_by === null
? $this->getJobById($job_id)
: $this->getJobByIdForUser($job_id, $created_by);
if ($existing_job === null) { if ($existing_job === null) {
throw new Exception('Queue job not found'); throw new Exception('Queue job not found');
} }
@@ -396,26 +172,19 @@ class economic_transfer_queue
throw new Exception('Queue job reached max retry attempts'); throw new Exception('Queue job reached max retry attempts');
} }
$sql = "UPDATE economic_transfer_queue_jobs $stmt = $db->prepare(
"UPDATE economic_transfer_queue_jobs
SET status = ?, progress_percent = 0, progress_message = 'Queued for retry', SET status = ?, progress_percent = 0, progress_message = 'Queued for retry',
error_message = NULL, result_json = NULL, started_at = NULL, completed_at = NULL, locked_at = NULL error_message = NULL, result_json = NULL, started_at = NULL, completed_at = NULL, locked_at = NULL
WHERE id = ? AND status = ?"; WHERE id = ? AND status = ?"
if ($created_by !== null) { );
$sql .= " AND created_by = ?";
}
$stmt = $db->prepare($sql);
if (!$stmt) { if (!$stmt) {
throw new Exception('Failed to prepare retry statement'); throw new Exception('Failed to prepare retry statement');
} }
$queued = self::STATUS_QUEUED; $queued = self::STATUS_QUEUED;
$failed = self::STATUS_FAILED; $failed = self::STATUS_FAILED;
if ($created_by !== null) { $stmt->bind_param('sis', $queued, $job_id, $failed);
$stmt->bind_param('sisi', $queued, $job_id, $failed, $created_by);
} else {
$stmt->bind_param('sis', $queued, $job_id, $failed);
}
$stmt->execute(); $stmt->execute();
$affected = $stmt->affected_rows; $affected = $stmt->affected_rows;
$stmt->close(); $stmt->close();
@@ -424,11 +193,7 @@ class economic_transfer_queue
throw new Exception('Failed to retry queue job'); throw new Exception('Failed to retry queue job');
} }
$this->clearDismissalsForJob($job_id); $job = $this->getJobById($job_id);
$job = $created_by === null
? $this->getJobById($job_id)
: $this->getJobByIdForUser($job_id, $created_by);
if ($job === null) { if ($job === null) {
throw new Exception('Retry updated job could not be loaded'); throw new Exception('Retry updated job could not be loaded');
} }
@@ -715,18 +480,6 @@ class economic_transfer_queue
]; ];
} }
private function clearDismissalsForJob(int $job_id): void
{
global $db;
$job_id = max(0, $job_id);
if ($job_id < 1) {
return;
}
$db->query("DELETE FROM economic_transfer_queue_job_dismissals WHERE queue_job_id = $job_id");
}
/** /**
* Release jobs stuck in PROCESSING due to crashes or killed workers. * Release jobs stuck in PROCESSING due to crashes or killed workers.
*/ */
@@ -817,31 +570,28 @@ class economic_transfer_queue
return $this->rejectPayload($created_by, $field_name . ' must be a boolean'); return $this->rejectPayload($created_by, $field_name . ' must be a boolean');
} }
private function findActiveJobByTarget(string $transfer_type, array $payload, int $created_by): ?array private function findActiveJobByTarget(string $transfer_type, array $payload): ?array
{ {
return match ($transfer_type) { return match ($transfer_type) {
self::TYPE_ORDER_DRAFT_EXPORT, self::TYPE_ORDER_INVOICE_EXPORT => $this->findActiveJobByJsonNumericTarget( self::TYPE_ORDER_DRAFT_EXPORT, self::TYPE_ORDER_INVOICE_EXPORT => $this->findActiveJobByJsonNumericTarget(
$transfer_type, $transfer_type,
'$.order_id', '$.order_id',
(int)($payload['order_id'] ?? 0), (int)($payload['order_id'] ?? 0)
$created_by
), ),
self::TYPE_COLLECTED_INVOICE_EXPORT => $this->findActiveJobByJsonNumericTarget( self::TYPE_COLLECTED_INVOICE_EXPORT => $this->findActiveJobByJsonNumericTarget(
$transfer_type, $transfer_type,
'$.collected_invoice_id', '$.collected_invoice_id',
(int)($payload['collected_invoice_id'] ?? 0), (int)($payload['collected_invoice_id'] ?? 0)
$created_by
), ),
default => null, default => null,
}; };
} }
private function findActiveJobByJsonNumericTarget(string $transfer_type, string $json_path, int $target_value, int $created_by): ?array private function findActiveJobByJsonNumericTarget(string $transfer_type, string $json_path, int $target_value): ?array
{ {
global $db; global $db;
$created_by = max(0, $created_by); if ($target_value < 1) {
if ($target_value < 1 || $created_by < 1) {
return null; return null;
} }
@@ -851,7 +601,6 @@ class economic_transfer_queue
WHERE transfer_type = ? WHERE transfer_type = ?
AND status IN (?, ?) AND status IN (?, ?)
AND CAST(JSON_UNQUOTE(JSON_EXTRACT(payload_json, '$json_path')) AS UNSIGNED) = ? AND CAST(JSON_UNQUOTE(JSON_EXTRACT(payload_json, '$json_path')) AS UNSIGNED) = ?
AND created_by = ?
ORDER BY id DESC ORDER BY id DESC
LIMIT 1" LIMIT 1"
); );
@@ -861,7 +610,7 @@ class economic_transfer_queue
$queued = self::STATUS_QUEUED; $queued = self::STATUS_QUEUED;
$processing = self::STATUS_PROCESSING; $processing = self::STATUS_PROCESSING;
$stmt->bind_param('sssii', $transfer_type, $queued, $processing, $target_value, $created_by); $stmt->bind_param('sssi', $transfer_type, $queued, $processing, $target_value);
if (!$stmt->execute()) { if (!$stmt->execute()) {
$stmt->close(); $stmt->close();
return null; return null;
@@ -27,20 +27,12 @@ class economic_transfer_queue_details_summary
'customer_number' => self::toPositiveInt( 'customer_number' => self::toPositiveInt(
$result['customer_number'] $result['customer_number']
?? $result['user']['customer_number'] ?? $result['user']['customer_number']
?? $payload['customer_number']
?? $payload['customer']['customer_number']
?? null ?? null
), ),
'name' => self::toNonEmptyString( 'name' => self::toNonEmptyString(
$result['customer_name'] $result['customer_name']
?? $result['user']['customer_name']
?? $result['user']['display_name']
?? $result['user']['name'] ?? $result['user']['name']
?? $result['user']['company_name'] ?? $result['user']['company_name']
?? $payload['customer_name']
?? $payload['customer']['customer_name']
?? $payload['customer']['display_name']
?? $payload['customer']['name']
?? null ?? null
), ),
], ],
@@ -42,18 +42,6 @@ class economic_transfer_queue_schema_bootstrap
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
); );
$db->query(
"CREATE TABLE IF NOT EXISTS economic_transfer_queue_job_dismissals (
queue_job_id BIGINT UNSIGNED NOT NULL,
user_id INT NOT NULL,
dismissed_status VARCHAR(32) NOT NULL,
dismissed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (queue_job_id, user_id),
INDEX idx_economic_transfer_queue_job_dismissals_user_status (user_id, dismissed_status),
INDEX idx_economic_transfer_queue_job_dismissals_job (queue_job_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
self::$initialized = true; self::$initialized = true;
} }
} }
@@ -389,15 +389,6 @@ class economic_v2_distribution_service
} }
$discount_row = $this->resolveDiscountForProduct($customer_number, $product_id, $created_at); $discount_row = $this->resolveDiscountForProduct($customer_number, $product_id, $created_at);
if ($discount_row === null) {
continue;
}
if (array_key_exists('fixed_price', $discount_row) && $discount_row['fixed_price'] !== null) {
$fixed_price = (float)$discount_row['fixed_price'];
$order_discount_total += (($base_price - $fixed_price) * $quantity);
continue;
}
$discount_percentage = (float)($discount_row['discount'] ?? 0); $discount_percentage = (float)($discount_row['discount'] ?? 0);
if ($discount_percentage <= 0) { if ($discount_percentage <= 0) {
continue; continue;
@@ -901,7 +892,6 @@ class economic_v2_distribution_service
$weight_total = array_sum($eligible_weights); $weight_total = array_sum($eligible_weights);
if (abs($weight_total) <= self::EPSILON) { if (abs($weight_total) <= self::EPSILON) {
$fallback_department_id = $this->getFallbackDistributionDepartmentId();
$warnings[] = 'Booked department 75 ' $warnings[] = 'Booked department 75 '
. $source_category . $source_category
. ' amount for customer ' . ' amount for customer '
@@ -910,15 +900,11 @@ class economic_v2_distribution_service
. $month_key . $month_key
. ' on invoice(s) ' . ' on invoice(s) '
. $invoice_ids . $invoice_ids
. ' has no redistribution basis and was assigned to fallback department ' . ' has no redistribution basis and remains undistributed.';
. $fallback_department_id
. '.';
return [ return [
'department_distribution' => [ 'department_distribution' => [],
$fallback_department_id => $booked_amount, 'undistributed_net_amount' => $booked_amount,
],
'undistributed_net_amount' => 0.0,
]; ];
} }
@@ -1075,9 +1061,6 @@ class economic_v2_distribution_service
} elseif (!$this->isOrderEligible($order)) { } elseif (!$this->isOrderEligible($order)) {
continue; continue;
} }
$distribution_department_id = $system_order_fallback
? $this->getFallbackDistributionDepartmentId()
: $department_id;
$fixed_version = $this->versioning->resolveFixedPricingVersionAt($customer_number, $created_at); $fixed_version = $this->versioning->resolveFixedPricingVersionAt($customer_number, $created_at);
if ($fixed_version === null) { if ($fixed_version === null) {
@@ -1107,14 +1090,14 @@ class economic_v2_distribution_service
$order_original_price = $this->calculateOrderOriginalPrice( $order_original_price = $this->calculateOrderOriginalPrice(
$order_items_by_order_id[$order_id] ?? [], $order_items_by_order_id[$order_id] ?? [],
$customer_number, $customer_number,
$distribution_department_id, $department_id,
$created_at $created_at
); );
$groups[$group_key]['original_price'] += $order_original_price; $groups[$group_key]['original_price'] += $order_original_price;
if (!isset($groups[$group_key]['department_totals'][$distribution_department_id])) { if (!isset($groups[$group_key]['department_totals'][$department_id])) {
$groups[$group_key]['department_totals'][$distribution_department_id] = 0.0; $groups[$group_key]['department_totals'][$department_id] = 0.0;
} }
$groups[$group_key]['department_totals'][$distribution_department_id] += $order_original_price; $groups[$group_key]['department_totals'][$department_id] += $order_original_price;
$groups[$group_key]['order_ids'][] = $order_id; $groups[$group_key]['order_ids'][] = $order_id;
if (!isset($customer_transactions[$customer_number][$order_id])) { if (!isset($customer_transactions[$customer_number][$order_id])) {
@@ -1158,15 +1141,12 @@ class economic_v2_distribution_service
} elseif (!$this->isOrderEligible($order)) { } elseif (!$this->isOrderEligible($order)) {
continue; continue;
} }
$distribution_department_id = $system_order_fallback
? $this->getFallbackDistributionDepartmentId()
: $department_id;
$month_key = substr($created_at, 0, 7); $month_key = substr($created_at, 0, 7);
if (!isset($customer_department_month_map[$customer_number][$month_key][$distribution_department_id])) { if (!isset($customer_department_month_map[$customer_number][$month_key][$department_id])) {
$customer_department_month_map[$customer_number][$month_key][$distribution_department_id] = 0; $customer_department_month_map[$customer_number][$month_key][$department_id] = 0;
} }
$customer_department_month_map[$customer_number][$month_key][$distribution_department_id]++; $customer_department_month_map[$customer_number][$month_key][$department_id]++;
$candidates = $this->buildWashSubscriptionCandidates( $candidates = $this->buildWashSubscriptionCandidates(
$order, $order,
@@ -1213,10 +1193,10 @@ class economic_v2_distribution_service
]; ];
} }
if (!isset($groups[$group_key]['distribution'][$distribution_department_id])) { if (!isset($groups[$group_key]['distribution'][$department_id])) {
$groups[$group_key]['distribution'][$distribution_department_id] = 0; $groups[$group_key]['distribution'][$department_id] = 0;
} }
$groups[$group_key]['distribution'][$distribution_department_id]++; $groups[$group_key]['distribution'][$department_id]++;
$groups[$group_key]['order_ids'][] = $order_id; $groups[$group_key]['order_ids'][] = $order_id;
$matched_order = true; $matched_order = true;
} }
@@ -1414,9 +1394,6 @@ class economic_v2_distribution_service
): array { ): array {
$distribution = []; $distribution = [];
$department_counts = $customer_department_month_map[$customer_number][$month_key] ?? []; $department_counts = $customer_department_month_map[$customer_number][$month_key] ?? [];
if (!empty($department_counts)) {
$department_counts = $this->normalizeFallbackDepartmentCounts($department_counts);
}
if (!empty($department_counts)) { if (!empty($department_counts)) {
$total = (float)array_sum($department_counts); $total = (float)array_sum($department_counts);
foreach ($department_counts as $department_id => $count) { foreach ($department_counts as $department_id => $count) {
@@ -1425,61 +1402,20 @@ class economic_v2_distribution_service
return $distribution; return $distribution;
} }
$customer_default_department_id = $this->getCustomerDefaultDepartmentId($customer_number); $default_department = 1;
$default_department = $customer_default_department_id !== null && $customer_default_department_id > 0 try {
? $customer_default_department_id $default = (new users_o())->getUserByCustomerNumber($customer_number)->getDefaultDepartment();
: $this->getFallbackDistributionDepartmentId(); if (!empty($default)) {
$default_department = (int)$default;
}
} catch (Exception $e) {
// fall back to department 1
}
$distribution[$default_department] = $monthly_price; $distribution[$default_department] = $monthly_price;
return $distribution; return $distribution;
} }
/**
* @param array<int|string,int|float> $department_counts
* @return array<int,int|float>
*/
private function normalizeFallbackDepartmentCounts(array $department_counts): array
{
$normalized = [];
foreach ($department_counts as $department_id => $count) {
$department_id = (int)$department_id;
if ($department_id === self::SYSTEM_ORDER_DEPARTMENT_ID || $department_id <= 0) {
$department_id = $this->getFallbackDistributionDepartmentId();
}
if (!isset($normalized[$department_id])) {
$normalized[$department_id] = 0;
}
$normalized[$department_id] += $count;
}
return $normalized;
}
protected function getCustomerDefaultDepartmentId(int $customer_number): ?int
{
try {
$default = (new users_o())->getUserByCustomerNumber($customer_number)->getDefaultDepartment();
return !empty($default) ? (int)$default : null;
} catch (Exception $e) {
return null;
}
}
protected function getFallbackDistributionDepartmentId(): int
{
try {
$department_id = (new economic())->getDefaultDistributionDepartmentId();
} catch (\Throwable $e) {
$department_id = economic::DEFAULT_DISTRIBUTION_DEPARTMENT_ID;
}
if ($department_id <= 0 || $department_id === self::SYSTEM_ORDER_DEPARTMENT_ID) {
return economic::DEFAULT_DISTRIBUTION_DEPARTMENT_ID;
}
return $department_id;
}
private function normalizeSubscriptionGroupAllocation(array $distribution, float $monthly_price): array private function normalizeSubscriptionGroupAllocation(array $distribution, float $monthly_price): array
{ {
if (empty($distribution)) { if (empty($distribution)) {
@@ -1529,12 +1465,6 @@ class economic_v2_distribution_service
} }
$discount_row = $this->resolveDiscountForProduct($customer_number, $product_id, $timestamp); $discount_row = $this->resolveDiscountForProduct($customer_number, $product_id, $timestamp);
if ($discount_row !== null && array_key_exists('fixed_price', $discount_row) && $discount_row['fixed_price'] !== null) {
$line_price = ((float)$discount_row['fixed_price']) * $quantity;
$total += $line_price;
continue;
}
$discount_percentage = (float)($discount_row['discount'] ?? 0); $discount_percentage = (float)($discount_row['discount'] ?? 0);
if ($discount_percentage > 0) { if ($discount_percentage > 0) {
$line_price *= (1 - ($discount_percentage / 100)); $line_price *= (1 - ($discount_percentage / 100));
@@ -1552,10 +1482,7 @@ class economic_v2_distribution_service
} }
$direct = $this->versioning->resolveDiscountOverrideAt($customer_number, false, (string)$product_id, $timestamp); $direct = $this->versioning->resolveDiscountOverrideAt($customer_number, false, (string)$product_id, $timestamp);
if ($direct !== null && ( if ($direct !== null && (int)($direct['discount'] ?? 0) > 0) {
(array_key_exists('fixed_price', $direct) && $direct['fixed_price'] !== null)
|| (int)($direct['discount'] ?? 0) > 0
)) {
return $this->discount_resolution_cache[$cache_key] = $direct; return $this->discount_resolution_cache[$cache_key] = $direct;
} }
@@ -65,7 +65,6 @@ class economic_v2_schema_bootstrap
is_category TINYINT(1) NOT NULL, is_category TINYINT(1) NOT NULL,
object_id VARCHAR(64) NOT NULL, object_id VARCHAR(64) NOT NULL,
discount INT NOT NULL, discount INT NOT NULL,
fixed_price INT NULL DEFAULT NULL,
effective_from DATETIME NOT NULL, effective_from DATETIME NOT NULL,
effective_to DATETIME NULL, effective_to DATETIME NULL,
source VARCHAR(64) NOT NULL DEFAULT 'live', source VARCHAR(64) NOT NULL DEFAULT 'live',
@@ -84,14 +83,6 @@ class economic_v2_schema_bootstrap
$db->query($sql); $db->query($sql);
} }
if (!self::tableHasColumn('customer_discount_override_versions', 'fixed_price')) {
$db->query(
"ALTER TABLE customer_discount_override_versions
ADD COLUMN fixed_price INT NULL DEFAULT NULL
AFTER discount"
);
}
self::$initialized = true; self::$initialized = true;
} }
@@ -115,3 +106,4 @@ class economic_v2_schema_bootstrap
return ((int)($row['c'] ?? 0)) > 0; return ((int)($row['c'] ?? 0)) > 0;
} }
} }
@@ -135,8 +135,7 @@ class economic_v2_versioning_service
string $source = 'live.discount_override', string $source = 'live.discount_override',
float $confidence = 1.0, float $confidence = 1.0,
bool $inferred = false, bool $inferred = false,
array $metadata = [], array $metadata = []
?int $fixed_price = null
): array { ): array {
$identity = [ $identity = [
'user_id' => $user_id, 'user_id' => $user_id,
@@ -145,7 +144,7 @@ class economic_v2_versioning_service
'object_id' => (string)$object_id, 'object_id' => (string)$object_id,
]; ];
if (($discount === null || (int)$discount === 0) && $fixed_price === null) { if ($discount === null || (int)$discount === 0) {
return $this->closeActiveVersion( return $this->closeActiveVersion(
'customer_discount_override_versions', 'customer_discount_override_versions',
$identity, $identity,
@@ -162,7 +161,6 @@ class economic_v2_versioning_service
$identity, $identity,
[ [
'discount' => (int)$discount, 'discount' => (int)$discount,
'fixed_price' => $is_category ? null : $fixed_price,
], ],
$this->normalizeDatetime($effective_from), $this->normalizeDatetime($effective_from),
$source, $source,
@@ -413,11 +411,8 @@ class economic_v2_versioning_service
} }
// Discount overrides current state. // Discount overrides current state.
price_overrides_schema_bootstrap::ensureColumns();
$has_override_created_at = economic_v2_schema_bootstrap::tableHasColumn('price_overrides', 'created_at'); $has_override_created_at = economic_v2_schema_bootstrap::tableHasColumn('price_overrides', 'created_at');
$has_override_fixed_price = economic_v2_schema_bootstrap::tableHasColumn('price_overrides', 'fixed_price');
$discount_cols = 'po.user_id, u.customer_number, po.is_category, po.product_or_category_id, po.percentage' . $discount_cols = 'po.user_id, u.customer_number, po.is_category, po.product_or_category_id, po.percentage' .
($has_override_fixed_price ? ', po.fixed_price' : '') .
($has_override_created_at ? ', po.created_at' : ''); ($has_override_created_at ? ', po.created_at' : '');
$discount_rows = $this->fetchAll( $discount_rows = $this->fetchAll(
"SELECT $discount_cols "SELECT $discount_cols
@@ -439,8 +434,7 @@ class economic_v2_versioning_service
'backfill.current_discount_override', 'backfill.current_discount_override',
$confidence, $confidence,
true, true,
['table' => 'price_overrides'], ['table' => 'price_overrides']
$has_override_fixed_price && $row['fixed_price'] !== null ? (int)$row['fixed_price'] : null
); );
$this->incrementReportAction($report['discount_overrides'], $result['action'] ?? 'noop'); $this->incrementReportAction($report['discount_overrides'], $result['action'] ?? 'noop');
} }
@@ -713,3 +707,4 @@ class economic_v2_versioning_service
$bucket[$action]++; $bucket[$action]++;
} }
} }
@@ -1,150 +0,0 @@
<?php
namespace classes;
use Exception;
class edge_broker_transport_exception extends Exception
{
public function __construct(string $message, private readonly int $curlErrno = 0, int $code = 0, ?Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
public function curlErrno(): int
{
return $this->curlErrno;
}
}
class edge_broker_http_exception extends Exception
{
public function __construct(string $message, private readonly int $statusCode, int $code = 0, ?Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
public function statusCode(): int
{
return $this->statusCode;
}
}
class edge_broker_client
{
private const DEFAULT_BROKER_URL = 'http://edge-broker:4300';
public function __construct(
private readonly ?string $baseUrl = null,
private readonly ?string $sharedSecret = null,
private readonly int $timeoutSeconds = 10
) {
}
public function isConfigured(): bool
{
return trim((string)$this->resolveBaseUrl()) !== '';
}
public function dispatchCommand(int $gatewayId, string $commandType, array $payload): array
{
$url = rtrim($this->resolveBaseUrl(), '/') . '/api/gateways/' . $gatewayId . '/commands';
$response = $this->request('POST', $url, [
'commandType' => $commandType,
'payload' => $payload,
]);
return is_array($response) ? $response : ['ok' => false, 'response' => $response];
}
public function validateAgent(int $gatewayId, string $agentToken): array
{
$url = rtrim($this->resolveBaseUrl(), '/') . '/api/internal/agent/auth';
$response = $this->request('POST', $url, [
'gatewayId' => $gatewayId,
'agentToken' => $agentToken,
]);
return is_array($response) ? $response : [];
}
public function validateShellSession(string $sessionToken): array
{
$url = rtrim($this->resolveBaseUrl(), '/') . '/api/internal/shell/auth';
$response = $this->request('POST', $url, [
'sessionToken' => $sessionToken,
]);
return is_array($response) ? $response : [];
}
public function closeShellSession(int $sessionId, string $sessionToken, string $transcript, string $closedReason): array
{
$url = rtrim($this->resolveBaseUrl(), '/') . '/api/internal/shell-sessions/' . $sessionId . '/close';
$response = $this->request('POST', $url, [
'sessionToken' => $sessionToken,
'transcript' => $transcript,
'closedReason' => $closedReason,
]);
return is_array($response) ? $response : [];
}
private function resolveBaseUrl(): string
{
return trim((string)($this->baseUrl ?? getenv('EDGE_BROKER_URL') ?: self::DEFAULT_BROKER_URL));
}
private function resolveSharedSecret(): string
{
return trim((string)($this->sharedSecret
?? getenv('EDGE_BROKER_SHARED_SECRET')
?: getenv('EDGE_INTERNAL_SECRET')
?: ''));
}
/**
* @throws Exception
*/
private function request(string $method, string $url, array $payload): array|object|null
{
if (trim($url) === '') {
throw new Exception('Edge broker URL is not configured');
}
$sharedSecret = $this->resolveSharedSecret();
if ($sharedSecret === '') {
throw new Exception('Edge broker shared secret is not configured');
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeoutSeconds);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Edge-Broker-Secret: ' . $sharedSecret,
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE));
$rawResponse = curl_exec($ch);
$statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErrno = curl_errno($ch);
$curlError = curl_error($ch);
curl_close($ch);
if ($rawResponse === false) {
throw new edge_broker_transport_exception('Edge broker request failed: ' . $curlError, $curlErrno);
}
$decoded = json_decode((string)$rawResponse, true);
if ($statusCode >= 400) {
$message = is_array($decoded)
? (string)($decoded['error'] ?? $decoded['message'] ?? 'Edge broker request failed')
: 'Edge broker request failed';
throw new edge_broker_http_exception($message, $statusCode);
}
return $decoded;
}
}
@@ -48,42 +48,4 @@ class edgegateway implements universal_module_i
$configured = trim((string)$this->config->default_update_window->getVariableValue()); $configured = trim((string)$this->config->default_update_window->getVariableValue());
return $configured !== '' ? $configured : '02:00-04:00'; return $configured !== '' ? $configured : '02:00-04:00';
} }
public function brokerUrl(): string
{
$configured = trim((string)$this->config->broker_url->getVariableValue());
if ($configured !== '') {
return rtrim($configured, '/');
}
$fallback = trim((string)(getenv('EDGE_BROKER_URL') ?: ''));
return $fallback !== '' ? rtrim($fallback, '/') : '';
}
public function publicBrokerUrl(): string
{
$configured = trim((string)$this->config->public_broker_url->getVariableValue());
if ($configured !== '') {
return rtrim($configured, '/');
}
$fallback = trim((string)(getenv('EDGE_PUBLIC_BROKER_URL') ?: ''));
return $fallback !== '' ? rtrim($fallback, '/') : '';
}
public function brokerAuthMode(): string
{
$configured = trim((string)$this->config->broker_auth_mode->getVariableValue());
return $configured !== '' ? $configured : 'manager';
}
public function brokerSharedSecret(): string
{
$configured = trim((string)$this->config->broker_shared_secret->getVariableValue());
if ($configured !== '') {
return $configured;
}
return trim((string)(getenv('EDGE_BROKER_SHARED_SECRET') ?: ''));
}
} }
+2 -111
View File
@@ -128,13 +128,13 @@ use Psr\Http\Client\ClientExceptionInterface;
private function sendEmailMailerSend(string $to, string $recipient_name, string $subject, string $message, string $html = null, string $references = null, array $attachments = []): void private function sendEmailMailerSend(string $to, string $recipient_name, string $subject, string $message, string $html = null, string $references = null, array $attachments = []): void
{ {
if (self::isFakeDeliveryEnabled()) { if (self::isFakeDeliveryEnabled()) {
self::recordFakeDelivery([ self::$fake_deliveries[] = [
'to' => $to, 'to' => $to,
'recipient_name' => $recipient_name, 'recipient_name' => $recipient_name,
'subject' => $subject, 'subject' => $subject,
'message' => $message, 'message' => $message,
'html' => $html, 'html' => $html,
]); ];
return; return;
} }
@@ -225,72 +225,6 @@ use Psr\Http\Client\ClientExceptionInterface;
public static function resetFakeDeliveries(): void public static function resetFakeDeliveries(): void
{ {
self::$fake_deliveries = []; self::$fake_deliveries = [];
$path = self::getFakeDeliveriesPath();
if ($path !== null && is_file($path)) {
unlink($path);
}
}
public static function syncFakeDeliveries(): void
{
$path = self::getFakeDeliveriesPath();
if ($path === null || !is_file($path)) {
self::$fake_deliveries = [];
return;
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
self::$fake_deliveries = [];
return;
}
$deliveries = [];
foreach ($lines as $line) {
$delivery = json_decode($line, true);
if (is_array($delivery)) {
$deliveries[] = $delivery;
}
}
self::$fake_deliveries = $deliveries;
}
private static function recordFakeDelivery(array $delivery): void
{
self::$fake_deliveries[] = $delivery;
$path = self::getFakeDeliveriesPath();
if ($path === null) {
return;
}
$directory = dirname($path);
if (!is_dir($directory)) {
mkdir($directory, 0777, true);
}
file_put_contents($path, json_encode($delivery, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND | LOCK_EX);
}
private static function getFakeDeliveriesPath(): ?string
{
if (!self::isFakeDeliveryEnabled()) {
return null;
}
$configuredPath = trim((string)(getenv('EMAIL_FAKE_DELIVERIES_PATH') ?: ''));
if ($configuredPath !== '') {
return $configuredPath;
}
if (getenv('RUN_API_TESTS') !== '1') {
return null;
}
return rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR)
. DIRECTORY_SEPARATOR
. 'truckwash-email-fake-deliveries-' . md5((string)getcwd()) . '.jsonl';
} }
private static function isFakeDeliveryEnabled(): bool private static function isFakeDeliveryEnabled(): bool
@@ -577,47 +511,4 @@ use Psr\Http\Client\ClientExceptionInterface;
$this->attachments $this->attachments
); );
} }
/**
* @throws Exception
*/
public function sendNewCustomerRegistrationNotifications(int $customer_number): void
{
$customer = (new users_o())->getUserByCustomerNumber($customer_number);
if (!$customer->exists()) {
throw new Exception('Customer not found with customer number: ' . $customer_number);
}
$customerName = $customer->getCustomerName((int)$customer->customer_number->value()) ?: 'Unknown customer';
$safeCustomerName = htmlspecialchars($customerName, ENT_QUOTES, 'UTF-8');
$safeCustomerNumber = (int)$customer->customer_number->value();
$customerUrl = 'https://truckwash.io/superuser/users?search=' . $safeCustomerNumber;
$message = "
<p>A new customer has registered on truckwash.io.</p>
<p>
<strong>Customer number:</strong> $safeCustomerNumber<br>
<strong>Customer name:</strong> $safeCustomerName
</p>
<p><a href='$customerUrl'>Open customer in Superuser</a></p>
";
foreach ((new users_o())->getSuperuserNewCustomerEmailNotificationRecipients() as $recipient) {
$recipientEmail = trim((string)($recipient['email'] ?? ''));
if ($recipientEmail === '') {
continue;
}
$recipientName = trim((string)($recipient['display_name'] ?? ''));
if ($recipientName === '') {
$recipientName = $recipientEmail;
}
$this->sendEmail(
$recipientEmail,
$recipientName,
'New customer registered on Truck Wash',
$message,
);
}
}
} }
@@ -1,76 +0,0 @@
<?php
namespace classes;
class error_report_schema_bootstrap
{
private static bool $initialized = false;
private static ?bool $tablesExist = null;
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
$db->query("CREATE TABLE IF NOT EXISTS error_reports (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
status VARCHAR(16) NOT NULL DEFAULT 'open',
reporter_type VARCHAR(16) NOT NULL,
reporter_user_id INT NULL,
reporter_subuser_id INT NULL,
reporter_customer_number INT NULL,
reporter_customer_number_context INT NULL,
reporter_name VARCHAR(255) NULL,
reporter_email VARCHAR(255) NULL,
route_path VARCHAR(512) NULL,
page_url VARCHAR(1024) NULL,
release_trace_id VARCHAR(64) NULL,
frontend_version VARCHAR(128) NULL,
api_version VARCHAR(128) NULL,
screenshot_object_key VARCHAR(512) NOT NULL,
screenshot_mime_type VARCHAR(64) NOT NULL,
screenshot_size_bytes INT UNSIGNED NOT NULL DEFAULT 0,
before_error TEXT NOT NULL,
expected TEXT NOT NULL,
actual TEXT NOT NULL,
request_error_count INT UNSIGNED NOT NULL DEFAULT 0,
vue_error_count INT UNSIGNED NOT NULL DEFAULT 0,
request_errors_json LONGTEXT NULL,
vue_errors_json LONGTEXT NULL,
runtime_context_json LONGTEXT NULL,
data_collection_accepted TINYINT(1) NOT NULL DEFAULT 0,
data_collection_accepted_at DATETIME NOT NULL,
data_collection_policy_version VARCHAR(64) NOT NULL DEFAULT 'error-report-v1',
resolved_at DATETIME NULL,
resolved_by_user_id INT NULL,
resolution_note TEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_error_reports_status_created (status, created_at),
INDEX idx_error_reports_reporter_user (reporter_user_id, created_at),
INDEX idx_error_reports_reporter_subuser (reporter_subuser_id, created_at),
INDEX idx_error_reports_customer (reporter_customer_number, reporter_customer_number_context),
INDEX idx_error_reports_trace (release_trace_id),
INDEX idx_error_reports_route (route_path)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
self::$initialized = true;
self::$tablesExist = true;
}
public static function tablesExist(): bool
{
if (self::$tablesExist !== null) {
return self::$tablesExist;
}
global $db;
$result = $db->query("SHOW TABLES LIKE 'error_reports'");
self::$tablesExist = $result !== false && $result->num_rows > 0;
return self::$tablesExist;
}
}
@@ -1,640 +0,0 @@
<?php
namespace classes;
use RuntimeException;
use Throwable;
class error_report_service
{
private const ANSWER_MAX_LENGTH = 4000;
private const NOTE_MAX_LENGTH = 2000;
private const SCREENSHOT_MAX_BYTES = 6_000_000;
private const JSON_MAX_LENGTH = 200_000;
private const STATUS_OPEN = 'open';
private const STATUS_RESOLVED = 'resolved';
private bool $schemaEnsured = false;
private error_report_store $store;
public function __construct(?error_report_store $store = null)
{
$this->store = $store ?? new error_report_store();
}
public static function redactPayload(mixed $value, int $depth = 0): mixed
{
if ($depth > 8) {
return '[depth-limit]';
}
if (is_array($value)) {
$redacted = [];
$index = 0;
foreach ($value as $key => $item) {
$index++;
if ($index > 80) {
$redacted['[truncated]'] = 'More than 80 keys omitted.';
break;
}
$keyString = (string)$key;
if (preg_match('/authorization|cookie|password|passwd|secret|token|api[_-]?key|session|credential|card|cpr|ssn/i', $keyString) === 1) {
$redacted[$key] = '[redacted]';
continue;
}
$redacted[$key] = self::redactPayload($item, $depth + 1);
}
return $redacted;
}
if (is_object($value)) {
return self::redactPayload((array)$value, $depth + 1);
}
if (is_string($value) && strlen($value) > 4000) {
return substr($value, 0, 4000) . "\n... [truncated]";
}
return $value;
}
public static function decodeScreenshotDataUri(string $dataUri): array
{
if (!preg_match('/^data:(image\/(?:png|jpeg|webp));base64,([a-zA-Z0-9+\/=\r\n]+)$/', trim($dataUri), $matches)) {
throw new RuntimeException('Screenshot must be a PNG, JPEG, or WebP data URI.');
}
$contents = base64_decode(preg_replace('/\s+/', '', $matches[2]) ?? '', true);
if ($contents === false || $contents === '') {
throw new RuntimeException('Screenshot could not be decoded.');
}
if (strlen($contents) > self::SCREENSHOT_MAX_BYTES) {
throw new RuntimeException('Screenshot is too large.');
}
return [
'mime_type' => $matches[1],
'contents' => $contents,
'size_bytes' => strlen($contents),
];
}
public function createFromCurrentPrincipal(array $payload): array
{
$this->ensureSchema();
$principal = $this->resolvePrincipal();
$answers = $this->validatedAnswers($payload);
if (!$this->acceptedDataCollection($payload['data_collection_accepted'] ?? null)) {
throw new RuntimeException('Data collection acceptance is required.');
}
$context = is_array($payload['context'] ?? null) ? $payload['context'] : [];
$storedScreenshot = $this->storeOptionalScreenshot($payload['screenshot'] ?? null, $context);
$requestErrors = $this->boundedArray($payload['request_errors'] ?? ($context['request_errors'] ?? []), 25);
$vueErrors = $this->boundedArray($payload['vue_errors'] ?? ($context['vue_errors'] ?? []), 25);
$runtimeContext = $this->runtimeContext($payload, $context);
$runtimeContext['screenshot_attachment'] = [
'status' => $storedScreenshot['status'],
'attached' => $storedScreenshot['key'] !== '',
'mime_type' => $storedScreenshot['mime_type'] !== '' ? $storedScreenshot['mime_type'] : null,
'size_bytes' => (int)$storedScreenshot['size_bytes'],
];
$this->execute(
"INSERT INTO error_reports (
status,
reporter_type,
reporter_user_id,
reporter_subuser_id,
reporter_customer_number,
reporter_customer_number_context,
reporter_name,
reporter_email,
route_path,
page_url,
release_trace_id,
frontend_version,
api_version,
screenshot_object_key,
screenshot_mime_type,
screenshot_size_bytes,
before_error,
expected,
actual,
request_error_count,
vue_error_count,
request_errors_json,
vue_errors_json,
runtime_context_json,
data_collection_accepted,
data_collection_accepted_at,
data_collection_policy_version
) VALUES (
'open', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW(), ?
)",
'siiiisssssssssisssiissss',
[
$principal['type'],
$principal['user_id'],
$principal['subuser_id'],
$principal['customer_number'],
$principal['customer_number_context'],
$principal['name'],
$principal['email'],
$runtimeContext['route_path'],
$runtimeContext['page_url'],
$runtimeContext['release_trace_id'],
$runtimeContext['frontend_version'],
$runtimeContext['api_version'],
$storedScreenshot['key'],
$storedScreenshot['mime_type'],
(int)$storedScreenshot['size_bytes'],
$answers['before_error'],
$answers['expected'],
$answers['actual'],
count($requestErrors),
count($vueErrors),
$this->jsonEncodeLimited(self::redactPayload($requestErrors)),
$this->jsonEncodeLimited(self::redactPayload($vueErrors)),
$this->jsonEncodeLimited(self::redactPayload($runtimeContext)),
$runtimeContext['data_collection_policy_version'],
]
);
return $this->get($this->insertId());
}
public function list(array $filters = []): array
{
$this->ensureSchema();
$where = ['1 = 1'];
$types = '';
$params = [];
$status = $this->statusFilter($filters['status'] ?? self::STATUS_OPEN);
if ($status !== 'all') {
$where[] = 'status = ?';
$types .= 's';
$params[] = $status;
}
$search = trim((string)($filters['q'] ?? $filters['search'] ?? ''));
if ($search !== '') {
$where[] = '(route_path LIKE ? OR page_url LIKE ? OR before_error LIKE ? OR actual LIKE ? OR reporter_name LIKE ? OR reporter_email LIKE ?)';
$types .= 'ssssss';
$like = '%' . $search . '%';
array_push($params, $like, $like, $like, $like, $like, $like);
}
$limit = min(200, max(1, (int)($filters['limit'] ?? 50)));
$offset = max(0, (int)($filters['offset'] ?? 0));
$types .= 'ii';
$params[] = $limit;
$params[] = $offset;
$items = $this->selectRows(
"SELECT id, status, reporter_type, reporter_user_id, reporter_subuser_id,
reporter_customer_number, reporter_customer_number_context, reporter_name, reporter_email,
route_path, page_url, release_trace_id, frontend_version, api_version,
screenshot_mime_type, screenshot_size_bytes, before_error, expected, actual,
request_error_count, vue_error_count, resolved_at, resolved_by_user_id, created_at, updated_at
FROM error_reports
WHERE " . implode(' AND ', $where) . "
ORDER BY created_at DESC
LIMIT ? OFFSET ?",
$types,
$params
);
return [
'items' => array_map(fn(array $row): array => $this->publicReport($row, false), $items),
'counts' => $this->counts(),
'limit' => $limit,
'offset' => $offset,
];
}
public function get(int $id): array
{
$this->ensureSchema();
$row = $this->selectOne('SELECT * FROM error_reports WHERE id = ? LIMIT 1', 'i', [$id]);
if ($row === null) {
throw new RuntimeException('Error report not found.');
}
return $this->publicReport($row, true);
}
public function updateStatus(int $id, string $status, ?string $resolutionNote, ?int $actorUserId): array
{
$this->ensureSchema();
$status = self::normalizeStatus($status);
$note = $resolutionNote !== null ? $this->trimmedString($resolutionNote, self::NOTE_MAX_LENGTH, false) : null;
if ($status === self::STATUS_RESOLVED) {
$this->execute(
'UPDATE error_reports SET status = ?, resolved_at = NOW(), resolved_by_user_id = ?, resolution_note = ? WHERE id = ?',
'sisi',
[$status, $actorUserId, $note, $id]
);
} else {
$this->execute(
'UPDATE error_reports SET status = ?, resolved_at = NULL, resolved_by_user_id = NULL, resolution_note = ? WHERE id = ?',
'ssi',
[$status, $note, $id]
);
}
return $this->get($id);
}
public static function normalizeStatus(string $status): string
{
$status = strtolower(trim($status));
if (!in_array($status, [self::STATUS_OPEN, self::STATUS_RESOLVED], true)) {
throw new RuntimeException('Invalid error report status.');
}
return $status;
}
private function validatedAnswers(array $payload): array
{
return [
'before_error' => $this->requiredAnswer($payload, ['before_error', 'what_were_you_doing_before_error_occurred']),
'expected' => $this->requiredAnswer($payload, ['expected', 'what_did_you_expect_would_happen']),
'actual' => $this->requiredAnswer($payload, ['actual', 'what_actually_happened']),
];
}
private function requiredAnswer(array $payload, array $keys): string
{
foreach ($keys as $key) {
if (array_key_exists($key, $payload)) {
return $this->trimmedString((string)$payload[$key], self::ANSWER_MAX_LENGTH, true);
}
}
throw new RuntimeException('Missing required answer.');
}
private function trimmedString(string $value, int $maxLength, bool $required): string
{
$value = trim($value);
if ($required && $value === '') {
throw new RuntimeException('Required text fields must not be empty.');
}
if (strlen($value) > $maxLength) {
return substr($value, 0, $maxLength);
}
return $value;
}
private function acceptedDataCollection(mixed $value): bool
{
return $value === true || $value === 1 || $value === '1' || $value === 'true';
}
private function storeOptionalScreenshot(mixed $value, array $context): array
{
if (!is_scalar($value) && !$value instanceof \Stringable && $value !== null) {
return $this->emptyScreenshotAttachment('invalid');
}
$dataUri = trim((string)($value ?? ''));
if ($dataUri === '') {
return $this->emptyScreenshotAttachment($this->contextScreenshotStatus($context) ?? 'not_provided');
}
try {
$screenshot = self::decodeScreenshotDataUri($dataUri);
} catch (RuntimeException $exception) {
$message = strtolower($exception->getMessage());
return $this->emptyScreenshotAttachment(str_contains($message, 'too large') ? 'too_large' : 'invalid');
}
try {
$storedScreenshot = $this->store->storeScreenshot($screenshot['mime_type'], $screenshot['contents']);
} catch (Throwable) {
return $this->emptyScreenshotAttachment('storage_failed');
}
return [
'key' => (string)($storedScreenshot['key'] ?? ''),
'mime_type' => (string)($storedScreenshot['mime_type'] ?? $screenshot['mime_type']),
'size_bytes' => (int)($storedScreenshot['size_bytes'] ?? $screenshot['size_bytes']),
'status' => 'stored',
];
}
private function emptyScreenshotAttachment(string $status): array
{
return [
'key' => '',
'mime_type' => '',
'size_bytes' => 0,
'status' => $status,
];
}
private function contextScreenshotStatus(array $context): ?string
{
$attachment = $context['screenshot_attachment'] ?? null;
$status = is_array($attachment) ? ($attachment['status'] ?? null) : null;
$status ??= $context['screenshot_capture_status'] ?? $context['screenshot_status'] ?? null;
return $this->normalizeEmptyScreenshotStatus($status);
}
private function normalizeEmptyScreenshotStatus(mixed $status): ?string
{
$status = strtolower(trim((string)$status));
if (in_array($status, ['capture_failed', 'not_provided'], true)) {
return $status;
}
return null;
}
private function runtimeContext(array $payload, array $context): array
{
return [
'route_path' => $this->nullableString($payload['route_path'] ?? $context['route_path'] ?? $context['route'] ?? null, 512),
'page_url' => $this->nullableString($payload['page_url'] ?? $context['page_url'] ?? $context['url'] ?? null, 1024),
'release_trace_id' => $this->nullableString($payload['release_trace_id'] ?? $context['release_trace_id'] ?? $context['trace_id'] ?? $this->releaseRequestContext('trace_id'), 64),
'frontend_version' => $this->nullableString($payload['frontend_version'] ?? $context['frontend_version'] ?? $this->releaseRequestContext('frontend_version'), 128),
'api_version' => $this->nullableString($payload['api_version'] ?? $context['api_version'] ?? $this->releaseRequestContext('backend_version'), 128),
'viewport' => is_array($context['viewport'] ?? null) ? $context['viewport'] : null,
'user_agent' => $this->nullableString($context['user_agent'] ?? ($_SERVER['HTTP_USER_AGENT'] ?? null), 1024),
'captured_at' => $this->nullableString($context['captured_at'] ?? null, 64),
'data_collection_policy_version' => $this->nullableString($payload['data_collection_policy_version'] ?? $context['data_collection_policy_version'] ?? 'error-report-v1', 64) ?? 'error-report-v1',
];
}
private function releaseRequestContext(string $key): ?string
{
$context = is_array($GLOBALS['RELEASE_REQUEST_CONTEXT'] ?? null) ? $GLOBALS['RELEASE_REQUEST_CONTEXT'] : [];
return isset($context[$key]) ? (string)$context[$key] : null;
}
private function nullableString(mixed $value, int $maxLength): ?string
{
if ($value === null) {
return null;
}
$value = trim((string)$value);
if ($value === '') {
return null;
}
return substr($value, 0, $maxLength);
}
private function boundedArray(mixed $value, int $limit): array
{
return is_array($value) ? array_slice(array_values($value), 0, $limit) : [];
}
private function statusFilter(mixed $status): string
{
$status = strtolower(trim((string)$status));
if ($status === '' || $status === self::STATUS_OPEN) {
return self::STATUS_OPEN;
}
if ($status === self::STATUS_RESOLVED || $status === 'all') {
return $status;
}
return self::STATUS_OPEN;
}
private function counts(): array
{
$rows = $this->selectRows('SELECT status, COUNT(*) AS count FROM error_reports GROUP BY status');
$counts = [
self::STATUS_OPEN => 0,
self::STATUS_RESOLVED => 0,
'all' => 0,
];
foreach ($rows as $row) {
$status = (string)($row['status'] ?? '');
$count = (int)($row['count'] ?? 0);
if (isset($counts[$status])) {
$counts[$status] = $count;
}
$counts['all'] += $count;
}
return $counts;
}
private function resolvePrincipal(): array
{
$auth = new authentication();
try {
$subuser = $auth->get_subuser();
if ($subuser !== false) {
return [
'type' => 'subuser',
'user_id' => null,
'subuser_id' => (int)$subuser->id,
'customer_number' => null,
'customer_number_context' => $this->headerInt('X-Customer-Number'),
'name' => $this->safeObjectValue($subuser, 'name') ?: $this->safeObjectValue($subuser, 'username'),
'email' => $this->safeObjectValue($subuser, 'email'),
];
}
} catch (Throwable) {
}
try {
$user = $auth->get_user();
if ($user !== false) {
return [
'type' => 'user',
'user_id' => (int)$user->id,
'subuser_id' => null,
'customer_number' => isset($user->customer_number) ? (int)$user->customer_number->value() : null,
'customer_number_context' => null,
'name' => $this->safeObjectValue($user, 'display_name'),
'email' => $this->safeObjectValue($user, 'email'),
];
}
} catch (Throwable) {
}
throw new RuntimeException('Authentication failed. Invalid or missing token.');
}
private function headerInt(string $name): ?int
{
$headers = function_exists('getallheaders') ? getallheaders() : [];
foreach ($headers as $key => $value) {
if (strcasecmp((string)$key, $name) === 0) {
$int = (int)$value;
return $int > 0 ? $int : null;
}
}
$serverKey = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
$int = (int)($_SERVER[$serverKey] ?? 0);
return $int > 0 ? $int : null;
}
private function safeObjectValue(object $object, string $property): ?string
{
try {
if (!isset($object->{$property}) || !method_exists($object->{$property}, 'value')) {
return null;
}
$value = $object->{$property}->value();
return $value === null ? null : substr((string)$value, 0, 255);
} catch (Throwable) {
return null;
}
}
private function publicReport(array $row, bool $includeDetail): array
{
$screenshotMimeType = trim((string)($row['screenshot_mime_type'] ?? ''));
$screenshotSizeBytes = isset($row['screenshot_size_bytes']) ? (int)$row['screenshot_size_bytes'] : 0;
$hasScreenshot = $screenshotMimeType !== '' && $screenshotSizeBytes > 0;
$report = [
'id' => (int)$row['id'],
'status' => (string)$row['status'],
'reporter' => [
'type' => $row['reporter_type'] ?? null,
'user_id' => isset($row['reporter_user_id']) ? (int)$row['reporter_user_id'] : null,
'subuser_id' => isset($row['reporter_subuser_id']) ? (int)$row['reporter_subuser_id'] : null,
'customer_number' => isset($row['reporter_customer_number']) ? (int)$row['reporter_customer_number'] : null,
'customer_number_context' => isset($row['reporter_customer_number_context']) ? (int)$row['reporter_customer_number_context'] : null,
'name' => $row['reporter_name'] ?? null,
'email' => $row['reporter_email'] ?? null,
],
'route_path' => $row['route_path'] ?? null,
'page_url' => $row['page_url'] ?? null,
'release_trace_id' => $row['release_trace_id'] ?? null,
'frontend_version' => $row['frontend_version'] ?? null,
'api_version' => $row['api_version'] ?? null,
'screenshot' => $hasScreenshot ? [
'mime_type' => $screenshotMimeType,
'size_bytes' => $screenshotSizeBytes,
] : null,
'answers' => [
'before_error' => $row['before_error'] ?? '',
'expected' => $row['expected'] ?? '',
'actual' => $row['actual'] ?? '',
],
'request_error_count' => isset($row['request_error_count']) ? (int)$row['request_error_count'] : 0,
'vue_error_count' => isset($row['vue_error_count']) ? (int)$row['vue_error_count'] : 0,
'resolved_at' => $row['resolved_at'] ?? null,
'resolved_by_user_id' => isset($row['resolved_by_user_id']) ? (int)$row['resolved_by_user_id'] : null,
'created_at' => $row['created_at'] ?? null,
'updated_at' => $row['updated_at'] ?? null,
];
if ($includeDetail) {
if ($hasScreenshot) {
$objectKey = trim((string)($row['screenshot_object_key'] ?? ''));
$report['screenshot']['url'] = $this->store->screenshotUrl($objectKey);
$report['screenshot']['object_key'] = $objectKey !== '' ? $objectKey : null;
}
$report['request_errors'] = $this->jsonDecode($row['request_errors_json'] ?? null);
$report['vue_errors'] = $this->jsonDecode($row['vue_errors_json'] ?? null);
$report['runtime_context'] = $this->jsonDecode($row['runtime_context_json'] ?? null);
$report['data_collection'] = [
'accepted' => (bool)($row['data_collection_accepted'] ?? false),
'accepted_at' => $row['data_collection_accepted_at'] ?? null,
'policy_version' => $row['data_collection_policy_version'] ?? null,
];
$report['resolution_note'] = $row['resolution_note'] ?? null;
}
return $report;
}
private function ensureSchema(): void
{
if ($this->schemaEnsured) {
return;
}
error_report_schema_bootstrap::ensureTables();
$this->schemaEnsured = true;
}
private function selectOne(string $sql, string $types = '', array $params = []): ?array
{
$rows = $this->selectRows($sql, $types, $params);
return $rows[0] ?? null;
}
private function selectRows(string $sql, string $types = '', array $params = []): array
{
global $db;
if ($types === '') {
$result = $db->query($sql);
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
}
$stmt = $db->prepare($sql);
if ($stmt === false) {
throw new RuntimeException('Could not prepare error report query.');
}
$stmt->bind_param($types, ...$params);
$stmt->execute();
$result = $stmt->get_result();
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
}
private function execute(string $sql, string $types = '', array $params = []): void
{
global $db;
if ($types === '') {
$db->query($sql);
return;
}
$stmt = $db->prepare($sql);
if ($stmt === false) {
throw new RuntimeException('Could not prepare error report statement.');
}
$stmt->bind_param($types, ...$params);
$stmt->execute();
}
private function insertId(): int
{
global $db;
return (int)$db->insert_id();
}
private function jsonEncodeLimited(mixed $value): string
{
$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($json === false) {
throw new RuntimeException('Could not encode error report JSON payload.');
}
if (strlen($json) <= self::JSON_MAX_LENGTH) {
return $json;
}
$truncated = [
'[truncated]' => 'Payload exceeded ' . self::JSON_MAX_LENGTH . ' bytes.',
'preview' => substr($json, 0, self::JSON_MAX_LENGTH),
];
$encoded = json_encode($truncated, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return $encoded === false ? '{}' : $encoded;
}
private function jsonDecode(mixed $value): array
{
if (!is_string($value) || trim($value) === '') {
return [];
}
$decoded = json_decode($value, true);
return is_array($decoded) ? $decoded : [];
}
}
@@ -1,47 +0,0 @@
<?php
namespace classes;
use traits\minio_t;
class error_report_store
{
use minio_t;
public function __construct()
{
self::setBucket('uploads');
}
public function storeScreenshot(string $mimeType, string $contents): array
{
$extension = match ($mimeType) {
'image/webp' => 'webp',
'image/jpeg' => 'jpg',
default => 'png',
};
$datePath = date('Y/m');
$key = sprintf('error-reports/%s/%s.%s', $datePath, bin2hex(random_bytes(16)), $extension);
if (!self::createObject($key, $contents)) {
throw new \RuntimeException('Could not store error report screenshot.');
}
return [
'key' => $key,
'mime_type' => $mimeType,
'size_bytes' => strlen($contents),
];
}
public function screenshotUrl(string $key): ?string
{
$key = trim($key);
if ($key === '') {
return null;
}
return self::getPresignedUrl($key, 1200, false);
}
}
-17
View File
@@ -1,17 +0,0 @@
<?php
namespace classes;
require_once WD . '/modules/failover/failover_c.php';
use failover\failover_c;
class failover
{
public failover_c $config;
public function __construct()
{
$this->config = new failover_c();
}
}
+16 -1
View File
@@ -12,6 +12,8 @@ use Exception;
use forms\form_helper_c; use forms\form_helper_c;
use forms\objects\book_interior_wash_f; use forms\objects\book_interior_wash_f;
use forms\objects\book_wash_f; use forms\objects\book_wash_f;
use forms\objects\complete_booking_f;
use forms\objects\generate_booking_wash_certificate_f;
use objects\form_submissions_o; use objects\form_submissions_o;
use traits\form_t; use traits\form_t;
@@ -28,12 +30,25 @@ class form
* @var book_wash_f $book_wash The BOOK_WASH form * @var book_wash_f $book_wash The BOOK_WASH form
*/ */
public book_wash_f $book_wash; public book_wash_f $book_wash;
/**
* The GENERATE_BOOKING_CERTIFICATE form
* @var generate_booking_wash_certificate_f $generate_booking_wash_certificate The GENERATE_BOOKING_CERTIFICATE form
*/
public generate_booking_wash_certificate_f $generate_booking_wash_certificate;
/**
* The COMPLETE_BOOKING_WITHOUT_WASH_CERTIFICATE form
* @var complete_booking_f $complete_booking_without_wash_certificate The COMPLETE_BOOKING_WITHOUT_WASH_CERTIFICATE form
*/
public complete_booking_f $complete_booking_without_wash_certificate;
public $last_submitted_form; public $last_submitted_form;
public form_submissions_o $form_submission; public form_submissions_o $form_submission;
public function __construct() public function __construct()
{ {
$this->book_wash = new book_wash_f(); $this->book_wash = new book_wash_f();
$this->generate_booking_wash_certificate = new generate_booking_wash_certificate_f();
$this->complete_booking_without_wash_certificate = new complete_booking_f();
} }
/** /**
@@ -94,4 +109,4 @@ class form
} }
throw new Exception('The form was not found'); throw new Exception('The form was not found');
} }
} }
@@ -9,10 +9,7 @@ use interfaces\shelly_transport_i;
class gateway_shelly_transport implements shelly_transport_i class gateway_shelly_transport implements shelly_transport_i
{ {
public function __construct( public function __construct(private readonly ?edge_gateway_manager $manager = null)
private readonly ?edge_gateway_manager $manager = null,
private readonly bool $localOnly = false
)
{ {
} }
@@ -38,8 +35,6 @@ class gateway_shelly_transport implements shelly_transport_i
return match ($endpoint) { return match ($endpoint) {
'/v2/devices/api/get' => $this->handleGetStates($department_id, $data), '/v2/devices/api/get' => $this->handleGetStates($department_id, $data),
'/v2/devices/api/set/switch' => $this->handleSetSwitch($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), default => throw new Exception('Unsupported gateway Shelly transport endpoint: ' . $endpoint),
}; };
} }
@@ -57,9 +52,7 @@ class gateway_shelly_transport implements shelly_transport_i
$result = []; $result = [];
foreach ($ids as $logicalRelayId) { foreach ($ids as $logicalRelayId) {
$status = $this->localOnly $status = $this->manager()->dispatchRelayStatus($departmentId, $logicalRelayId);
? $this->manager()->dispatchRelayStatusLocalOnly($departmentId, $logicalRelayId)
: $this->manager()->dispatchRelayStatus($departmentId, $logicalRelayId);
$result[] = $this->normalizeRelayPayload($logicalRelayId, $status); $result[] = $this->normalizeRelayPayload($logicalRelayId, $status);
} }
@@ -76,73 +69,15 @@ class gateway_shelly_transport implements shelly_transport_i
throw new Exception('Shelly gateway switch requests require an id'); throw new Exception('Shelly gateway switch requests require an id');
} }
$toggleAfter = $this->normalizeToggleAfter( $status = $this->manager()->dispatchRelaySwitch(
$data['toggle_after'] ?? $data['toggleAfter'] ?? $data['timer'] ?? null $departmentId,
$logicalRelayId,
(bool)($data['on'] ?? false)
); );
$status = $this->localOnly
? $this->manager()->dispatchRelaySwitchLocalOnlyWithTimer(
$departmentId,
$logicalRelayId,
(bool)($data['on'] ?? false),
$toggleAfter
)
: $this->manager()->dispatchRelaySwitchWithTimer(
$departmentId,
$logicalRelayId,
(bool)($data['on'] ?? false),
$toggleAfter
);
return [$this->normalizeRelayPayload($logicalRelayId, $status)]; 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 * @param array<string,mixed> $status
* @return array<string,mixed> * @return array<string,mixed>
@@ -154,7 +89,6 @@ class gateway_shelly_transport implements shelly_transport_i
return [ return [
'id' => $logicalRelayId, 'id' => $logicalRelayId,
'relay_id' => $logicalRelayId,
'online' => $online, 'online' => $online,
'on' => $on, 'on' => $on,
'status' => [ 'status' => [
@@ -163,7 +97,6 @@ class gateway_shelly_transport implements shelly_transport_i
], ],
], ],
'binding' => (array)($status['binding'] ?? []), 'binding' => (array)($status['binding'] ?? []),
'execution' => (array)($status['execution'] ?? []),
'raw' => (array)($status['raw'] ?? []), 'raw' => (array)($status['raw'] ?? []),
]; ];
} }
@@ -172,14 +105,4 @@ class gateway_shelly_transport implements shelly_transport_i
{ {
return $this->manager ?? new edge_gateway_manager(); return $this->manager ?? new edge_gateway_manager();
} }
private function normalizeToggleAfter(mixed $toggleAfter): ?int
{
if ($toggleAfter === null || $toggleAfter === '') {
return null;
}
$seconds = (int)$toggleAfter;
return $seconds > 0 ? $seconds : null;
}
} }
@@ -1,151 +0,0 @@
<?php
namespace classes;
use RuntimeException;
class hetzner_cloud_api_exception extends RuntimeException
{
public function __construct(
string $message,
private readonly int $statusCode = 0,
private readonly string $apiCode = ''
) {
parent::__construct($message, $statusCode);
}
public function statusCode(): int
{
return $this->statusCode;
}
public function apiCode(): string
{
return $this->apiCode;
}
}
class hetzner_cloud_client
{
private const BASE_URL = 'https://api.hetzner.cloud/v1';
public function __construct(private readonly string $token, private readonly int $timeoutSeconds = 8)
{
if (trim($token) === '') {
throw new RuntimeException('Hetzner Cloud API token is required.');
}
}
public function getLoadBalancer(int|string $id): array
{
return $this->request('GET', '/load_balancers/' . rawurlencode((string)$id))['load_balancer'] ?? [];
}
public function addIpTarget(int|string $loadBalancerId, string $ip): array
{
return $this->request('POST', '/load_balancers/' . rawurlencode((string)$loadBalancerId) . '/actions/add_target', [
'type' => 'ip',
'ip' => ['ip' => $ip],
]);
}
public function removeIpTarget(int|string $loadBalancerId, string $ip): array
{
return $this->request('POST', '/load_balancers/' . rawurlencode((string)$loadBalancerId) . '/actions/remove_target', [
'type' => 'ip',
'ip' => ['ip' => $ip],
]);
}
public function addService(int|string $loadBalancerId, string $protocol, int $listenPort, int $destinationPort, array $options = []): array
{
return $this->request('POST', '/load_balancers/' . rawurlencode((string)$loadBalancerId) . '/actions/add_service', self::servicePayload(
$protocol,
$listenPort,
$destinationPort,
$options
));
}
public function updateService(int|string $loadBalancerId, string $protocol, int $listenPort, int $destinationPort, array $options = []): array
{
return $this->request('POST', '/load_balancers/' . rawurlencode((string)$loadBalancerId) . '/actions/update_service', self::servicePayload(
$protocol,
$listenPort,
$destinationPort,
$options
));
}
private static function servicePayload(string $protocol, int $listenPort, int $destinationPort, array $options): array
{
$payload = [
'protocol' => strtolower($protocol),
'listen_port' => $listenPort,
'destination_port' => $destinationPort,
'proxyprotocol' => false,
];
foreach (['health_check', 'http'] as $key) {
if (isset($options[$key]) && is_array($options[$key])) {
$payload[$key] = $options[$key];
}
}
return $payload;
}
private function request(string $method, string $path, ?array $payload = null): array
{
$curl = curl_init(self::BASE_URL . $path);
if ($curl === false) {
throw new RuntimeException('Could not initialize Hetzner Cloud API request.');
}
$headers = [
'Accept: application/json',
'Authorization: Bearer ' . trim($this->token),
];
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, min(3, $this->timeoutSeconds));
curl_setopt($curl, CURLOPT_TIMEOUT, max(1, $this->timeoutSeconds));
curl_setopt($curl, CURLOPT_NOSIGNAL, true);
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
if ($payload !== null) {
$body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($body === false) {
throw new RuntimeException('Could not encode Hetzner Cloud API payload.');
}
$headers[] = 'Content-Type: application/json';
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$raw = curl_exec($curl);
$error = curl_error($curl);
$status = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($raw === false) {
throw new RuntimeException('Hetzner Cloud API request failed: ' . $error);
}
$decoded = trim((string)$raw) === '' ? [] : json_decode((string)$raw, true);
if (!is_array($decoded)) {
$decoded = ['raw' => (string)$raw];
}
if ($status < 200 || $status >= 300) {
$errorPayload = is_array($decoded['error'] ?? null) ? $decoded['error'] : [];
$apiCode = (string)($errorPayload['code'] ?? $decoded['code'] ?? '');
$message = (string)($errorPayload['message'] ?? $decoded['message'] ?? ('HTTP ' . $status));
throw new hetzner_cloud_api_exception('Hetzner Cloud API request failed: ' . $message, $status, $apiCode);
}
return $decoded;
}
}
@@ -1,64 +0,0 @@
<?php
namespace classes;
/**
* Ensures additive schema for superuser invoice-period flags.
*/
class invoice_period_flag_schema_bootstrap
{
private static bool $initialized = false;
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
return;
}
$db->query(
"CREATE TABLE IF NOT EXISTS invoice_period_flags (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
source VARCHAR(32) NOT NULL,
severity VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'active',
target_type VARCHAR(64) NOT NULL,
target_id BIGINT NOT NULL,
field VARCHAR(64) NULL,
customer_number INT NULL,
order_id BIGINT NULL,
order_item_id BIGINT NULL,
invoice_collection_id BIGINT NULL,
xlvask_usage_log_id BIGINT NULL,
definition_key VARCHAR(128) NULL,
fingerprint VARCHAR(191) NULL,
reason TEXT NULL,
status_reason TEXT NULL,
context_json JSON NULL,
created_by INT NULL,
status_changed_by INT NULL,
status_changed_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_invoice_period_flags_auto_fingerprint (source, fingerprint),
KEY idx_invoice_period_flags_target (target_type, target_id, status),
KEY idx_invoice_period_flags_customer_status (customer_number, status),
KEY idx_invoice_period_flags_source_status (source, status),
KEY idx_invoice_period_flags_order (order_id),
KEY idx_invoice_period_flags_order_item (order_item_id),
KEY idx_invoice_period_flags_invoice_collection (invoice_collection_id),
KEY idx_invoice_period_flags_xlvask (xlvask_usage_log_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
products_schema_bootstrap::ensureTables();
xlvask_usage_logs_schema_bootstrap::ensureTables();
self::$initialized = true;
}
}
File diff suppressed because it is too large Load Diff
@@ -10,22 +10,6 @@ use licenseplaterecognizer\licenseplaterecognizer_c;
class licenseplaterecognizer implements licenseplaterecognizer_i class licenseplaterecognizer implements licenseplaterecognizer_i
{ {
private const DEFAULT_API_URL = 'https://vs4sws0kg4sog4ssw8kwowk4.coolify.truckwash.dk';
private const PLATE_READER_CONFIG_JSON = '{"mode":"fast","plates_per_vehicle":1,"zoom_in_vehicles":0}';
private const RESULT_CACHE_CONTEXT = '{"config":{"mode":"fast","plates_per_vehicle":1,"zoom_in_vehicles":0},"regions":"dk,de,se,no"}';
private const PLATE_READER_REGIONS = 'dk,de,se,no';
private const DEFAULT_UPLOAD_FILE_NAME = 'license-plate.jpg';
private const RUNTIME_CONFIG_CACHE_TTL_SECONDS = 15;
private const RUNTIME_CONFIG_REDIS_CACHE_KEY = 'licenseplaterecognizer:runtime_config:v1';
private const RESULT_CACHE_TTL_SECONDS = 10;
private const RESULT_CACHE_REDIS_KEY_PREFIX = 'licenseplaterecognizer:result:v1:';
private const PLATE_READER_CONNECT_TIMEOUT_MS = 1000;
private const PLATE_READER_TOTAL_TIMEOUT_MS = 4500;
/**
* @var array<string, float>
*/
private array $last_timings = [];
/** /**
* The configuration of the module * The configuration of the module
* @var licenseplaterecognizer_c * @var licenseplaterecognizer_c
@@ -35,25 +19,12 @@ class licenseplaterecognizer implements licenseplaterecognizer_i
* API URL * API URL
* @var string * @var string
*/ */
private string $api_url; private string $api_url = 'https://vs4sws0kg4sog4ssw8kwowk4.coolify.truckwash.dk'; // Default (cloud): 'https://api.platerecognizer.com'; (without /v1/plate-reader/)';
/**
* @var array{enabled: bool, api_key: string}|null
*/
private ?array $runtime_config = null;
/** public function __construct()
* @var array{values: array{enabled: bool, api_key: string}, cached_at: float}|null
*/
private static ?array $runtime_config_cache = null;
public function __construct(bool $load_config = true, ?string $api_url = null)
{ {
$this->api_url = self::normalizeApiUrl($api_url ?? self::configuredApiUrl()); $this->config = new licenseplaterecognizer_c();
if ($load_config) {
$this->config = new licenseplaterecognizer_c();
}
} }
@@ -62,7 +33,7 @@ class licenseplaterecognizer implements licenseplaterecognizer_i
*/ */
public function requireModuleEnabled(): void public function requireModuleEnabled(): void
{ {
if (!$this->runtimeConfig()['enabled']) { if (!(bool)$this->config->enabled->getVariableValue()) {
throw new Exception('licenseplaterecognizer module is not enabled.'); throw new Exception('licenseplaterecognizer module is not enabled.');
} }
} }
@@ -74,516 +45,54 @@ class licenseplaterecognizer implements licenseplaterecognizer_i
*/ */
public function licenseplaterecognizer(string $base64_image): array public function licenseplaterecognizer(string $base64_image): array
{ {
return $this->recognizePlate( $image_processor = new image_processor();
fn () => $this->buildPlateReaderPayload($base64_image),
fn () => $this->buildResultCacheKeyFromUploadString($base64_image)
);
}
public function licenseplaterecognizerUpload(string $image_data, string $mime_type = 'image/jpeg'): array //ADD PARAMETER IN REQUEST LIKE regions
{ $data = array(
return $this->recognizePlate( 'upload' => $base64_image,
fn () => $this->buildPlateReaderPayloadFromUpload( //'regions' => 'dk' // Optional
$this->buildUploadValueFromBytes($image_data, $mime_type)
),
fn () => $this->buildResultCacheKeyFromBytes($image_data)
); );
}
public function licenseplaterecognizerUploadUncached(string $image_data, string $mime_type = 'image/jpeg'): array // Prepare new cURL resource
{ //$ch = curl_init('https://api.platerecognizer.com/v1/plate-reader/');
return $this->recognizePlate( $ch = curl_init($this->api_url . '/v1/plate-reader/');
fn () => $this->buildPlateReaderPayloadFromUpload( curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$this->buildUploadValueFromBytes($image_data, $mime_type) curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
// Set HTTP Header for POST request
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Authorization: Token " . $this->config->api_key->getVariableValue()
) )
); );
}
public function licenseplaterecognizerUploadFile(string $image_path, string $mime_type = 'image/jpeg'): array // Submit the POST request and close cURL session handle
{ $result = curl_exec($ch);
return $this->recognizePlate( curl_close($ch);
fn () => $this->buildPlateReaderPayloadFromUpload( // Print the response from the server
$this->buildUploadValueFromFile($image_path, $mime_type) if ($result === false) {
) throw new Exception('Error in API request.');
); }
}
/** $response_data = json_decode($result, true);
* @throws Exception if (isset($response_data['results']) && count($response_data['results']) > 0) {
*/ return [
private function recognizePlate(callable $payload_factory, ?callable $result_cache_key_factory = null): array 'success' => true,
{ 'license_plate_number' => $response_data['results'][0]['plate'] ?? null,
$started_at = microtime(true); 'confidence' => $response_data['results'][0]['score'] ?? null,
$this->last_timings = []; 'raw_response' => $response_data,
$result_cache = null;
$result_cache_key = null;
try {
$config_started_at = microtime(true);
$runtime_config = $this->runtimeConfig();
if (!$runtime_config['enabled']) {
throw new Exception('licenseplaterecognizer module is not enabled.');
}
$api_key = $runtime_config['api_key'];
$this->last_timings['config'] = $this->elapsedMs($config_started_at);
if ($result_cache_key_factory !== null) {
$cache_started_at = microtime(true);
try {
$result_cache = $this->resultCacheStore();
if ($result_cache !== null) {
$result_cache_key = $result_cache_key_factory();
if ($result_cache_key !== null) {
$cached_result = $this->readRecognitionResultCache($result_cache, $result_cache_key);
if ($cached_result !== null) {
$this->last_timings['cache_hit'] = 1;
return $cached_result;
}
}
}
$this->last_timings['cache_miss'] = 1;
} finally {
$this->last_timings['cache'] = $this->elapsedMs($cache_started_at);
}
}
$payload_started_at = microtime(true);
$data = $payload_factory();
$this->last_timings['payload'] = $this->elapsedMs($payload_started_at);
$ch = curl_init($this->api_url . '/v1/plate-reader/');
if (!$ch instanceof \CurlHandle) {
throw new Exception('Error initializing API request.');
}
$curl_options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CONNECTTIMEOUT_MS => self::PLATE_READER_CONNECT_TIMEOUT_MS,
CURLOPT_TIMEOUT_MS => self::PLATE_READER_TOTAL_TIMEOUT_MS,
CURLOPT_NOSIGNAL => true,
CURLOPT_NOPROGRESS => false,
CURLOPT_XFERINFOFUNCTION => self::clientDisconnectAbortCallback(),
CURLOPT_HTTPHEADER => [
"Authorization: Token " . $api_key,
'Expect:',
],
]; ];
if (defined('CURLOPT_TCP_NODELAY')) { } else {
$curl_options[(int)constant('CURLOPT_TCP_NODELAY')] = true; return [
}
curl_setopt_array($ch, $curl_options);
// Submit the POST request and close cURL session handle
$upstream_started_at = microtime(true);
$result = curl_exec($ch);
$this->last_timings['upstream'] = $this->elapsedMs($upstream_started_at);
$this->recordCurlTimings($ch);
curl_close($ch);
// Print the response from the server
if ($result === false) {
throw new Exception('Error in API request.');
}
$parse_started_at = microtime(true);
$response_data = json_decode($result, true);
$this->last_timings['parse'] = $this->elapsedMs($parse_started_at);
$this->recordResponseTimings($response_data);
if (isset($response_data['results']) && count($response_data['results']) > 0) {
$recognized_result = [
'success' => true,
'license_plate_number' => $response_data['results'][0]['plate'] ?? null,
'confidence' => $response_data['results'][0]['score'] ?? null,
];
$this->writeRecognitionResultCache($result_cache, $result_cache_key, $recognized_result);
return $recognized_result;
}
$recognized_result = [
'success' => false, 'success' => false,
'message' => 'No license plate detected.', 'message' => 'No license plate detected.',
'raw_response' => $response_data,
]; ];
$this->writeRecognitionResultCache($result_cache, $result_cache_key, $recognized_result);
return $recognized_result;
} finally {
$this->last_timings['total'] = $this->elapsedMs($started_at);
} }
} }
private static function clientDisconnectAbortCallback(): callable
{
return static function (): int {
return connection_aborted() ? 1 : 0;
};
}
public function getLastTimings(): array
{
return $this->last_timings;
}
private function elapsedMs(float $started_at): float
{
return (microtime(true) - $started_at) * 1000;
}
private static function configuredApiUrl(): string
{
$configured = getenv('PLATE_RECOGNIZER_API_URL');
if ($configured === false || trim((string)$configured) === '') {
$configured = $_ENV['PLATE_RECOGNIZER_API_URL'] ?? $_SERVER['PLATE_RECOGNIZER_API_URL'] ?? self::DEFAULT_API_URL;
}
return (string)$configured;
}
private static function normalizeApiUrl(string $api_url): string
{
$api_url = trim($api_url);
if ($api_url === '') {
return self::DEFAULT_API_URL;
}
return rtrim($api_url, '/');
}
private function recordCurlTimings(\CurlHandle $curl_handle): void
{
$mapping = [
CURLINFO_NAMELOOKUP_TIME => 'upstream_dns',
CURLINFO_CONNECT_TIME => 'upstream_connect',
CURLINFO_APPCONNECT_TIME => 'upstream_tls',
CURLINFO_PRETRANSFER_TIME => 'upstream_pretransfer',
CURLINFO_STARTTRANSFER_TIME => 'upstream_ttfb',
CURLINFO_TOTAL_TIME => 'upstream_total',
];
foreach ($mapping as $curl_info_option => $timing_key) {
$value = curl_getinfo($curl_handle, $curl_info_option);
if (!is_numeric($value)) {
continue;
}
$this->last_timings[$timing_key] = max(0, (float)$value * 1000);
}
}
private function recordResponseTimings(mixed $response_data): void
{
if (!is_array($response_data) || !isset($response_data['processing_time']) || !is_numeric($response_data['processing_time'])) {
return;
}
$this->last_timings['upstream_processing'] = max(0, (float)$response_data['processing_time']);
}
private function buildResultCacheKeyFromUploadString(string $base64_image): string
{
$base64_image = trim($base64_image);
if (preg_match('/^data:image\/[a-zA-Z0-9.+-]+;base64,(.*)$/s', $base64_image, $matches) === 1) {
$image_data = base64_decode((string)$matches[1], true);
if (is_string($image_data)) {
return $this->buildResultCacheKeyFromBytes($image_data);
}
}
return $this->buildResultCacheKeyFromBytes($base64_image);
}
private function buildResultCacheKeyFromBytes(string $image_data): string
{
$context = hash_init('sha256');
hash_update($context, $this->resultCacheContext());
hash_update($context, "\0");
hash_update($context, $image_data);
return self::RESULT_CACHE_REDIS_KEY_PREFIX . hash_final($context);
}
private function resultCacheContext(): string
{
return self::RESULT_CACHE_CONTEXT;
}
protected function resultCacheStore(): ?object
{
return $this->runtimeConfigCacheStore();
}
private function readRecognitionResultCache(?object $cache, ?string $key): ?array
{
if ($cache === null || $key === null || !method_exists($cache, 'get')) {
return null;
}
try {
$cached = $cache->get($key);
} catch (\Throwable) {
return null;
}
if (!is_string($cached) || trim($cached) === '') {
return null;
}
$decoded = json_decode($cached, true);
if (!is_array($decoded) || !array_key_exists('success', $decoded)) {
return null;
}
return $decoded;
}
private function writeRecognitionResultCache(?object $cache, ?string $key, array $result): void
{
if ($cache === null || $key === null || !method_exists($cache, 'setEx')) {
return;
}
try {
$encoded = json_encode($result, JSON_UNESCAPED_SLASHES);
if (is_string($encoded)) {
$cache->setEx($key, $encoded, self::RESULT_CACHE_TTL_SECONDS);
}
} catch (\Throwable) {
// Scanner result cache is best-effort; Plate Recognizer remains the source of truth.
}
}
protected function buildPlateReaderPayload(string $base64_image): array
{
return $this->buildPlateReaderPayloadFromUpload($this->buildUploadValue($base64_image));
}
protected function buildPlateReaderPayloadFromUpload(string|\CURLFile|\CURLStringFile $upload): array
{
return [
'upload' => $upload,
'config' => self::PLATE_READER_CONFIG_JSON,
'regions' => self::PLATE_READER_REGIONS,
];
}
private function buildUploadValue(string $base64_image): string|\CURLStringFile
{
$base64_image = trim($base64_image);
if (preg_match('/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.*)$/s', $base64_image, $matches) !== 1) {
return $base64_image;
}
$image_data = base64_decode((string)$matches[2], true);
if ($image_data === false || !class_exists(\CURLStringFile::class)) {
return (string)$matches[2];
}
return new \CURLStringFile($image_data, self::DEFAULT_UPLOAD_FILE_NAME, (string)$matches[1]);
}
private function buildUploadValueFromBytes(string $image_data, string $mime_type): string|\CURLStringFile
{
$mime_type = trim($mime_type) !== '' ? trim($mime_type) : 'image/jpeg';
if (!str_starts_with($mime_type, 'image/')) {
$mime_type = 'image/jpeg';
}
if (!class_exists(\CURLStringFile::class)) {
return $image_data;
}
return new \CURLStringFile($image_data, self::DEFAULT_UPLOAD_FILE_NAME, $mime_type);
}
/**
* @throws Exception
*/
private function buildUploadValueFromFile(string $image_path, string $mime_type): \CURLFile
{
$image_path = trim($image_path);
$mime_type = trim($mime_type) !== '' ? trim($mime_type) : 'image/jpeg';
if (!str_starts_with($mime_type, 'image/')) {
$mime_type = 'image/jpeg';
}
if ($image_path === '' || !is_file($image_path) || !class_exists(\CURLFile::class)) {
throw new Exception('Image upload file is invalid.');
}
return new \CURLFile($image_path, $mime_type, self::DEFAULT_UPLOAD_FILE_NAME);
}
protected function runtimeConfig(): array
{
if ($this->runtime_config !== null) {
return $this->runtime_config;
}
if ($this->shouldUseSharedRuntimeConfigCache()) {
$cached_config = self::getSharedRuntimeConfigCache();
if ($cached_config !== null) {
$this->runtime_config = $cached_config;
return $this->runtime_config;
}
$cached_config = $this->readRuntimeConfigCacheStore();
if ($cached_config !== null) {
self::$runtime_config_cache = [
'values' => $cached_config,
'cached_at' => microtime(true),
];
$this->runtime_config = $cached_config;
return $this->runtime_config;
}
}
$values = $this->readRuntimeModuleConfig();
$this->runtime_config = [
'enabled' => $this->parseModuleConfigBool($values['enabled'] ?? false),
'api_key' => (string)($values['api_key'] ?? ''),
];
if ($this->shouldUseSharedRuntimeConfigCache()) {
self::$runtime_config_cache = [
'values' => $this->runtime_config,
'cached_at' => microtime(true),
];
$this->writeRuntimeConfigCacheStore($this->runtime_config);
}
return $this->runtime_config;
}
protected function shouldUseSharedRuntimeConfigCache(): bool
{
return static::class === self::class;
}
private static function getSharedRuntimeConfigCache(): ?array
{
if (self::$runtime_config_cache === null) {
return null;
}
$cache_age_seconds = microtime(true) - self::$runtime_config_cache['cached_at'];
if ($cache_age_seconds > self::RUNTIME_CONFIG_CACHE_TTL_SECONDS) {
self::$runtime_config_cache = null;
return null;
}
return self::$runtime_config_cache['values'];
}
protected function runtimeConfigCacheStore(): ?object
{
return defined('redis') ? constant('redis') : null;
}
private function readRuntimeConfigCacheStore(): ?array
{
$cache = $this->runtimeConfigCacheStore();
if ($cache === null || !method_exists($cache, 'get')) {
return null;
}
try {
$cached = $cache->get(self::RUNTIME_CONFIG_REDIS_CACHE_KEY);
} catch (\Throwable) {
return null;
}
if (!is_string($cached) || trim($cached) === '') {
return null;
}
$decoded = json_decode($cached, true);
if (!is_array($decoded)) {
return null;
}
if (!array_key_exists('enabled', $decoded) || !array_key_exists('api_key', $decoded)) {
return null;
}
return [
'enabled' => $this->parseModuleConfigBool($decoded['enabled']),
'api_key' => (string)$decoded['api_key'],
];
}
/**
* @param array{enabled: bool, api_key: string} $config
*/
private function writeRuntimeConfigCacheStore(array $config): void
{
$cache = $this->runtimeConfigCacheStore();
if ($cache === null || !method_exists($cache, 'setEx')) {
return;
}
try {
$encoded = json_encode($config, JSON_UNESCAPED_SLASHES);
if (is_string($encoded)) {
$cache->setEx(self::RUNTIME_CONFIG_REDIS_CACHE_KEY, $encoded, self::RUNTIME_CONFIG_CACHE_TTL_SECONDS);
}
} catch (\Throwable) {
// Scanner config cache is best-effort; DB remains the source of truth.
}
}
private function parseModuleConfigBool(mixed $value): bool
{
if (is_bool($value)) {
return $value;
}
if (is_numeric($value)) {
return (int)$value === 1;
}
return strtolower(trim((string)$value)) === 'true';
}
protected function readRuntimeModuleConfig(): array
{
global $db;
if ($db instanceof db) {
$module = $db->escape_string('licenseplaterecognizer');
$result = $db->query("SELECT variable, value FROM module_config WHERE module = '$module' AND variable IN ('enabled', 'api_key')");
$values = [];
if ($result instanceof \mysqli_result) {
while ($row = $result->fetch_assoc()) {
$variable = (string)($row['variable'] ?? '');
if ($variable !== '') {
$values[$variable] = (string)($row['value'] ?? '');
}
}
}
return $values;
}
if (!isset($this->config)) {
$this->config = new licenseplaterecognizer_c();
}
return [
'enabled' => (string)$this->config->enabled->getVariableValue(),
'api_key' => (string)$this->config->api_key->getVariableValue(),
];
}
/** /**
* @inheritDoc * @inheritDoc
* @throws Exception If the module is not enabled or if there is an error in the API request * @throws Exception If the module is not enabled or if there is an error in the API request
@@ -591,8 +100,8 @@ class licenseplaterecognizer implements licenseplaterecognizer_i
*/ */
public function get_usage(): licenseplaterecognizer_info public function get_usage(): licenseplaterecognizer_info
{ {
// Require the module to be enabled
$this->requireModuleEnabled(); $this->requireModuleEnabled();
$api_key = $this->runtimeConfig()['api_key'];
$curl = curl_init(); $curl = curl_init();
curl_setopt_array($curl, array( curl_setopt_array($curl, array(
CURLOPT_URL => $this->api_url . '/info/', CURLOPT_URL => $this->api_url . '/info/',
@@ -603,9 +112,9 @@ class licenseplaterecognizer implements licenseplaterecognizer_i
CURLOPT_FOLLOWLOCATION => true, CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2TLS,
CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => array(
'Authorization: Token ' . $api_key, 'Authorization: Token ' . $this->config->api_key->getVariableValue()
], ),
)); ));
$response = curl_exec($curl); $response = curl_exec($curl);
curl_close($curl); curl_close($curl);
@@ -615,4 +124,4 @@ class licenseplaterecognizer implements licenseplaterecognizer_i
} }
return new licenseplaterecognizer_info($response_data); return new licenseplaterecognizer_info($response_data);
} }
} }
@@ -1,24 +0,0 @@
<?php
namespace classes;
class limited_backoffice_exception extends \RuntimeException
{
public function __construct(
string $message,
private readonly int $statusCode = 400,
private readonly ?array $payload = null
) {
parent::__construct($message);
}
public function statusCode(): int
{
return $this->statusCode;
}
public function payload(): array|string
{
return $this->payload ?? $this->getMessage();
}
}
@@ -1,43 +0,0 @@
<?php
namespace classes;
class limited_backoffice_schema_bootstrap
{
private static bool $initialized = false;
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
return;
}
$db->query(<<<'SQL'
CREATE TABLE IF NOT EXISTS `limited_backoffice_employees` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT NOT NULL,
`managed_group_id` INT NOT NULL,
`role_key` VARCHAR(64) NOT NULL,
`department_ids` LONGTEXT NOT NULL,
`created_by_user_id` INT NOT NULL,
`updated_by_user_id` INT NULL,
`deactivated_at` DATETIME NULL,
`created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_limited_backoffice_employees_user_id` (`user_id`),
KEY `idx_limited_backoffice_employees_group_id` (`managed_group_id`),
KEY `idx_limited_backoffice_employees_role_key` (`role_key`),
KEY `idx_limited_backoffice_employees_deactivated_at` (`deactivated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
SQL);
self::$initialized = true;
}
}
File diff suppressed because it is too large Load Diff
+2 -9
View File
@@ -155,7 +155,7 @@ class motorapi implements motorapi_i
// Get the cached result from the log/local database/cache // Get the cached result from the log/local database/cache
$motorapi_lookups = new motorapi_lookups_o(); $motorapi_lookups = new motorapi_lookups_o();
// Add the cached value to the meta // Add the cached value to the meta
self::addCachedMetaIfPossible($response); $response->add_meta('cached', true);
$cleaned_result = self::cleanJSON($motorapi_lookups->getCachedResult($licensePlate)->result->value()); $cleaned_result = self::cleanJSON($motorapi_lookups->getCachedResult($licensePlate)->result->value());
$object = json_decode($cleaned_result); $object = json_decode($cleaned_result);
if ($object === null) { if ($object === null) {
@@ -165,13 +165,6 @@ class motorapi implements motorapi_i
return json_decode($cleaned_result); return json_decode($cleaned_result);
} }
public static function addCachedMetaIfPossible(mixed $response): void
{
if (is_object($response) && method_exists($response, 'add_meta')) {
$response->add_meta('cached', true);
}
}
/** /**
* @inheritDoc * @inheritDoc
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid * @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
@@ -392,4 +385,4 @@ class motorapi implements motorapi_i
$motorapi_lookups = new motorapi_lookups_o(); $motorapi_lookups = new motorapi_lookups_o();
$motorapi_lookups->add($licensePlate, json_encode($response), $endpoint); $motorapi_lookups->add($licensePlate, json_encode($response), $endpoint);
} }
} }
+4 -35
View File
@@ -340,49 +340,18 @@ class n8n implements n8n_i
throw new Exception('Webhook target must not be empty.'); throw new Exception('Webhook target must not be empty.');
} }
if (filter_var($target, FILTER_VALIDATE_URL) !== false) {
return $target;
}
$baseUrl = trim((string)$this->config->webhook_base_url->getVariableValue()); $baseUrl = trim((string)$this->config->webhook_base_url->getVariableValue());
if ($baseUrl === '') { if ($baseUrl === '') {
throw new Exception('n8n webhook base URL is not configured.'); throw new Exception('n8n webhook base URL is not configured.');
} }
if (filter_var($target, FILTER_VALIDATE_URL) !== false) {
if (!$this->isAllowedWebhookAbsoluteUrl($target, $baseUrl)) {
throw new Exception('Webhook URL must use the configured n8n webhook host.');
}
return $target;
}
return rtrim($baseUrl, '/') . '/' . ltrim($target, '/'); return rtrim($baseUrl, '/') . '/' . ltrim($target, '/');
} }
private function isAllowedWebhookAbsoluteUrl(string $targetUrl, string $baseUrl): bool
{
$targetParts = parse_url($targetUrl);
$baseParts = parse_url($baseUrl);
if (!is_array($targetParts) || !is_array($baseParts)) {
return false;
}
$targetHost = strtolower((string)($targetParts['host'] ?? ''));
$baseHost = strtolower((string)($baseParts['host'] ?? ''));
if ($targetHost === '' || $baseHost === '' || $targetHost !== $baseHost) {
return false;
}
$targetScheme = strtolower((string)($targetParts['scheme'] ?? ''));
$baseScheme = strtolower((string)($baseParts['scheme'] ?? ''));
if ($targetScheme === '' || $baseScheme === '' || $targetScheme !== $baseScheme) {
return false;
}
$targetPort = (int)($targetParts['port'] ?? ($targetScheme === 'https' ? 443 : 80));
$basePort = (int)($baseParts['port'] ?? ($baseScheme === 'https' ? 443 : 80));
return $targetPort === $basePort;
}
/** /**
* @throws Exception * @throws Exception
*/ */
+2 -49
View File
@@ -34,58 +34,11 @@ class openai implements openai_i
*/ */
public function requireModuleEnabled(): void public function requireModuleEnabled(): void
{ {
if (!$this->config->enabled->isTrue()) { if (!(bool)$this->config->enabled->getVariableValue()) {
throw new Exception('OpenAI module is not enabled.'); throw new Exception('OpenAI module is not enabled.');
} }
} }
/**
* Send a structured JSON text task to the OpenAI Responses API.
*
* @throws Exception
*/
public function jsonTask(string $schemaName, string $prompt, array $payload, array $schema, float $temperature = 0.1): array
{
$this->requireModuleEnabled();
$data = [
'model' => $this->model,
'input' => [
[
'role' => 'user',
'content' => [
[
'type' => 'input_text',
'text' => $prompt . "\n\nData:\n" . json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
],
],
],
],
'text' => [
'format' => [
'type' => 'json_schema',
'name' => $schemaName,
'schema' => $schema,
'strict' => true,
],
],
'temperature' => $temperature,
];
$response = $this->sendRequest($data);
$output = $response['output'][0]['content'][0]['text'] ?? null;
if (!is_string($output) || $output === '') {
throw new Exception('Invalid response format from OpenAI API. (Missing text field)');
}
$decoded = json_decode($output, true);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
throw new Exception('Error parsing JSON response: ' . json_last_error_msg());
}
return $decoded;
}
protected function getLPRSchema(): array protected function getLPRSchema(): array
{ {
return [ return [
@@ -295,4 +248,4 @@ class openai implements openai_i
//print_r($responseData); //print_r($responseData);
return $responseData; return $responseData;
} }
} }
@@ -1,556 +0,0 @@
<?php
namespace classes;
use PDO;
class order_reference_suggestions_service
{
private const DEFAULT_LIMIT = 10;
private const MAX_LIMIT = 25;
private const MAX_SOURCE_ROWS = 500;
/**
* @var array<string, bool>
*/
private array $columnExistsCache = [];
/**
* @param array{
* search?: mixed,
* department_id?: mixed,
* customer_id?: mixed,
* reg_1?: mixed,
* reg_2?: mixed,
* reg_3?: mixed,
* limit?: mixed
* } $criteria
* @return array<int, array<string, mixed>>
*/
public function suggest(array $criteria): array
{
$departmentId = $this->toPositiveInt($criteria['department_id'] ?? null);
if ($departmentId === null) {
return [];
}
$search = $this->normalizeText($criteria['search'] ?? '');
$customerId = $this->toPositiveInt($criteria['customer_id'] ?? null);
$plates = $this->normalizePlates([
$criteria['reg_1'] ?? '',
$criteria['reg_2'] ?? '',
$criteria['reg_3'] ?? '',
]);
$limit = $this->clampLimit($criteria['limit'] ?? self::DEFAULT_LIMIT);
$rows = [
...$this->fetchBookingRows($departmentId, $search),
...$this->fetchOrderRows($departmentId, $search),
...$this->fetchVehicleRows($departmentId, $customerId, $plates, $search),
];
$suggestions = $this->aggregateRows($rows, $search, $customerId, $plates);
usort($suggestions, [$this, 'sortSuggestions']);
return array_slice($suggestions, 0, $limit);
}
/**
* @return array<int, array<string, mixed>>
*/
private function fetchBookingRows(int $departmentId, string $search): array
{
$where = [
'department = :department_id',
'reference IS NOT NULL',
"TRIM(reference) <> ''",
];
if ($this->tableHasColumn('order_bookings', 'deleted_at')) {
array_unshift($where, 'deleted_at IS NULL');
}
$params = ['department_id' => $departmentId];
if ($search !== '') {
$where[] = 'LOWER(reference) LIKE :search';
$params['search'] = '%' . $this->lower($search) . '%';
}
$sql = "SELECT
'booking' AS source,
id AS origin_id,
TRIM(reference) AS reference,
datetime AS source_created_at,
datetime AS used_at,
customer_number AS customer_id,
department AS department_id,
reg_1,
reg_2,
reg_3
FROM order_bookings
WHERE " . implode(' AND ', $where) . "
ORDER BY datetime DESC, id DESC
LIMIT :source_limit";
return $this->fetchRows($sql, $params);
}
/**
* @return array<int, array<string, mixed>>
*/
private function fetchOrderRows(int $departmentId, string $search): array
{
$where = [
'department_id = :department_id',
'reference IS NOT NULL',
"TRIM(reference) <> ''",
];
if ($this->tableHasColumn('orders', 'deleted_at')) {
array_unshift($where, 'deleted_at IS NULL');
}
$params = ['department_id' => $departmentId];
if ($search !== '') {
$where[] = 'LOWER(reference) LIKE :search';
$params['search'] = '%' . $this->lower($search) . '%';
}
$sql = "SELECT
'order' AS source,
id AS origin_id,
TRIM(reference) AS reference,
created_at AS source_created_at,
created_at AS used_at,
customer_id,
department_id,
reg_1,
reg_2,
reg_3
FROM orders
WHERE " . implode(' AND ', $where) . "
ORDER BY created_at DESC, id DESC
LIMIT :source_limit";
return $this->fetchRows($sql, $params);
}
/**
* @param array<int, string> $plates
* @return array<int, array<string, mixed>>
*/
private function fetchVehicleRows(int $departmentId, ?int $customerId, array $plates, string $search): array
{
$contextWhere = [];
$params = [];
if ($customerId !== null) {
$contextWhere[] = 'customer_id = :customer_id';
$params['customer_id'] = $customerId;
}
foreach ($plates as $index => $plate) {
$key = 'plate_' . $index;
$contextWhere[] = "UPPER(REPLACE(reg, ' ', '')) = :$key";
$params[$key] = $plate;
}
if ($contextWhere === []) {
return [];
}
$where = [
'reference IS NOT NULL',
"TRIM(reference) <> ''",
'(' . implode(' OR ', $contextWhere) . ')',
];
if ($this->tableHasColumn('customer_vehicles', 'deleted_at')) {
array_unshift($where, 'deleted_at IS NULL');
}
if ($search !== '') {
$where[] = 'LOWER(reference) LIKE :search';
$params['search'] = '%' . $this->lower($search) . '%';
}
$where[] = $this->vehicleDepartmentAccessPredicate();
$params['orders_department_id'] = $departmentId;
$params['bookings_department_id'] = $departmentId;
$sql = "SELECT
'vehicle' AS source,
id AS origin_id,
TRIM(reference) AS reference,
created_at AS source_created_at,
created_at AS used_at,
customer_id,
NULL AS department_id,
reg AS reg_1,
'' AS reg_2,
'' AS reg_3
FROM customer_vehicles
WHERE " . implode(' AND ', $where) . "
ORDER BY created_at DESC, id DESC
LIMIT :source_limit";
return $this->fetchRows($sql, $params);
}
private function vehicleDepartmentAccessPredicate(): string
{
$ordersWhere = [
'authorized_orders.department_id = :orders_department_id',
'(authorized_orders.customer_id = customer_vehicles.customer_id'
. " OR UPPER(REPLACE(authorized_orders.reg_1, ' ', '')) = UPPER(REPLACE(customer_vehicles.reg, ' ', ''))"
. " OR UPPER(REPLACE(authorized_orders.reg_2, ' ', '')) = UPPER(REPLACE(customer_vehicles.reg, ' ', ''))"
. " OR UPPER(REPLACE(authorized_orders.reg_3, ' ', '')) = UPPER(REPLACE(customer_vehicles.reg, ' ', '')))"
];
if ($this->tableHasColumn('orders', 'deleted_at')) {
$ordersWhere[] = 'authorized_orders.deleted_at IS NULL';
}
$bookingsWhere = [
'authorized_bookings.department = :bookings_department_id',
'(authorized_bookings.customer_number = customer_vehicles.customer_id'
. " OR UPPER(REPLACE(authorized_bookings.reg_1, ' ', '')) = UPPER(REPLACE(customer_vehicles.reg, ' ', ''))"
. " OR UPPER(REPLACE(authorized_bookings.reg_2, ' ', '')) = UPPER(REPLACE(customer_vehicles.reg, ' ', ''))"
. " OR UPPER(REPLACE(authorized_bookings.reg_3, ' ', '')) = UPPER(REPLACE(customer_vehicles.reg, ' ', '')))"
];
if ($this->tableHasColumn('order_bookings', 'deleted_at')) {
$bookingsWhere[] = 'authorized_bookings.deleted_at IS NULL';
}
return '(EXISTS (
SELECT 1
FROM orders authorized_orders
WHERE ' . implode(' AND ', $ordersWhere) . '
) OR EXISTS (
SELECT 1
FROM order_bookings authorized_bookings
WHERE ' . implode(' AND ', $bookingsWhere) . '
))';
}
/**
* @param array<string, mixed> $params
* @return array<int, array<string, mixed>>
*/
private function fetchRows(string $sql, array $params): array
{
$pdo = db::getPDO();
$statement = $pdo->prepare($sql);
foreach ($params as $key => $value) {
$statement->bindValue(':' . $key, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
}
$statement->bindValue(':source_limit', self::MAX_SOURCE_ROWS, PDO::PARAM_INT);
$statement->execute();
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
return is_array($rows) ? $rows : [];
}
private function tableHasColumn(string $table, string $column): bool
{
$cacheKey = $table . '.' . $column;
if (array_key_exists($cacheKey, $this->columnExistsCache)) {
return $this->columnExistsCache[$cacheKey];
}
$pdo = db::getPDO();
$statement = $pdo->prepare(
'SELECT COUNT(*) AS total
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = :table_name
AND COLUMN_NAME = :column_name'
);
$statement->bindValue(':table_name', $table, PDO::PARAM_STR);
$statement->bindValue(':column_name', $column, PDO::PARAM_STR);
$statement->execute();
$this->columnExistsCache[$cacheKey] = ((int)$statement->fetchColumn()) > 0;
return $this->columnExistsCache[$cacheKey];
}
/**
* @param array<int, array<string, mixed>> $rows
* @param array<int, string> $plates
* @return array<int, array<string, mixed>>
*/
private function aggregateRows(array $rows, string $search, ?int $customerId, array $plates): array
{
$groups = [];
foreach ($rows as $row) {
$reference = $this->normalizeText($row['reference'] ?? '');
if ($reference === '') {
continue;
}
$key = $this->lower($reference);
if (!isset($groups[$key])) {
$groups[$key] = [
'reference' => $reference,
'rows' => [],
'usage_count' => 0,
'last_used_at' => null,
'context_boost' => 0,
'section' => 'other',
];
}
$section = $this->contextSection($row, $customerId, $plates);
$groups[$key]['usage_count']++;
$groups[$key]['rows'][] = $row;
$groups[$key]['last_used_at'] = $this->maxDate(
$groups[$key]['last_used_at'],
$this->normalizeDate($row['used_at'] ?? null)
);
$groups[$key]['context_boost'] = max(
$groups[$key]['context_boost'],
$this->contextBoost($row, $customerId, $plates)
);
$groups[$key]['section'] = $this->bestSection(
(string)$groups[$key]['section'],
$section
);
}
$suggestions = [];
foreach ($groups as $group) {
$bestRow = $this->bestOriginRow($group['rows']);
if ($bestRow === null) {
continue;
}
$source = (string)($bestRow['source'] ?? 'order');
$usageCount = (int)$group['usage_count'];
$score = $this->matchScore((string)$group['reference'], $search)
+ (int)$group['context_boost']
+ $this->sectionScore((string)$group['section'])
+ $this->sourceScore($source)
+ min($usageCount, 20) * 5;
$suggestions[] = [
'source' => $source,
'section' => (string)$group['section'],
'reference' => (string)$group['reference'],
'source_created_at' => $this->normalizeDate($bestRow['source_created_at'] ?? null),
'last_used_at' => $group['last_used_at'],
'usage_count' => $usageCount,
'origin_id' => (int)($bestRow['origin_id'] ?? 0),
'score' => $score,
];
}
return $suggestions;
}
/**
* @param array<int, array<string, mixed>> $rows
*/
private function bestOriginRow(array $rows): ?array
{
usort($rows, function (array $left, array $right): int {
$sourceCompare = $this->sourceScore((string)($right['source'] ?? ''))
<=> $this->sourceScore((string)($left['source'] ?? ''));
if ($sourceCompare !== 0) {
return $sourceCompare;
}
$dateCompare = strcmp(
(string)$this->normalizeDate($right['source_created_at'] ?? null),
(string)$this->normalizeDate($left['source_created_at'] ?? null)
);
if ($dateCompare !== 0) {
return $dateCompare;
}
return ((int)($right['origin_id'] ?? 0)) <=> ((int)($left['origin_id'] ?? 0));
});
return $rows[0] ?? null;
}
private function sortSuggestions(array $left, array $right): int
{
$scoreCompare = ((int)($right['score'] ?? 0)) <=> ((int)($left['score'] ?? 0));
if ($scoreCompare !== 0) {
return $scoreCompare;
}
$usageCompare = ((int)($right['usage_count'] ?? 0)) <=> ((int)($left['usage_count'] ?? 0));
if ($usageCompare !== 0) {
return $usageCompare;
}
$sectionCompare = $this->sectionScore((string)($right['section'] ?? ''))
<=> $this->sectionScore((string)($left['section'] ?? ''));
if ($sectionCompare !== 0) {
return $sectionCompare;
}
$dateCompare = strcmp((string)($right['last_used_at'] ?? ''), (string)($left['last_used_at'] ?? ''));
if ($dateCompare !== 0) {
return $dateCompare;
}
$referenceCompare = strcmp((string)($left['reference'] ?? ''), (string)($right['reference'] ?? ''));
if ($referenceCompare !== 0) {
return $referenceCompare;
}
return $this->sourceScore((string)($right['source'] ?? '')) <=> $this->sourceScore((string)($left['source'] ?? ''));
}
/**
* @param array<int, string> $plates
*/
private function contextBoost(array $row, ?int $customerId, array $plates): int
{
$score = 0;
if ($customerId !== null && (int)($row['customer_id'] ?? 0) === $customerId) {
$score += 80;
}
if ($this->rowMatchesAnyPlate($row, $plates)) {
$score += 90;
}
return $score;
}
/**
* @param array<int, string> $plates
*/
private function contextSection(array $row, ?int $customerId, array $plates): string
{
if ($this->rowMatchesAnyPlate($row, $plates)) {
return 'this_vehicle';
}
if ($customerId !== null && (int)($row['customer_id'] ?? 0) === $customerId) {
return 'other_customer_vehicle';
}
return 'other';
}
/**
* @param array<int, string> $plates
*/
private function rowMatchesAnyPlate(array $row, array $plates): bool
{
$rowPlates = $this->normalizePlates([
$row['reg_1'] ?? '',
$row['reg_2'] ?? '',
$row['reg_3'] ?? '',
]);
return $plates !== [] && array_intersect($plates, $rowPlates) !== [];
}
private function matchScore(string $reference, string $search): int
{
if ($search === '') {
return 0;
}
$referenceKey = $this->lower($reference);
$searchKey = $this->lower($search);
if ($referenceKey === $searchKey) {
return 1000;
}
if (str_starts_with($referenceKey, $searchKey)) {
return 600;
}
if (str_contains($referenceKey, $searchKey)) {
return 300;
}
return 0;
}
private function sourceScore(string $source): int
{
return match ($source) {
'booking' => 30,
'order' => 20,
'vehicle' => 10,
default => 0,
};
}
private function sectionScore(string $section): int
{
return match ($section) {
'this_vehicle' => 40,
'other_customer_vehicle' => 20,
default => 0,
};
}
private function bestSection(string $left, string $right): string
{
return $this->sectionScore($right) > $this->sectionScore($left) ? $right : $left;
}
private function clampLimit(mixed $value): int
{
$limit = $this->toPositiveInt($value) ?? self::DEFAULT_LIMIT;
return max(1, min($limit, self::MAX_LIMIT));
}
private function toPositiveInt(mixed $value): ?int
{
$parsed = filter_var($value, FILTER_VALIDATE_INT);
return is_int($parsed) && $parsed > 0 ? $parsed : null;
}
private function normalizeText(mixed $value): string
{
return trim((string)($value ?? ''));
}
private function lower(string $value): string
{
return function_exists('mb_strtolower') ? mb_strtolower($value) : strtolower($value);
}
/**
* @param array<int, mixed> $values
* @return array<int, string>
*/
private function normalizePlates(array $values): array
{
$plates = [];
foreach ($values as $value) {
$plate = strtoupper(preg_replace('/\s+/', '', (string)($value ?? '')));
if ($plate !== '') {
$plates[] = $plate;
}
}
return array_values(array_unique($plates));
}
private function normalizeDate(mixed $value): ?string
{
$date = trim((string)($value ?? ''));
return $date === '' || $date === '0000-00-00 00:00:00' ? null : $date;
}
private function maxDate(?string $left, ?string $right): ?string
{
if ($left === null) {
return $right;
}
if ($right === null) {
return $left;
}
return strcmp($right, $left) > 0 ? $right : $left;
}
}
@@ -41,45 +41,9 @@ class orders_schema_bootstrap
); );
} }
self::backfillBookingPoDefaults($db);
self::ensureIndex($db, 'orders', 'idx_orders_period_customer_created_deleted', 'customer_id, created_at, deleted_at');
self::ensureIndex($db, 'orders', 'idx_orders_period_created_deleted_customer', 'created_at, deleted_at, customer_id');
self::ensureIndex($db, 'order_items', 'idx_order_items_order_deleted', 'order_id, deleted_at');
self::ensureIndex($db, 'customer_attributes', 'idx_customer_attributes_attribute_user', 'attribute, user_id');
self::$initialized = true; self::$initialized = true;
} }
private static function backfillBookingPoDefaults(object $db): void
{
if (
!self::tableExists($db, 'order_bookings')
|| !self::columnExists($db, 'orders', 'booking_id')
|| !self::columnExists($db, 'orders', 'po')
|| !self::columnExists($db, 'order_bookings', 'po')
|| !self::columnExists($db, 'order_bookings', 'customer_number')
|| !self::columnExists($db, 'order_bookings', 'department')
|| !self::columnExists($db, 'order_bookings', 'deleted_at')
) {
return;
}
$db->query(
"UPDATE orders o
INNER JOIN order_bookings b ON b.id = o.booking_id
AND b.customer_number = o.customer_id
AND b.department = o.department_id
AND b.deleted_at IS NULL
SET o.po = b.po
WHERE o.booking_id IS NOT NULL
AND o.booking_id > 0
AND (o.po IS NULL OR TRIM(o.po) = '')
AND b.po IS NOT NULL
AND TRIM(b.po) <> ''"
);
}
private static function tableExists(object $db, string $table): bool private static function tableExists(object $db, string $table): bool
{ {
$table = self::escapeIdentifier($table); $table = self::escapeIdentifier($table);
@@ -105,46 +69,6 @@ class orders_schema_bootstrap
return (int)$result->num_rows > 0; return (int)$result->num_rows > 0;
} }
private static function ensureIndex(object $db, string $table, string $index, string $columns): void
{
if (
!self::tableExists($db, $table)
|| self::indexExists($db, $table, $index)
|| !self::columnsExist($db, $table, $columns)
) {
return;
}
$table = self::escapeIdentifier($table);
$index = self::escapeIdentifier($index);
$db->query("ALTER TABLE `{$table}` ADD INDEX `{$index}` ({$columns})");
}
private static function columnsExist(object $db, string $table, string $columns): bool
{
foreach (explode(',', $columns) as $column) {
$column = trim($column, " \t\n\r\0\x0B`");
if ($column === '' || !self::columnExists($db, $table, $column)) {
return false;
}
}
return true;
}
private static function indexExists(object $db, string $table, string $index): bool
{
$table = self::escapeIdentifier($table);
$index = self::escapeIdentifier($index);
$result = $db->query("SHOW INDEX FROM `{$table}` WHERE Key_name = '{$index}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function escapeIdentifier(string $value): string private static function escapeIdentifier(string $value): string
{ {
return str_replace(['\\', "'", '`'], ['\\\\', "\\'", ''], $value); return str_replace(['\\', "'", '`'], ['\\\\', "\\'", ''], $value);
+1 -5
View File
@@ -47,12 +47,8 @@ class pdf_store implements minio_pdfs_i
*/ */
public function download(string $file): string public function download(string $file): string
{ {
if ($this->shouldUseLocalTestStorage()) {
return $this->getLocalTestObjectPath($file);
}
$path = '/tmp/' . $file; $path = '/tmp/' . $file;
self::getS3Client()->getObject([ $result = self::getS3Client()->getObject([
'Bucket' => self::getBucket(), 'Bucket' => self::getBucket(),
'Key' => $file, 'Key' => $file,
'SaveAs' => $path 'SaveAs' => $path
@@ -1,68 +0,0 @@
<?php
namespace classes;
/**
* Ensures additive schema for customer product price overrides.
*/
class price_overrides_schema_bootstrap
{
private static bool $initialized = false;
public static function ensureColumns(): void
{
if (self::$initialized) {
return;
}
global $db;
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
return;
}
if (!self::tableExists($db, 'price_overrides')) {
return;
}
if (!self::columnExists($db, 'price_overrides', 'fixed_price')) {
$db->query(
"ALTER TABLE price_overrides
ADD COLUMN fixed_price INT NULL DEFAULT NULL
AFTER percentage"
);
}
self::$initialized = true;
}
private static function tableExists(object $db, string $table): bool
{
$table = self::escapeIdentifier($table);
$result = $db->query("SHOW TABLES LIKE '{$table}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function columnExists(object $db, string $table, string $column): bool
{
$table = self::escapeIdentifier($table);
$column = self::escapeIdentifier($column);
$result = $db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function escapeIdentifier(string $value): string
{
return str_replace(['\\', "'", '`'], ['\\\\', "\\'", ''], $value);
}
}
@@ -1,68 +0,0 @@
<?php
namespace classes;
/**
* Ensures additive schema for product metadata used outside the core product form.
*/
class products_schema_bootstrap
{
private static bool $initialized = false;
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
return;
}
if (!self::tableExists($db, 'products')) {
return;
}
if (!self::columnExists($db, 'products', 'max_quantity_per_order')) {
$db->query(
"ALTER TABLE products
ADD COLUMN max_quantity_per_order INT NULL DEFAULT NULL
AFTER order_priority"
);
}
self::$initialized = true;
}
private static function tableExists(object $db, string $table): bool
{
$table = self::escapeIdentifier($table);
$result = $db->query("SHOW TABLES LIKE '{$table}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function columnExists(object $db, string $table, string $column): bool
{
$table = self::escapeIdentifier($table);
$column = self::escapeIdentifier($column);
$result = $db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
if ($result === false || !is_object($result) || !property_exists($result, 'num_rows')) {
return false;
}
return (int)$result->num_rows > 0;
}
private static function escapeIdentifier(string $value): string
{
return str_replace(['\\', "'", '`'], ['\\\\', "\\'", ''], $value);
}
}
-192
View File
@@ -364,194 +364,6 @@ class redis implements redis_i
return $this; return $this;
} }
/**
* @inheritDoc
*/
public function cache_invoice_period_manual_flags(array $flags): self
{
$this->set_array('invoice_period_manual_flags', $flags);
return $this;
}
/**
* @inheritDoc
*/
public function get_invoice_period_manual_flags(): array|null
{
return $this->get_array('invoice_period_manual_flags');
}
/**
* @inheritDoc
*/
public function clear_invoice_period_manual_flags(): self
{
$this->delete('invoice_period_manual_flags');
return $this;
}
private function invoicePeriodCacheKey(string $prefix, string $dateFrom, string $dateTo): string
{
return $prefix . ':'
. $this->normalizeInvoicePeriodCacheDate($dateFrom, true) . ':'
. $this->normalizeInvoicePeriodCacheDate($dateTo, false);
}
private function normalizeInvoicePeriodCacheDate(string $date, bool $startOfDay): string
{
$timestamp = strtotime($date);
if ($timestamp === false) {
return $date;
}
return date($startOfDay ? 'Y-m-d 00:00:00' : 'Y-m-d 23:59:59', $timestamp);
}
private function workfeedEmployeeNameCacheKey(string $employeeId): string
{
return 'workfeed_employee_name:' . rawurlencode($employeeId);
}
/**
* @inheritDoc
*/
public function cache_invoice_period_automatic_flags(string $dateFrom, string $dateTo, array $flags): self
{
$this->set_array($this->invoicePeriodCacheKey('invoice_period_automatic_flags', $dateFrom, $dateTo), $flags);
return $this;
}
/**
* @inheritDoc
*/
public function get_invoice_period_automatic_flags(string $dateFrom, string $dateTo): array|null
{
return $this->get_array($this->invoicePeriodCacheKey('invoice_period_automatic_flags', $dateFrom, $dateTo));
}
/**
* @inheritDoc
*/
public function clear_invoice_period_automatic_flags(string $dateFrom, string $dateTo): self
{
$this->delete($this->invoicePeriodCacheKey('invoice_period_automatic_flags', $dateFrom, $dateTo));
return $this;
}
/**
* @inheritDoc
*/
public function cache_invoice_period_order_item_rows(string $dateFrom, string $dateTo, array $rows): self
{
$this->set_array($this->invoicePeriodCacheKey('invoice_period_order_item_rows', $dateFrom, $dateTo), $rows);
return $this;
}
/**
* @inheritDoc
*/
public function get_invoice_period_order_item_rows(string $dateFrom, string $dateTo): array|null
{
return $this->get_array($this->invoicePeriodCacheKey('invoice_period_order_item_rows', $dateFrom, $dateTo));
}
/**
* @inheritDoc
*/
public function clear_invoice_period_order_item_rows(string $dateFrom, string $dateTo): self
{
$this->delete($this->invoicePeriodCacheKey('invoice_period_order_item_rows', $dateFrom, $dateTo));
return $this;
}
/**
* @inheritDoc
*/
public function cache_workfeed_employee_name(string $employeeId, string $employeeName, int $ttl = 86400): self
{
$normalizedEmployeeId = trim($employeeId);
if ($normalizedEmployeeId === '') {
return $this;
}
$normalizedEmployeeName = trim($employeeName);
if ($normalizedEmployeeName === '') {
return $this;
}
$key = $this->workfeedEmployeeNameCacheKey($normalizedEmployeeId);
$this->set($key, $normalizedEmployeeName);
$this->expire($key, $ttl);
return $this;
}
/**
* @inheritDoc
*/
public function get_workfeed_employee_name(string $employeeId): string|null
{
$normalizedEmployeeId = trim($employeeId);
if ($normalizedEmployeeId === '') {
return null;
}
$value = $this->get($this->workfeedEmployeeNameCacheKey($normalizedEmployeeId));
if ($value === null) {
return null;
}
$normalized = trim((string)$value);
return $normalized !== '' ? $normalized : null;
}
/**
* @inheritDoc
*/
public function clear_workfeed_employee_name(string $employeeId): self
{
$normalizedEmployeeId = trim($employeeId);
if ($normalizedEmployeeId === '') {
return $this;
}
$this->delete($this->workfeedEmployeeNameCacheKey($normalizedEmployeeId));
return $this;
}
/**
* @inheritDoc
*/
public function enqueue_invoice_period_warming(string $dateFrom, string $dateTo): self
{
$this->get_client()->sadd('invoice_period_warming_queue', [
$this->normalizeInvoicePeriodCacheDate($dateFrom, true)
. '|'
. $this->normalizeInvoicePeriodCacheDate($dateTo, false),
]);
return $this;
}
/**
* @inheritDoc
*/
public function consume_invoice_period_warming_queue(): array
{
$client = $this->get_client();
$members = $client->smembers('invoice_period_warming_queue');
if (!empty($members)) {
$client->del('invoice_period_warming_queue');
}
$periods = [];
foreach ($members as $member) {
$parts = explode('|', (string)$member, 2);
if (count($parts) === 2 && $parts[0] !== '' && $parts[1] !== '') {
$periods[] = ['dateFrom' => $parts[0], 'dateTo' => $parts[1]];
}
}
return $periods;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -659,10 +471,6 @@ class redis implements redis_i
public function mget(array $array_map): array public function mget(array $array_map): array
{ {
if (empty($array_map)) {
return [];
}
// Get multiple keys from Redis // Get multiple keys from Redis
return $this->redis->mget($array_map); return $this->redis->mget($array_map);
} }
File diff suppressed because it is too large Load Diff
@@ -1,557 +0,0 @@
<?php
namespace classes;
/**
* Additive schema bootstrap for application release management.
*
* The legacy API does not have a centralized migration runner, so these
* migrations must be safe to call from request handlers, tests, and cron.
*/
class release_manager_schema_bootstrap
{
private static bool $initialized = false;
private static ?bool $tablesExist = null;
public static function ensureTables(): void
{
if (self::$initialized) {
return;
}
global $db;
$queries = [
"CREATE TABLE IF NOT EXISTS release_channels (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
slug VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
description TEXT NULL,
enabled TINYINT(1) NOT NULL DEFAULT 1,
default_channel TINYINT(1) NOT NULL DEFAULT 0,
rollout_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00,
frontend_base_url VARCHAR(512) NULL,
api_base_url VARCHAR(512) NULL,
replay_enabled TINYINT(1) NOT NULL DEFAULT 0,
capture_level VARCHAR(32) NOT NULL DEFAULT 'metadata',
retention_days INT NOT NULL DEFAULT 14,
metadata_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
UNIQUE KEY uq_release_channels_slug (slug),
INDEX idx_release_channels_enabled (enabled, deleted_at),
INDEX idx_release_channels_default (default_channel, deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_versions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
app VARCHAR(16) NOT NULL,
repository VARCHAR(255) NULL,
branch VARCHAR(128) NULL,
commit_sha VARCHAR(64) NULL,
tag VARCHAR(128) NULL,
version_label VARCHAR(128) NULL,
build_url VARCHAR(512) NULL,
artifact_url VARCHAR(512) NULL,
deployed_url VARCHAR(512) NULL,
status VARCHAR(32) NOT NULL DEFAULT 'discovered',
metadata_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deployed_at DATETIME NULL,
INDEX idx_release_versions_app_status (app, status),
INDEX idx_release_versions_commit (commit_sha),
INDEX idx_release_versions_repo_branch (repository, branch)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_channel_versions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NOT NULL,
frontend_version_id BIGINT UNSIGNED NULL,
api_version_id BIGINT UNSIGNED NULL,
deployment_id BIGINT UNSIGNED NULL,
service_set_id BIGINT UNSIGNED NULL,
bundle_id BIGINT UNSIGNED NULL,
actor_user_id INT NULL,
active TINYINT(1) NOT NULL DEFAULT 1,
activated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_release_channel_versions_channel_active (channel_id, active),
INDEX idx_release_channel_versions_frontend (frontend_version_id),
INDEX idx_release_channel_versions_api (api_version_id),
INDEX idx_release_channel_versions_deployment (deployment_id),
INDEX idx_release_channel_versions_service_set (service_set_id),
INDEX idx_release_channel_versions_bundle (bundle_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_assignments (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
subject_type VARCHAR(16) NOT NULL,
subject_id VARCHAR(64) NOT NULL,
channel_id BIGINT UNSIGNED NOT NULL,
reason VARCHAR(255) NULL,
expires_at DATETIME NULL,
actor_user_id INT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
INDEX idx_release_assignments_subject (subject_type, subject_id, deleted_at, expires_at),
INDEX idx_release_assignments_channel (channel_id, deleted_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_deployment_targets (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NOT NULL,
app VARCHAR(16) NOT NULL,
coolify_instance_id BIGINT UNSIGNED NULL,
coolify_service_uuid VARCHAR(128) NULL,
repository VARCHAR(255) NOT NULL,
branch VARCHAR(128) NOT NULL DEFAULT 'main',
auto_deploy TINYINT(1) NOT NULL DEFAULT 1,
health_url VARCHAR(512) NULL,
deploy_context_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
INDEX idx_release_targets_channel_app (channel_id, app, deleted_at),
INDEX idx_release_targets_repo_branch (repository, branch, auto_deploy, deleted_at),
INDEX idx_release_targets_coolify (coolify_instance_id, coolify_service_uuid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_auto_sync_events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NOT NULL,
app VARCHAR(16) NOT NULL,
repository VARCHAR(255) NOT NULL,
branch VARCHAR(128) NOT NULL,
commit_sha VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
source VARCHAR(64) NULL,
workflow_url VARCHAR(512) NULL,
gate_operation_id BIGINT UNSIGNED NULL,
sync_operation_id BIGINT UNSIGNED NULL,
deployment_id BIGINT UNSIGNED NULL,
error_message TEXT NULL,
metadata_json LONGTEXT NULL,
received_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
gate_passed_at DATETIME NULL,
synced_at DATETIME NULL,
promoted_at DATETIME NULL,
failed_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uq_release_auto_sync_event (channel_id, app, repository, branch, commit_sha),
INDEX idx_release_auto_sync_channel_status (channel_id, status, updated_at),
INDEX idx_release_auto_sync_gate (gate_operation_id),
INDEX idx_release_auto_sync_sync (sync_operation_id),
INDEX idx_release_auto_sync_deployment (deployment_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_service_sets (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NULL,
name VARCHAR(128) NOT NULL,
slug VARCHAR(64) NOT NULL,
mode VARCHAR(32) NOT NULL DEFAULT 'attach_existing',
source_service_set_id BIGINT UNSIGNED NULL,
frontend_target_id BIGINT UNSIGNED NULL,
api_target_id BIGINT UNSIGNED NULL,
database_coolify_target_id BIGINT UNSIGNED NULL,
redis_coolify_target_id BIGINT UNSIGNED NULL,
minio_coolify_target_id BIGINT UNSIGNED NULL,
status VARCHAR(32) NOT NULL DEFAULT 'needs_configuration',
health_json LONGTEXT NULL,
metadata_json LONGTEXT NULL,
actor_user_id INT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
UNIQUE KEY uq_release_service_sets_slug (slug),
INDEX idx_release_service_sets_channel (channel_id, deleted_at),
INDEX idx_release_service_sets_source (source_service_set_id),
INDEX idx_release_service_sets_status (status, deleted_at),
INDEX idx_release_service_sets_frontend_target (frontend_target_id),
INDEX idx_release_service_sets_api_target (api_target_id),
INDEX idx_release_service_sets_database_target (database_coolify_target_id),
INDEX idx_release_service_sets_redis_target (redis_coolify_target_id),
INDEX idx_release_service_sets_minio_target (minio_coolify_target_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_deployments (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NOT NULL,
target_id BIGINT UNSIGNED NULL,
version_id BIGINT UNSIGNED NULL,
service_set_id BIGINT UNSIGNED NULL,
bundle_id BIGINT UNSIGNED NULL,
deployment_kind VARCHAR(32) NOT NULL DEFAULT 'single_app',
app VARCHAR(16) NOT NULL,
active_channel_app_key VARCHAR(96) NULL,
provider VARCHAR(32) NOT NULL DEFAULT 'coolify',
repository VARCHAR(255) NULL,
branch VARCHAR(128) NULL,
commit_sha VARCHAR(64) NULL,
status VARCHAR(32) NOT NULL DEFAULT 'queued',
provider_operation_id VARCHAR(128) NULL,
deployment_url VARCHAR(512) NULL,
actor_user_id INT NULL,
requested_payload_json LONGTEXT NULL,
result_json LONGTEXT NULL,
error_message TEXT NULL,
started_at DATETIME NULL,
completed_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_release_deployments_channel_status (channel_id, status),
INDEX idx_release_deployments_target (target_id),
INDEX idx_release_deployments_version (version_id),
INDEX idx_release_deployments_service_set (service_set_id),
INDEX idx_release_deployments_bundle (bundle_id),
INDEX idx_release_deployments_kind (deployment_kind),
INDEX idx_release_deployments_commit (commit_sha)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_bundles (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NOT NULL,
service_set_id BIGINT UNSIGNED NOT NULL,
version_label VARCHAR(128) NULL,
frontend_version_id BIGINT UNSIGNED NULL,
api_version_id BIGINT UNSIGNED NULL,
frontend_deployment_id BIGINT UNSIGNED NULL,
api_deployment_id BIGINT UNSIGNED NULL,
frontend_repository VARCHAR(255) NULL,
frontend_branch VARCHAR(128) NULL,
frontend_commit_sha VARCHAR(64) NULL,
api_repository VARCHAR(255) NULL,
api_branch VARCHAR(128) NULL,
api_commit_sha VARCHAR(64) NULL,
status VARCHAR(32) NOT NULL DEFAULT 'draft',
deployment_result_json LONGTEXT NULL,
metadata_json LONGTEXT NULL,
actor_user_id INT NULL,
deployed_at DATETIME NULL,
promoted_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
INDEX idx_release_bundles_channel_status (channel_id, status, deleted_at),
INDEX idx_release_bundles_service_set (service_set_id, status, deleted_at),
INDEX idx_release_bundles_frontend_version (frontend_version_id),
INDEX idx_release_bundles_api_version (api_version_id),
INDEX idx_release_bundles_frontend_deployment (frontend_deployment_id),
INDEX idx_release_bundles_api_deployment (api_deployment_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_replay_targets (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
target_type VARCHAR(16) NOT NULL,
target_id VARCHAR(64) NULL,
channel_id BIGINT UNSIGNED NULL,
capture_level VARCHAR(32) NOT NULL DEFAULT 'full_redacted',
enabled TINYINT(1) NOT NULL DEFAULT 1,
expires_at DATETIME NULL,
actor_user_id INT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at DATETIME NULL,
INDEX idx_release_replay_target (target_type, target_id, enabled, deleted_at, expires_at),
INDEX idx_release_replay_channel (channel_id, enabled, deleted_at, expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_timeline_sessions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
trace_id VARCHAR(64) NOT NULL,
session_hash VARCHAR(64) NULL,
principal_type VARCHAR(16) NULL,
principal_id VARCHAR(64) NULL,
customer_number INT NULL,
channel_id BIGINT UNSIGNED NULL,
channel_slug VARCHAR(64) NULL,
frontend_version_id BIGINT UNSIGNED NULL,
api_version_id BIGINT UNSIGNED NULL,
device_type VARCHAR(16) NULL,
browser_name VARCHAR(64) NULL,
browser_version VARCHAR(64) NULL,
os_name VARCHAR(64) NULL,
os_version VARCHAR(64) NULL,
viewport_width INT NULL,
viewport_height INT NULL,
device_pixel_ratio DECIMAL(6,3) NULL,
frontend_version_label VARCHAR(128) NULL,
frontend_commit_sha VARCHAR(128) NULL,
api_version_label VARCHAR(128) NULL,
api_commit_sha VARCHAR(128) NULL,
last_route_path VARCHAR(255) NULL,
user_agent VARCHAR(512) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_seen_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uq_release_timeline_trace (trace_id),
INDEX idx_release_timeline_principal (principal_type, principal_id),
INDEX idx_release_timeline_channel (channel_id, channel_slug),
INDEX idx_release_timeline_device (device_type),
INDEX idx_release_timeline_release (frontend_version_label, api_version_label),
INDEX idx_release_timeline_customer (customer_number),
INDEX idx_release_timeline_last_seen (last_seen_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_timeline_events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
timeline_session_id BIGINT UNSIGNED NULL,
trace_id VARCHAR(64) NOT NULL,
event_type VARCHAR(64) NOT NULL,
severity VARCHAR(16) NOT NULL DEFAULT 'info',
module_key VARCHAR(64) NULL,
route_path VARCHAR(255) NULL,
component VARCHAR(255) NULL,
request_id VARCHAR(128) NULL,
occurred_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
payload_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_release_timeline_events_session (timeline_session_id, occurred_at),
INDEX idx_release_timeline_events_trace (trace_id, occurred_at),
INDEX idx_release_timeline_events_type (event_type, severity),
INDEX idx_release_timeline_events_module (module_key, occurred_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_module_health_snapshots (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NULL,
module_key VARCHAR(64) NOT NULL,
status VARCHAR(32) NOT NULL,
reason VARCHAR(512) NULL,
payload_json LONGTEXT NULL,
checked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_release_module_health_module (module_key, checked_at),
INDEX idx_release_module_health_channel (channel_id, module_key, checked_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_operation_runs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
operation_type VARCHAR(64) NOT NULL,
subject_type VARCHAR(64) NULL,
subject_id VARCHAR(128) NULL,
channel_id BIGINT UNSIGNED NULL,
app VARCHAR(16) NULL,
status VARCHAR(32) NOT NULL DEFAULT 'queued',
title VARCHAR(255) NULL,
summary TEXT NULL,
solution_hint TEXT NULL,
actor_user_id INT NULL,
context_json LONGTEXT NULL,
started_at DATETIME NULL,
completed_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_release_operation_runs_channel (channel_id, created_at),
INDEX idx_release_operation_runs_subject (subject_type, subject_id, created_at),
INDEX idx_release_operation_runs_type_status (operation_type, status),
INDEX idx_release_operation_runs_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_operation_steps (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
operation_run_id BIGINT UNSIGNED NOT NULL,
step_key VARCHAR(64) NOT NULL,
label VARCHAR(255) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'queued',
message TEXT NULL,
diagnostic TEXT NULL,
solution_hint TEXT NULL,
context_json LONGTEXT NULL,
started_at DATETIME NULL,
completed_at DATETIME NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_release_operation_steps_run (operation_run_id, id),
INDEX idx_release_operation_steps_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS release_audit_logs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
channel_id BIGINT UNSIGNED NULL,
deployment_id BIGINT UNSIGNED NULL,
action VARCHAR(64) NOT NULL,
actor_user_id INT NULL,
severity VARCHAR(16) NOT NULL DEFAULT 'info',
context_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_release_audit_channel (channel_id, created_at),
INDEX idx_release_audit_deployment (deployment_id),
INDEX idx_release_audit_action (action)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
];
foreach ($queries as $sql) {
$db->query($sql);
}
self::ensureColumn('release_channel_versions', 'service_set_id', 'BIGINT UNSIGNED NULL AFTER deployment_id');
self::ensureColumn('release_channel_versions', 'bundle_id', 'BIGINT UNSIGNED NULL AFTER service_set_id');
self::ensureColumn('release_deployments', 'service_set_id', 'BIGINT UNSIGNED NULL AFTER version_id');
self::ensureColumn('release_deployments', 'bundle_id', 'BIGINT UNSIGNED NULL AFTER service_set_id');
self::ensureColumn('release_deployments', 'deployment_kind', "VARCHAR(32) NOT NULL DEFAULT 'single_app' AFTER bundle_id");
self::ensureColumn('release_deployments', 'active_channel_app_key', 'VARCHAR(96) NULL AFTER app');
self::ensureColumn('release_timeline_sessions', 'device_type', 'VARCHAR(16) NULL AFTER api_version_id');
self::ensureColumn('release_timeline_sessions', 'browser_name', 'VARCHAR(64) NULL AFTER device_type');
self::ensureColumn('release_timeline_sessions', 'browser_version', 'VARCHAR(64) NULL AFTER browser_name');
self::ensureColumn('release_timeline_sessions', 'os_name', 'VARCHAR(64) NULL AFTER browser_version');
self::ensureColumn('release_timeline_sessions', 'os_version', 'VARCHAR(64) NULL AFTER os_name');
self::ensureColumn('release_timeline_sessions', 'viewport_width', 'INT NULL AFTER os_version');
self::ensureColumn('release_timeline_sessions', 'viewport_height', 'INT NULL AFTER viewport_width');
self::ensureColumn('release_timeline_sessions', 'device_pixel_ratio', 'DECIMAL(6,3) NULL AFTER viewport_height');
self::ensureColumn('release_timeline_sessions', 'frontend_version_label', 'VARCHAR(128) NULL AFTER device_pixel_ratio');
self::ensureColumn('release_timeline_sessions', 'frontend_commit_sha', 'VARCHAR(128) NULL AFTER frontend_version_label');
self::ensureColumn('release_timeline_sessions', 'api_version_label', 'VARCHAR(128) NULL AFTER frontend_commit_sha');
self::ensureColumn('release_timeline_sessions', 'api_commit_sha', 'VARCHAR(128) NULL AFTER api_version_label');
self::ensureColumn('release_timeline_sessions', 'last_route_path', 'VARCHAR(255) NULL AFTER api_commit_sha');
self::ensureIndex('release_timeline_sessions', 'idx_release_timeline_device', 'device_type');
self::ensureIndex('release_timeline_sessions', 'idx_release_timeline_release', 'frontend_version_label, api_version_label');
self::ensureUniqueIndex('release_deployments', 'uniq_release_deployments_active_channel_app', 'active_channel_app_key');
self::ensureModuleConfigDefault('ReleaseManager', 'enabled', 'true', 'bool');
self::ensureModuleConfigDefault('ReleaseManager', 'github_webhook_secret', '', 'string');
self::ensureModuleConfigDefault('ReleaseManager', 'release_gate_token', '', 'string');
self::ensureModuleConfigDefault('ReleaseManager', 'release_gate_required_for_promotion', 'true', 'bool');
self::ensureModuleConfigDefault('ReleaseManager', 'github_token', '', 'string');
self::ensureModuleConfigDefault('ReleaseManager', 'github_api_url', 'https://api.github.com', 'string');
self::ensureModuleConfigDefault('ReleaseManager', 'default_retention_days', '14', 'int');
self::ensureDefaultChannels();
self::$initialized = true;
self::$tablesExist = true;
}
public static function tablesExist(): bool
{
if (self::$tablesExist !== null) {
return self::$tablesExist;
}
global $db;
foreach ([
'release_channels',
'release_versions',
'release_channel_versions',
'release_assignments',
'release_auto_sync_events',
'release_service_sets',
'release_deployments',
'release_bundles',
'release_operation_runs',
'release_operation_steps',
'release_timeline_sessions',
'release_timeline_events',
] as $table) {
$tableSql = $db->escape_string($table);
$result = $db->query("SHOW TABLES LIKE '$tableSql'");
if ($result === false || $result->num_rows === 0) {
self::$tablesExist = false;
return false;
}
}
self::$tablesExist = true;
return true;
}
private static function ensureDefaultChannels(): void
{
global $db;
$channels = [
['stable', 'Stable', 'Default production channel.', 1, 1, 'metadata'],
['canary', 'Canary', 'Earliest production validation channel.', 1, 0, 'metadata'],
['beta', 'Beta', 'Broader pre-stable rollout channel.', 1, 0, 'metadata'],
['internal', 'Internal', 'Internal staff and superuser validation channel.', 1, 0, 'metadata'],
];
foreach ($channels as [$slug, $name, $description, $enabled, $default, $captureLevel]) {
$db->query(sprintf(
"INSERT IGNORE INTO release_channels (slug, name, description, enabled, default_channel, capture_level)
VALUES ('%s', '%s', '%s', %d, %d, '%s')",
$db->escape_string($slug),
$db->escape_string($name),
$db->escape_string($description),
(int)$enabled,
(int)$default,
$db->escape_string($captureLevel)
));
}
}
private static function ensureModuleConfigDefault(string $module, string $variable, string $value, string $type): void
{
global $db;
$moduleSql = $db->escape_string($module);
$variableSql = $db->escape_string($variable);
$result = $db->query("SELECT value FROM module_config WHERE module = '$moduleSql' AND variable = '$variableSql' LIMIT 1");
if ($result !== false && $result->num_rows > 0) {
return;
}
$valueSql = $db->escape_string($value);
$typeSql = $db->escape_string($type);
$db->query("INSERT INTO module_config (module, variable, value, type) VALUES ('$moduleSql', '$variableSql', '$valueSql', '$typeSql')");
}
private static function ensureColumn(string $table, string $column, string $definition): void
{
global $db;
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
$column = preg_replace('/[^a-zA-Z0-9_]/', '', $column);
if ($table === '' || $column === '') {
return;
}
$result = $db->query("SHOW COLUMNS FROM `$table` LIKE '$column'");
if ($result !== false && $result->num_rows > 0) {
return;
}
$db->query("ALTER TABLE `$table` ADD COLUMN `$column` $definition");
}
private static function ensureIndex(string $table, string $index, string $columns): void
{
global $db;
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
$index = preg_replace('/[^a-zA-Z0-9_]/', '', $index);
if ($table === '' || $index === '') {
return;
}
$indexSql = $db->escape_string($index);
$result = $db->query("SHOW INDEX FROM `$table` WHERE Key_name = '$indexSql'");
if ($result !== false && $result->num_rows > 0) {
return;
}
$db->query("ALTER TABLE `$table` ADD INDEX `$index` ($columns)");
}
private static function ensureUniqueIndex(string $table, string $index, string $columns): void
{
global $db;
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
$index = preg_replace('/[^a-zA-Z0-9_]/', '', $index);
if ($table === '' || $index === '') {
return;
}
$indexSql = $db->escape_string($index);
$result = $db->query("SHOW INDEX FROM `$table` WHERE Key_name = '$indexSql'");
if ($result !== false && $result->num_rows > 0) {
return;
}
$db->query("ALTER TABLE `$table` ADD UNIQUE KEY `$index` ($columns)");
}
}
@@ -1,19 +0,0 @@
<?php
namespace classes;
class releasemanager
{
public function isEnabled(): bool
{
try {
release_manager_schema_bootstrap::ensureTables();
global $db;
$result = $db->query("SELECT value FROM module_config WHERE module = 'ReleaseManager' AND variable = 'enabled' LIMIT 1");
$row = $result ? $result->fetch_assoc() : null;
return in_array(strtolower(trim((string)($row['value'] ?? 'true'))), ['1', 'true', 'yes', 'on'], true);
} catch (\Throwable) {
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show More