Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85eb0039d1 |
+1
-7
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Default branch protection
|
|
||||||
|
|
||||||
`master` is changed through pull requests. Do not push or publish directly to
|
|
||||||
the default branch, including through automation or the Git Data API.
|
|
||||||
|
|
||||||
## Normal publishing flow
|
|
||||||
|
|
||||||
1. Create a scoped `agent/*` or feature branch from the current `origin/master`.
|
|
||||||
2. Commit and push only the intended changes.
|
|
||||||
3. Open a pull request targeting `master`.
|
|
||||||
4. Wait for the `Required CI` check. If `master` moves, update the branch and
|
|
||||||
wait for the strict check to rerun.
|
|
||||||
5. Resolve every review conversation and squash-merge the pull request.
|
|
||||||
6. Confirm the post-merge `Release Manager gate` completes on `master`.
|
|
||||||
|
|
||||||
The aggregate check covers the PHP unit, integration, API, and legacy matrix,
|
|
||||||
plus Edge Agent, Edge Broker, and Edge Gateway Backend. Qodana is advisory and
|
|
||||||
the Release Manager gate is intentionally post-merge.
|
|
||||||
|
|
||||||
## Desired ruleset
|
|
||||||
|
|
||||||
[`rulesets/protect-default-branch.json`](rulesets/protect-default-branch.json)
|
|
||||||
is the importable final desired-state repository-ruleset request body. For the
|
|
||||||
initial POST, copy the file and override `enforcement` to `disabled`. Inspect
|
|
||||||
the normalized ruleset and verify a green preparation PR and post-merge run,
|
|
||||||
then PUT the exact committed file to activate it.
|
|
||||||
|
|
||||||
The desired rule targets `~DEFAULT_BRANCH`, requires pull requests with zero
|
|
||||||
approvals, conversation resolution, strict `Required CI` from GitHub Actions
|
|
||||||
integration `15368`, squash-only linear history, and blocks deletion and force
|
|
||||||
pushes. Repository administrators receive pull-request-only bypass; they do not
|
|
||||||
receive a standing direct-push bypass.
|
|
||||||
|
|
||||||
When the ruleset is activated, align repository settings at the same time:
|
|
||||||
retain squash merging, disable merge commits and rebase merging, enable
|
|
||||||
auto-merge and branch-update suggestions, delete merged branches automatically,
|
|
||||||
keep the Actions token read-only, and prevent Actions from approving reviews.
|
|
||||||
|
|
||||||
## Break glass
|
|
||||||
|
|
||||||
When an incident cannot wait for the normal gate:
|
|
||||||
|
|
||||||
1. Open a pull request and describe the incident, risk, and reason for bypass.
|
|
||||||
2. Have a repository administrator use the pull-request-only bypass.
|
|
||||||
3. Monitor `Required CI` and the post-merge Release Manager workflow.
|
|
||||||
4. Open a follow-up pull request for any deferred validation or remediation.
|
|
||||||
|
|
||||||
Never bypass by updating `refs/heads/master` directly. Ruleset changes and
|
|
||||||
emergency bypasses must remain visible in GitHub's audit trail.
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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}"
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Protect default branch",
|
|
||||||
"target": "branch",
|
|
||||||
"enforcement": "active",
|
|
||||||
"bypass_actors": [
|
|
||||||
{
|
|
||||||
"actor_id": 5,
|
|
||||||
"actor_type": "RepositoryRole",
|
|
||||||
"bypass_mode": "pull_request"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"conditions": {
|
|
||||||
"ref_name": {
|
|
||||||
"exclude": [],
|
|
||||||
"include": [
|
|
||||||
"~DEFAULT_BRANCH"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"type": "deletion"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "non_fast_forward"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "required_linear_history"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "pull_request",
|
|
||||||
"parameters": {
|
|
||||||
"allowed_merge_methods": [
|
|
||||||
"squash"
|
|
||||||
],
|
|
||||||
"dismiss_stale_reviews_on_push": false,
|
|
||||||
"require_code_owner_review": false,
|
|
||||||
"require_last_push_approval": false,
|
|
||||||
"required_approving_review_count": 0,
|
|
||||||
"required_review_thread_resolution": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "required_status_checks",
|
|
||||||
"parameters": {
|
|
||||||
"do_not_enforce_on_create": false,
|
|
||||||
"required_status_checks": [
|
|
||||||
{
|
|
||||||
"context": "Required CI",
|
|
||||||
"integration_id": 15368
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"strict_required_status_checks_policy": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -3,52 +3,26 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: # Specify your branches here
|
||||||
- master
|
- main # The 'main' branch
|
||||||
- beta
|
- 'releases/*' # The release branches
|
||||||
- canary
|
|
||||||
- internal
|
|
||||||
|
|
||||||
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."
|
|
||||||
|
|||||||
+167
-338
@@ -3,74 +3,58 @@ name: Tests
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- beta
|
|
||||||
- canary
|
|
||||||
- internal
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }}
|
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
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
|
||||||
@@ -80,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
|
||||||
@@ -115,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: |
|
||||||
@@ -150,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
|
||||||
@@ -159,271 +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
|
||||||
|
|
||||||
required-ci:
|
services:
|
||||||
name: Required CI
|
redis:
|
||||||
runs-on: ubuntu-latest
|
image: redis:7
|
||||||
needs: [php, edge-agent, edge-broker, edge-gateway-backend]
|
ports:
|
||||||
if: ${{ always() }}
|
- 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: Verify required jobs succeeded
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.2'
|
||||||
|
extensions: mysqli, curl, openssl, json, redis
|
||||||
|
ini-values: variables_order=EGPCS
|
||||||
|
|
||||||
|
- name: Resolve dependencies
|
||||||
|
working-directory: services/nginx/app
|
||||||
|
run: composer update --no-interaction --prefer-dist
|
||||||
|
|
||||||
|
- name: Run API tests
|
||||||
|
working-directory: services/nginx/app
|
||||||
env:
|
env:
|
||||||
PHP_RESULT: ${{ needs.php.result }}
|
RUN_API_TESTS: '1'
|
||||||
EDGE_AGENT_RESULT: ${{ needs.edge-agent.result }}
|
API_TEST_BOOTSTRAP_SCHEMA: '1'
|
||||||
EDGE_BROKER_RESULT: ${{ needs.edge-broker.result }}
|
USE_ENV: 'true'
|
||||||
EDGE_GATEWAY_BACKEND_RESULT: ${{ needs.edge-gateway-backend.result }}
|
DEBUG: '0'
|
||||||
run: |
|
ENCRYPTION_KEY: test-key
|
||||||
set -euo pipefail
|
CORS: '*'
|
||||||
failed=0
|
CONFIG_TIMEZONE: Europe/Copenhagen
|
||||||
for dependency in \
|
ECONOMIC_API_APP_ACCESS_GRANT: test
|
||||||
"php=${PHP_RESULT}" \
|
ECONOMIC_API_APP_ACCESS_GRANT2: test
|
||||||
"edge-agent=${EDGE_AGENT_RESULT}" \
|
ECONOMIC_API_APP_SECRET_TOKEN: test
|
||||||
"edge-broker=${EDGE_BROKER_RESULT}" \
|
WORDPRESS_STATIC_TOKEN: ''
|
||||||
"edge-gateway-backend=${EDGE_GATEWAY_BACKEND_RESULT}"
|
EMAIL_WASH_CERTIFICATE_TOKEN: ''
|
||||||
do
|
WORDPRESS_API_URL: http://localhost
|
||||||
name="${dependency%%=*}"
|
MINIO_ENDPOINT: ''
|
||||||
result="${dependency#*=}"
|
MINIO_ACCESS_KEY: ''
|
||||||
if [ "$result" != "success" ]; then
|
MINIO_SECRET_KEY: ''
|
||||||
echo "Required dependency ${name} completed with result: ${result:-missing}" >&2
|
SLACK_DEFAULT_WEBHOOK: ''
|
||||||
failed=1
|
REDIS_CONFIG_HOST: 127.0.0.1
|
||||||
fi
|
REDIS_CONFIG_DATABASE: '0'
|
||||||
done
|
REDIS_CONFIG_PASSWORD: ''
|
||||||
test "$failed" -eq 0
|
REDIS_CONFIG_PORT: '6379'
|
||||||
|
CONFIG_DB_HOST: 127.0.0.1
|
||||||
release-manager-gate:
|
CONFIG_DB_USER: root
|
||||||
name: Release Manager gate
|
CONFIG_DB_PASSWORD: root
|
||||||
runs-on: [self-hosted, Linux, X64, pleno, backend]
|
CONFIG_DB_DATABASE: app_test
|
||||||
needs: [required-ci]
|
CONFIG_DB_PORT: '3306'
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && needs.required-ci.result == 'success' }}
|
run: composer test:api
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Record Release Manager API gate
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
printf '%s\n' "$response_body"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if printf '%s' "$response_body" | grep -qi '<b>Parse error</b>'; then
|
|
||||||
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."
|
|
||||||
printf '%s\n' "$response_body"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '%s\n' "$response_body"
|
|
||||||
echo "Release Manager gate failed with HTTP $http_code." >&2
|
|
||||||
exit 1
|
|
||||||
env:
|
|
||||||
RELEASE_MANAGER_GATE_URL: ${{ secrets.RELEASE_MANAGER_GATE_URL || 'https://api.truckwash.io/release/gate/test-runs' }}
|
|
||||||
RELEASE_MANAGER_GATE_TOKEN: ${{ secrets.RELEASE_MANAGER_GATE_TOKEN }}
|
|
||||||
RELEASE_REPOSITORY: ${{ github.repository }}
|
|
||||||
RELEASE_BRANCH: ${{ github.ref_name }}
|
|
||||||
RELEASE_EXPECTED_COMMIT: ${{ github.sha }}
|
|
||||||
RELEASE_WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
|
|||||||
@@ -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
@@ -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"]
|
||||||
@@ -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"]
|
|
||||||
@@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
Backend API for Copenhagen Truck Wash services.
|
Backend API for Copenhagen Truck Wash services.
|
||||||
|
|
||||||
Changes are published from a scoped feature branch through a pull request to
|
|
||||||
`master`; direct default-branch pushes are not part of the release workflow.
|
|
||||||
See [default branch protection](.github/BRANCH_PROTECTION.md) for the CI gate
|
|
||||||
and emergency procedure.
|
|
||||||
|
|
||||||
## Architecture & Stack
|
## Architecture & Stack
|
||||||
- **Edge Proxy:** [Traefik 2.11](https://doc.traefik.io/traefik/) (Handles TLS termination, routing, and rate limiting).
|
- **Edge Proxy:** [Traefik 2.11](https://doc.traefik.io/traefik/) (Handles TLS termination, routing, and rate limiting).
|
||||||
- **Web Server:** [Caddy 2.7](https://caddyserver.com/) (Serves the PHP application via FastCGI).
|
- **Web Server:** [Caddy 2.7](https://caddyserver.com/) (Serves the PHP application via FastCGI).
|
||||||
@@ -75,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`.
|
||||||
@@ -101,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:
|
||||||
@@ -112,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.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
+1
-1
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
|
||||||
@@ -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 Let’s 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 Let’s 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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+113
-5634
File diff suppressed because it is too large
Load Diff
-132545
File diff suppressed because one or more lines are too long
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
@@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
});
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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({});
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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"]}}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
mkdir -p /run/nginx
|
|
||||||
php-fpm -D
|
|
||||||
exec nginx -g "daemon off;"
|
|
||||||
@@ -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/ ./
|
||||||
|
|
||||||
|
|||||||
Vendored
+36
-382
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,21 +37,8 @@ class attachment_store implements minio_uploads_i
|
|||||||
*/
|
*/
|
||||||
public function isValidFilePath(string $filePath): bool
|
public function isValidFilePath(string $filePath): bool
|
||||||
{
|
{
|
||||||
if (
|
// Check if the file path is valid
|
||||||
$filePath === ''
|
return preg_match('/^[a-zA-Z0-9_\-\/.]+$/', $filePath) === 1;
|
||||||
|| str_starts_with($filePath, '/')
|
|
||||||
|| preg_match('/^[a-zA-Z0-9_\-\/.]+$/', $filePath) !== 1
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (explode('/', $filePath) as $segment) {
|
|
||||||
if ($segment === '' || $segment === '.' || $segment === '..') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,10 +82,7 @@ class attachment_store implements minio_uploads_i
|
|||||||
{
|
{
|
||||||
$host = 'https://api.truckwash.io';
|
$host = 'https://api.truckwash.io';
|
||||||
|
|
||||||
$this->requireValidFilePath($fileName);
|
|
||||||
$encodedPath = implode('/', array_map('rawurlencode', explode('/', $fileName)));
|
|
||||||
|
|
||||||
// Generate a direct download URL for the given file name
|
// Generate a direct download URL for the given file name
|
||||||
return $host . '/files/' . $encodedPath;
|
return $host . '/files/' . $fileName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
class backup_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 backup_records (
|
|
||||||
backup_uuid VARCHAR(64) NOT NULL PRIMARY KEY,
|
|
||||||
name VARCHAR(191) NOT NULL,
|
|
||||||
description TEXT NULL,
|
|
||||||
source VARCHAR(32) NOT NULL DEFAULT 'manual',
|
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'queued',
|
|
||||||
schema_version INT UNSIGNED NOT NULL DEFAULT 2,
|
|
||||||
storage_bucket VARCHAR(191) NOT NULL DEFAULT 'backups',
|
|
||||||
storage_prefix VARCHAR(255) NOT NULL,
|
|
||||||
manifest_key VARCHAR(255) NULL,
|
|
||||||
manifest_sha256 CHAR(64) NULL,
|
|
||||||
encryption_key_id VARCHAR(191) NULL,
|
|
||||||
component_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
object_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
total_bytes BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
requested_by_user_id INT NULL,
|
|
||||||
started_at DATETIME NULL,
|
|
||||||
completed_at DATETIME NULL,
|
|
||||||
verified_at DATETIME NULL,
|
|
||||||
expires_at DATETIME NULL,
|
|
||||||
last_error TEXT NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_backup_records_status_created (status, created_at),
|
|
||||||
KEY idx_backup_records_verified (verified_at),
|
|
||||||
KEY idx_backup_records_expires (expires_at)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
$db->query(
|
|
||||||
"CREATE TABLE IF NOT EXISTS backup_components (
|
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
backup_uuid VARCHAR(64) NOT NULL,
|
|
||||||
component_type VARCHAR(32) NOT NULL,
|
|
||||||
logical_name VARCHAR(191) NOT NULL,
|
|
||||||
source_bucket VARCHAR(191) NULL,
|
|
||||||
source_prefix VARCHAR(255) NULL,
|
|
||||||
storage_key VARCHAR(255) NULL,
|
|
||||||
manifest_key VARCHAR(255) NULL,
|
|
||||||
object_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
byte_size BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
content_sha256 CHAR(64) NULL,
|
|
||||||
encrypted_sha256 CHAR(64) NULL,
|
|
||||||
encryption_key_id VARCHAR(191) NULL,
|
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
|
||||||
error_message TEXT NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_backup_components_backup (backup_uuid),
|
|
||||||
KEY idx_backup_components_status (status),
|
|
||||||
KEY idx_backup_components_type_name (component_type, logical_name)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
$db->query(
|
|
||||||
"CREATE TABLE IF NOT EXISTS backup_jobs (
|
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
job_type VARCHAR(32) NOT NULL,
|
|
||||||
backup_uuid VARCHAR(64) NULL,
|
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'queued',
|
|
||||||
progress_percent TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
progress_message VARCHAR(255) NULL,
|
|
||||||
payload_json LONGTEXT NULL,
|
|
||||||
result_json LONGTEXT NULL,
|
|
||||||
actor_user_id INT NULL,
|
|
||||||
locked_at DATETIME NULL,
|
|
||||||
lock_owner VARCHAR(191) NULL,
|
|
||||||
started_at DATETIME NULL,
|
|
||||||
completed_at DATETIME NULL,
|
|
||||||
error_message TEXT NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_backup_jobs_status_created (status, created_at),
|
|
||||||
KEY idx_backup_jobs_backup (backup_uuid),
|
|
||||||
KEY idx_backup_jobs_type_status (job_type, status)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
$db->query(
|
|
||||||
"CREATE TABLE IF NOT EXISTS backup_restore_audit (
|
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
restore_job_id BIGINT UNSIGNED NULL,
|
|
||||||
preview_job_id BIGINT UNSIGNED NULL,
|
|
||||||
backup_uuid VARCHAR(64) NOT NULL,
|
|
||||||
actor_user_id INT NULL,
|
|
||||||
target_environment VARCHAR(64) NOT NULL DEFAULT 'production',
|
|
||||||
confirmation_fingerprint CHAR(64) NULL,
|
|
||||||
reason TEXT NULL,
|
|
||||||
ip_address VARCHAR(64) NULL,
|
|
||||||
user_agent VARCHAR(255) NULL,
|
|
||||||
pre_restore_backup_uuid VARCHAR(64) NULL,
|
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'queued',
|
|
||||||
started_at DATETIME NULL,
|
|
||||||
completed_at DATETIME NULL,
|
|
||||||
error_message TEXT NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_backup_restore_audit_backup (backup_uuid),
|
|
||||||
KEY idx_backup_restore_audit_job (restore_job_id),
|
|
||||||
KEY idx_backup_restore_audit_created (created_at)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
self::$initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,294 +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 listApplications(): array
|
|
||||||
{
|
|
||||||
return $this->request('GET', '/applications');
|
|
||||||
}
|
|
||||||
|
|
||||||
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 stopService(string $uuid): array
|
|
||||||
{
|
|
||||||
return $this->request('GET', '/services/' . rawurlencode($uuid) . '/stop');
|
|
||||||
}
|
|
||||||
|
|
||||||
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 deleteApplication(string $uuid): array
|
|
||||||
{
|
|
||||||
return $this->request('DELETE', '/applications/' . 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 . ")"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
class cron_schedule
|
|
||||||
{
|
|
||||||
public static function normalize(array $schedule): array
|
|
||||||
{
|
|
||||||
$type = strtolower(trim((string)($schedule['type'] ?? 'interval')));
|
|
||||||
if ($type !== 'interval') {
|
|
||||||
throw new InvalidArgumentException('Unsupported cron schedule type: ' . $type);
|
|
||||||
}
|
|
||||||
|
|
||||||
$seconds = (int)($schedule['seconds'] ?? $schedule['interval'] ?? 0);
|
|
||||||
if ($seconds < 30 || $seconds > 2678400) {
|
|
||||||
throw new InvalidArgumentException('Cron interval must be between 30 seconds and 31 days.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'type' => 'interval',
|
|
||||||
'seconds' => $seconds,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function nextRunAt(array $schedule, ?string $anchorDateTime, int $now): string
|
|
||||||
{
|
|
||||||
$normalized = self::normalize($schedule);
|
|
||||||
$anchor = $anchorDateTime !== null && trim($anchorDateTime) !== ''
|
|
||||||
? strtotime($anchorDateTime)
|
|
||||||
: false;
|
|
||||||
$base = $anchor !== false ? (int)$anchor : $now;
|
|
||||||
$next = $base + (int)$normalized['seconds'];
|
|
||||||
|
|
||||||
if ($next <= $now) {
|
|
||||||
$missed = (int)floor(($now - $next) / (int)$normalized['seconds']) + 1;
|
|
||||||
$next += $missed * (int)$normalized['seconds'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return date('Y-m-d H:i:s', $next);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function dueAt(array $schedule, ?string $lastRunAt, int $now, ?int $legacyLastRun = null): string
|
|
||||||
{
|
|
||||||
$normalized = self::normalize($schedule);
|
|
||||||
|
|
||||||
if ($lastRunAt !== null && trim($lastRunAt) !== '') {
|
|
||||||
return self::nextRunAt($normalized, $lastRunAt, $now);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($legacyLastRun !== null && $legacyLastRun > 0) {
|
|
||||||
return date('Y-m-d H:i:s', $legacyLastRun + (int)$normalized['seconds']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return date('Y-m-d H:i:s', $now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,681 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use RuntimeException;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class cron_scheduler
|
|
||||||
{
|
|
||||||
private cron_task_registry $registry;
|
|
||||||
private string $lock_owner;
|
|
||||||
|
|
||||||
public function __construct(?cron_task_registry $registry = null)
|
|
||||||
{
|
|
||||||
$this->registry = $registry ?? new cron_task_registry();
|
|
||||||
$this->lock_owner = gethostname() . ':' . getmypid() . ':' . bin2hex(random_bytes(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listTasks(): array
|
|
||||||
{
|
|
||||||
$this->ensureReady();
|
|
||||||
$this->syncDefinitions();
|
|
||||||
|
|
||||||
$states = $this->stateRows();
|
|
||||||
$estimates = $this->durationEstimates();
|
|
||||||
$tasks = [];
|
|
||||||
$now = time();
|
|
||||||
|
|
||||||
foreach ($this->registry->definitions() as $definition) {
|
|
||||||
$state = $states[$definition->id] ?? [];
|
|
||||||
$schedule = is_array($state['schedule'] ?? null) && $state['schedule'] !== []
|
|
||||||
? $state['schedule']
|
|
||||||
: $definition->schedule;
|
|
||||||
$nextRunAt = $state['next_run_at'] ?? null;
|
|
||||||
if ($nextRunAt === null || trim((string)$nextRunAt) === '') {
|
|
||||||
$nextRunAt = cron_schedule::dueAt($schedule, $state['last_run_at'] ?? null, $now);
|
|
||||||
}
|
|
||||||
|
|
||||||
$task = $definition->asArray($state + ['next_run_at' => $nextRunAt], $estimates[$definition->id] ?? null);
|
|
||||||
$task['due'] = strtotime($nextRunAt) !== false && strtotime($nextRunAt) <= $now;
|
|
||||||
$task['seconds_until_due'] = max(0, (int)strtotime($nextRunAt) - $now);
|
|
||||||
$tasks[] = $task;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'tasks' => $tasks,
|
|
||||||
'summary' => [
|
|
||||||
'total' => count($tasks),
|
|
||||||
'enabled' => count(array_filter($tasks, static fn(array $task): bool => (bool)$task['enabled'])),
|
|
||||||
'due' => count(array_filter($tasks, static fn(array $task): bool => (bool)$task['due'] && (bool)$task['enabled'])),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listRuns(?string $task_id = null, int $limit = 50): array
|
|
||||||
{
|
|
||||||
$this->ensureReady();
|
|
||||||
$limit = max(1, min(200, $limit));
|
|
||||||
|
|
||||||
$where = '';
|
|
||||||
if ($task_id !== null && trim($task_id) !== '') {
|
|
||||||
$where = "WHERE task_id = " . $this->sql($task_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->fetchAll(
|
|
||||||
"SELECT * FROM cron_task_runs $where ORDER BY id DESC LIMIT $limit"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function queueTaskRun(string $task_id_or_legacy_name, ?int $actor_user_id = null, bool $force = false): array
|
|
||||||
{
|
|
||||||
$this->ensureReady();
|
|
||||||
$this->syncDefinitions();
|
|
||||||
|
|
||||||
$definition = $this->registry->get($task_id_or_legacy_name);
|
|
||||||
if ($definition === null) {
|
|
||||||
throw new RuntimeException('Cron task not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$state = $this->stateRows()[$definition->id] ?? [];
|
|
||||||
$enabled = (bool)($state['enabled'] ?? $definition->enabled);
|
|
||||||
if (!$enabled && !$force) {
|
|
||||||
throw new RuntimeException('Cron task is disabled.');
|
|
||||||
}
|
|
||||||
if ($this->taskIsLocked($state)) {
|
|
||||||
throw new RuntimeException('Cron task is already running.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$existing = $this->fetchOne(
|
|
||||||
"SELECT * FROM cron_task_runs
|
|
||||||
WHERE task_id = " . $this->sql($definition->id) . " AND status = 'queued'
|
|
||||||
ORDER BY id DESC LIMIT 1"
|
|
||||||
);
|
|
||||||
if ($existing !== null) {
|
|
||||||
$this->markTaskQueued($definition);
|
|
||||||
return $this->publicRun($existing);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scheduled_for = date('Y-m-d H:i:s');
|
|
||||||
$this->query(
|
|
||||||
"INSERT INTO cron_task_runs
|
|
||||||
(task_id, module, source, status, actor_user_id, scheduled_for, force_run)
|
|
||||||
VALUES ("
|
|
||||||
. $this->sql($definition->id) . ', '
|
|
||||||
. $this->sql($definition->module) . ", 'manual', 'queued', "
|
|
||||||
. ($actor_user_id === null ? 'NULL' : (string)(int)$actor_user_id) . ', '
|
|
||||||
. $this->sql($scheduled_for) . ', '
|
|
||||||
. ($force ? '1' : '0')
|
|
||||||
. ")"
|
|
||||||
);
|
|
||||||
|
|
||||||
$run_id = (int)$this->insertId();
|
|
||||||
$this->markTaskQueued($definition);
|
|
||||||
|
|
||||||
return $this->publicRun($this->fetchOne("SELECT * FROM cron_task_runs WHERE id = $run_id") ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runDue(string $source = 'automatic'): array
|
|
||||||
{
|
|
||||||
$this->ensureReady();
|
|
||||||
$this->syncDefinitions();
|
|
||||||
|
|
||||||
$ran = [];
|
|
||||||
foreach ($this->queuedRuns() as $queuedRun) {
|
|
||||||
try {
|
|
||||||
$run = $this->runQueuedRun($queuedRun);
|
|
||||||
if ($run !== null) {
|
|
||||||
$ran[] = $run;
|
|
||||||
}
|
|
||||||
} catch (Throwable $throwable) {
|
|
||||||
$ran[] = [
|
|
||||||
'task_id' => (string)($queuedRun['task_id'] ?? ''),
|
|
||||||
'module' => (string)($queuedRun['module'] ?? ''),
|
|
||||||
'source' => (string)($queuedRun['source'] ?? 'manual'),
|
|
||||||
'status' => 'skipped',
|
|
||||||
'error_message' => $throwable->getMessage(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$now = time();
|
|
||||||
$states = $this->stateRows();
|
|
||||||
foreach ($this->registry->definitions() as $definition) {
|
|
||||||
$state = $states[$definition->id] ?? [];
|
|
||||||
if (!(bool)($state['enabled'] ?? $definition->enabled)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nextRunAt = (string)($state['next_run_at'] ?? '');
|
|
||||||
if ($nextRunAt === '' || strtotime($nextRunAt) === false || strtotime($nextRunAt) > $now) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$ran[] = $this->runTask($definition->id, $source, null, false, $nextRunAt);
|
|
||||||
} catch (Throwable $throwable) {
|
|
||||||
$ran[] = [
|
|
||||||
'task_id' => $definition->id,
|
|
||||||
'module' => $definition->module,
|
|
||||||
'source' => $source,
|
|
||||||
'status' => 'skipped',
|
|
||||||
'error_message' => $throwable->getMessage(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'ran' => $ran,
|
|
||||||
'count' => count($ran),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function markExpiredRunningRuns(): int
|
|
||||||
{
|
|
||||||
$this->ensureReady();
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
$message = 'Task lock expired before completion.';
|
|
||||||
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_runs r
|
|
||||||
INNER JOIN cron_task_state s ON s.task_id = r.task_id AND s.current_run_id = r.id
|
|
||||||
SET r.status = 'timed_out',
|
|
||||||
r.completed_at = COALESCE(s.locked_until, " . $this->sql($now) . "),
|
|
||||||
r.error_message = COALESCE(r.error_message, " . $this->sql($message) . "),
|
|
||||||
s.current_run_id = NULL,
|
|
||||||
s.locked_until = NULL,
|
|
||||||
s.lock_owner = NULL,
|
|
||||||
s.last_status = 'timed_out',
|
|
||||||
s.last_error = " . $this->sql($message) . "
|
|
||||||
WHERE r.status = 'running'
|
|
||||||
AND s.locked_until IS NOT NULL
|
|
||||||
AND s.locked_until < " . $this->sql($now)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->affectedRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runTask(
|
|
||||||
string $task_id_or_legacy_name,
|
|
||||||
string $source = 'manual',
|
|
||||||
?int $actor_user_id = null,
|
|
||||||
bool $force = false,
|
|
||||||
?string $scheduled_for = null
|
|
||||||
): array {
|
|
||||||
$this->ensureReady();
|
|
||||||
$this->syncDefinitions();
|
|
||||||
|
|
||||||
$definition = $this->registry->get($task_id_or_legacy_name);
|
|
||||||
if ($definition === null) {
|
|
||||||
throw new RuntimeException('Cron task not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$state = $this->stateRows()[$definition->id] ?? [];
|
|
||||||
$enabled = (bool)($state['enabled'] ?? $definition->enabled);
|
|
||||||
if (!$enabled && !$force) {
|
|
||||||
throw new RuntimeException('Cron task is disabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->claimLock($definition)) {
|
|
||||||
throw new RuntimeException('Cron task is already running.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$started = microtime(true);
|
|
||||||
$started_at = date('Y-m-d H:i:s', (int)$started);
|
|
||||||
$run_id = $this->createRun($definition, $source, $actor_user_id, $scheduled_for, $started_at, $force);
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state SET current_run_id = $run_id WHERE task_id = " . $this->sql($definition->id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->executeClaimedRun($definition, $run_id, $started);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function executeClaimedRun(cron_task_definition $definition, int $run_id, float $started): array
|
|
||||||
{
|
|
||||||
$status = 'succeeded';
|
|
||||||
$summary = [];
|
|
||||||
$error_message = null;
|
|
||||||
$output = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (function_exists('set_time_limit')) {
|
|
||||||
@set_time_limit($definition->timeout_seconds + 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->ensureLegacyFunctionsLoaded($definition);
|
|
||||||
if (!is_callable($definition->handler)) {
|
|
||||||
throw new RuntimeException('Cron task handler is not callable: ' . $definition->handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$result = call_user_func($definition->handler);
|
|
||||||
$output = (string)ob_get_clean();
|
|
||||||
$summary = is_array($result) ? $result : [];
|
|
||||||
} catch (Throwable $throwable) {
|
|
||||||
if (ob_get_level() > 0) {
|
|
||||||
$output .= (string)ob_get_clean();
|
|
||||||
}
|
|
||||||
$status = 'failed';
|
|
||||||
$error_message = $throwable->getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
$completed = microtime(true);
|
|
||||||
$duration_ms = (int)round(($completed - $started) * 1000);
|
|
||||||
if ($duration_ms > ($definition->timeout_seconds * 1000) && $status === 'succeeded') {
|
|
||||||
$status = 'timed_out';
|
|
||||||
$error_message = 'Task exceeded its configured timeout window.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($output !== '') {
|
|
||||||
$summary['output'] = substr($output, 0, 8000);
|
|
||||||
}
|
|
||||||
|
|
||||||
$completed_at = date('Y-m-d H:i:s', (int)$completed);
|
|
||||||
$this->completeRun($run_id, $status, $completed_at, $duration_ms, $summary, $error_message);
|
|
||||||
$this->releaseLock($definition, $status, $error_message, $completed_at);
|
|
||||||
|
|
||||||
return $this->publicRun($this->fetchOne("SELECT * FROM cron_task_runs WHERE id = $run_id") ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateTaskConfig(string $task_id, array $config): array
|
|
||||||
{
|
|
||||||
$this->ensureReady();
|
|
||||||
$this->syncDefinitions();
|
|
||||||
|
|
||||||
$definition = $this->registry->get($task_id);
|
|
||||||
if ($definition === null) {
|
|
||||||
throw new RuntimeException('Cron task not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$updates = [];
|
|
||||||
if (array_key_exists('enabled', $config)) {
|
|
||||||
$updates[] = 'enabled = ' . ((bool)$config['enabled'] ? '1' : '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('schedule', $config)) {
|
|
||||||
$schedule = $config['schedule'] === null ? null : cron_schedule::normalize((array)$config['schedule']);
|
|
||||||
$updates[] = 'schedule_json = ' . ($schedule === null ? 'NULL' : $this->sql(json_encode($schedule)));
|
|
||||||
$anchor = (string)($this->fetchOne("SELECT last_run_at FROM cron_task_state WHERE task_id = " . $this->sql($definition->id))['last_run_at'] ?? '');
|
|
||||||
$updates[] = 'next_run_at = ' . $this->sql(cron_schedule::dueAt($schedule ?? $definition->schedule, $anchor !== '' ? $anchor : null, time()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($updates !== []) {
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state SET " . implode(', ', $updates) . " WHERE task_id = " . $this->sql($definition->id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->listTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function ensureReady(): void
|
|
||||||
{
|
|
||||||
cron_schema_bootstrap::ensureTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function syncDefinitions(): void
|
|
||||||
{
|
|
||||||
$now = time();
|
|
||||||
foreach ($this->registry->definitions() as $definition) {
|
|
||||||
$row = $this->fetchOne(
|
|
||||||
"SELECT * FROM cron_task_state WHERE task_id = " . $this->sql($definition->id)
|
|
||||||
);
|
|
||||||
if ($row !== null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$legacyLastRun = $this->legacyLastRun($definition);
|
|
||||||
$nextRunAt = cron_schedule::dueAt($definition->schedule, null, $now, $legacyLastRun);
|
|
||||||
$this->query(
|
|
||||||
"INSERT INTO cron_task_state (task_id, module, enabled, schedule_json, next_run_at)
|
|
||||||
VALUES ("
|
|
||||||
. $this->sql($definition->id) . ', '
|
|
||||||
. $this->sql($definition->module) . ', '
|
|
||||||
. ($definition->enabled ? '1' : '0') . ', NULL, '
|
|
||||||
. $this->sql($nextRunAt)
|
|
||||||
. ")"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function legacyLastRun(cron_task_definition $definition): ?int
|
|
||||||
{
|
|
||||||
if ($definition->legacy_name === null || !defined('redis')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$last_run = redis->get_last_crond_run($definition->legacy_name);
|
|
||||||
return $last_run !== null ? (int)$last_run : null;
|
|
||||||
} catch (Throwable) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function claimLock(cron_task_definition $definition): bool
|
|
||||||
{
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
$locked_until = date('Y-m-d H:i:s', time() + $definition->timeout_seconds + 60);
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state
|
|
||||||
SET locked_until = " . $this->sql($locked_until) . ",
|
|
||||||
lock_owner = " . $this->sql($this->lock_owner) . "
|
|
||||||
WHERE task_id = " . $this->sql($definition->id) . "
|
|
||||||
AND (locked_until IS NULL OR locked_until < " . $this->sql($now) . ")"
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->affectedRows() === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function taskIsLocked(array $state): bool
|
|
||||||
{
|
|
||||||
$lockedUntil = (string)($state['locked_until'] ?? '');
|
|
||||||
return $lockedUntil !== ''
|
|
||||||
&& strtotime($lockedUntil) !== false
|
|
||||||
&& strtotime($lockedUntil) >= time();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function markTaskQueued(cron_task_definition $definition): void
|
|
||||||
{
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state
|
|
||||||
SET last_status = 'queued',
|
|
||||||
last_error = NULL,
|
|
||||||
next_run_at = CASE
|
|
||||||
WHEN next_run_at IS NULL OR next_run_at > " . $this->sql($now) . " THEN " . $this->sql($now) . "
|
|
||||||
ELSE next_run_at
|
|
||||||
END
|
|
||||||
WHERE task_id = " . $this->sql($definition->id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, array<string, mixed>>
|
|
||||||
*/
|
|
||||||
private function queuedRuns(): array
|
|
||||||
{
|
|
||||||
return $this->fetchAll("SELECT * FROM cron_task_runs WHERE status = 'queued' ORDER BY id ASC LIMIT 50");
|
|
||||||
}
|
|
||||||
|
|
||||||
private function runQueuedRun(array $queuedRun): ?array
|
|
||||||
{
|
|
||||||
$run_id = (int)($queuedRun['id'] ?? 0);
|
|
||||||
$definition = $this->registry->get((string)($queuedRun['task_id'] ?? ''));
|
|
||||||
if ($run_id < 1 || $definition === null) {
|
|
||||||
if ($run_id > 0) {
|
|
||||||
$this->skipQueuedRun($run_id, 'Cron task not found.');
|
|
||||||
return $this->publicRun($this->fetchOne("SELECT * FROM cron_task_runs WHERE id = $run_id") ?? []);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$force = (bool)($queuedRun['force_run'] ?? false);
|
|
||||||
$state = $this->stateRows()[$definition->id] ?? [];
|
|
||||||
$enabled = (bool)($state['enabled'] ?? $definition->enabled);
|
|
||||||
if (!$enabled && !$force) {
|
|
||||||
$this->skipQueuedRun($run_id, 'Cron task is disabled.', $definition);
|
|
||||||
return $this->publicRun($this->fetchOne("SELECT * FROM cron_task_runs WHERE id = $run_id") ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->claimLock($definition)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$started = microtime(true);
|
|
||||||
$started_at = date('Y-m-d H:i:s', (int)$started);
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_runs
|
|
||||||
SET status = 'running',
|
|
||||||
started_at = " . $this->sql($started_at) . ",
|
|
||||||
lock_owner = " . $this->sql($this->lock_owner) . "
|
|
||||||
WHERE id = $run_id AND status = 'queued'"
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->affectedRows() !== 1) {
|
|
||||||
$this->clearClaimedLock($definition);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state SET current_run_id = $run_id WHERE task_id = " . $this->sql($definition->id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->executeClaimedRun($definition, $run_id, $started);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function skipQueuedRun(int $run_id, string $message, ?cron_task_definition $definition = null): void
|
|
||||||
{
|
|
||||||
$completed_at = date('Y-m-d H:i:s');
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_runs
|
|
||||||
SET status = 'skipped',
|
|
||||||
completed_at = " . $this->sql($completed_at) . ",
|
|
||||||
duration_ms = 0,
|
|
||||||
error_message = " . $this->sql($message) . "
|
|
||||||
WHERE id = $run_id AND status = 'queued'"
|
|
||||||
);
|
|
||||||
$updated = $this->affectedRows() === 1;
|
|
||||||
if (!$updated || $definition === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state
|
|
||||||
SET last_status = 'skipped',
|
|
||||||
last_error = " . $this->sql($message) . "
|
|
||||||
WHERE task_id = " . $this->sql($definition->id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function clearClaimedLock(cron_task_definition $definition): void
|
|
||||||
{
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state
|
|
||||||
SET locked_until = NULL,
|
|
||||||
lock_owner = NULL
|
|
||||||
WHERE task_id = " . $this->sql($definition->id) . "
|
|
||||||
AND lock_owner = " . $this->sql($this->lock_owner)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function releaseLock(cron_task_definition $definition, string $status, ?string $error_message, string $completed_at): void
|
|
||||||
{
|
|
||||||
$state = $this->fetchOne("SELECT schedule_json FROM cron_task_state WHERE task_id = " . $this->sql($definition->id));
|
|
||||||
$schedule = $this->decodeJson($state['schedule_json'] ?? null);
|
|
||||||
if ($schedule === []) {
|
|
||||||
$schedule = $definition->schedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nextRunAt = cron_schedule::nextRunAt($schedule, $completed_at, time());
|
|
||||||
if ($status !== 'succeeded') {
|
|
||||||
$retrySeconds = min(300, max(60, (int)$schedule['seconds']));
|
|
||||||
$nextRunAt = date('Y-m-d H:i:s', time() + $retrySeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_state
|
|
||||||
SET last_run_at = " . $this->sql($completed_at) . ",
|
|
||||||
next_run_at = " . $this->sql($nextRunAt) . ",
|
|
||||||
locked_until = NULL,
|
|
||||||
lock_owner = NULL,
|
|
||||||
current_run_id = NULL,
|
|
||||||
last_status = " . $this->sql($status) . ",
|
|
||||||
last_error = " . $this->nullableSql($error_message) . "
|
|
||||||
WHERE task_id = " . $this->sql($definition->id) . "
|
|
||||||
AND lock_owner = " . $this->sql($this->lock_owner)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($definition->legacy_name !== null && defined('redis')) {
|
|
||||||
try {
|
|
||||||
redis->set_last_crond_run($definition->legacy_name, time());
|
|
||||||
} catch (Throwable) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createRun(
|
|
||||||
cron_task_definition $definition,
|
|
||||||
string $source,
|
|
||||||
?int $actor_user_id,
|
|
||||||
?string $scheduled_for,
|
|
||||||
string $started_at,
|
|
||||||
bool $force = false
|
|
||||||
): int {
|
|
||||||
$this->query(
|
|
||||||
"INSERT INTO cron_task_runs
|
|
||||||
(task_id, module, source, status, actor_user_id, scheduled_for, started_at, lock_owner, force_run)
|
|
||||||
VALUES ("
|
|
||||||
. $this->sql($definition->id) . ', '
|
|
||||||
. $this->sql($definition->module) . ', '
|
|
||||||
. $this->sql($source) . ", 'running', "
|
|
||||||
. ($actor_user_id === null ? 'NULL' : (string)(int)$actor_user_id) . ', '
|
|
||||||
. $this->nullableSql($scheduled_for) . ', '
|
|
||||||
. $this->sql($started_at) . ', '
|
|
||||||
. $this->sql($this->lock_owner) . ', '
|
|
||||||
. ($force ? '1' : '0')
|
|
||||||
. ")"
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->insertId();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function completeRun(
|
|
||||||
int $run_id,
|
|
||||||
string $status,
|
|
||||||
string $completed_at,
|
|
||||||
int $duration_ms,
|
|
||||||
array $summary,
|
|
||||||
?string $error_message
|
|
||||||
): void {
|
|
||||||
$this->query(
|
|
||||||
"UPDATE cron_task_runs
|
|
||||||
SET status = " . $this->sql($status) . ",
|
|
||||||
completed_at = " . $this->sql($completed_at) . ",
|
|
||||||
duration_ms = " . (string)$duration_ms . ",
|
|
||||||
summary_json = " . $this->sql(json_encode($summary, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) . ",
|
|
||||||
error_message = " . $this->nullableSql($error_message) . "
|
|
||||||
WHERE id = " . (string)$run_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function ensureLegacyFunctionsLoaded(cron_task_definition $definition): void
|
|
||||||
{
|
|
||||||
if (function_exists($definition->handler)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!defined('WD')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!defined('CRON_LOAD_LEGACY_FUNCTIONS_ONLY')) {
|
|
||||||
define('CRON_LOAD_LEGACY_FUNCTIONS_ONLY', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once WD . '/cron/Cron.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, array<string, mixed>>
|
|
||||||
*/
|
|
||||||
private function stateRows(): array
|
|
||||||
{
|
|
||||||
$rows = $this->fetchAll("SELECT * FROM cron_task_state");
|
|
||||||
$states = [];
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$row['enabled'] = (bool)$row['enabled'];
|
|
||||||
$row['schedule'] = $this->decodeJson($row['schedule_json'] ?? null);
|
|
||||||
$states[(string)$row['task_id']] = $row;
|
|
||||||
}
|
|
||||||
return $states;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, int>
|
|
||||||
*/
|
|
||||||
private function durationEstimates(): array
|
|
||||||
{
|
|
||||||
$rows = $this->fetchAll(
|
|
||||||
"SELECT task_id, AVG(duration_ms) AS avg_duration_ms
|
|
||||||
FROM (
|
|
||||||
SELECT task_id, duration_ms
|
|
||||||
FROM cron_task_runs
|
|
||||||
WHERE status = 'succeeded' AND duration_ms IS NOT NULL
|
|
||||||
ORDER BY id DESC
|
|
||||||
LIMIT 500
|
|
||||||
) recent_runs
|
|
||||||
GROUP BY task_id"
|
|
||||||
);
|
|
||||||
|
|
||||||
$estimates = [];
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$estimates[(string)$row['task_id']] = (int)round((float)$row['avg_duration_ms']);
|
|
||||||
}
|
|
||||||
return $estimates;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function decodeJson(mixed $json): array
|
|
||||||
{
|
|
||||||
if (!is_string($json) || trim($json) === '') {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
$decoded = json_decode($json, true);
|
|
||||||
return is_array($decoded) ? $decoded : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function publicRun(array $run): array
|
|
||||||
{
|
|
||||||
if ($run === []) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$run['force_run'] = (bool)($run['force_run'] ?? false);
|
|
||||||
$run['summary'] = $this->decodeJson($run['summary_json'] ?? null);
|
|
||||||
return $run;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function fetchOne(string $sql): ?array
|
|
||||||
{
|
|
||||||
$rows = $this->fetchAll($sql);
|
|
||||||
return $rows[0] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function fetchAll(string $sql): array
|
|
||||||
{
|
|
||||||
$result = $this->query($sql);
|
|
||||||
if ($result === false || $result === true) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return $result->fetch_all(MYSQLI_ASSOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function query(string $sql): \mysqli_result|bool
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return $db->query($sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sql(string $value): string
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return "'" . $db->escape_string($value) . "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
private function nullableSql(?string $value): string
|
|
||||||
{
|
|
||||||
return $value === null ? 'NULL' : $this->sql($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function affectedRows(): int
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return (int)$db->conn()->affected_rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function insertId(): int
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return (int)$db->insert_id();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
class cron_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 cron_task_state (
|
|
||||||
task_id VARCHAR(191) NOT NULL PRIMARY KEY,
|
|
||||||
module VARCHAR(64) NOT NULL,
|
|
||||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
|
||||||
schedule_json LONGTEXT NULL,
|
|
||||||
last_run_at DATETIME NULL,
|
|
||||||
next_run_at DATETIME NULL,
|
|
||||||
locked_until DATETIME NULL,
|
|
||||||
lock_owner VARCHAR(191) NULL,
|
|
||||||
current_run_id BIGINT UNSIGNED NULL,
|
|
||||||
last_status VARCHAR(32) NULL,
|
|
||||||
last_error TEXT NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_cron_task_state_next_run (enabled, next_run_at),
|
|
||||||
KEY idx_cron_task_state_lock (locked_until)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
$db->query(
|
|
||||||
"CREATE TABLE IF NOT EXISTS cron_task_runs (
|
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
task_id VARCHAR(191) NOT NULL,
|
|
||||||
module VARCHAR(64) NOT NULL,
|
|
||||||
source VARCHAR(32) NOT NULL,
|
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'running',
|
|
||||||
actor_user_id INT NULL,
|
|
||||||
scheduled_for DATETIME NULL,
|
|
||||||
started_at DATETIME NULL,
|
|
||||||
completed_at DATETIME NULL,
|
|
||||||
duration_ms INT UNSIGNED NULL,
|
|
||||||
summary_json LONGTEXT NULL,
|
|
||||||
error_message TEXT NULL,
|
|
||||||
lock_owner VARCHAR(191) NULL,
|
|
||||||
force_run TINYINT(1) NOT NULL DEFAULT 0,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_cron_task_runs_task_created (task_id, created_at),
|
|
||||||
KEY idx_cron_task_runs_status_created (status, created_at),
|
|
||||||
KEY idx_cron_task_runs_module_created (module, created_at)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
self::ensureColumn('cron_task_runs', 'force_run', 'TINYINT(1) NOT NULL DEFAULT 0 AFTER lock_owner');
|
|
||||||
|
|
||||||
$db->query(
|
|
||||||
"CREATE TABLE IF NOT EXISTS cron_worker_state (
|
|
||||||
worker_id VARCHAR(191) NOT NULL PRIMARY KEY,
|
|
||||||
name VARCHAR(191) NOT NULL,
|
|
||||||
hostname VARCHAR(191) NULL,
|
|
||||||
pid INT UNSIGNED NULL,
|
|
||||||
source VARCHAR(64) NOT NULL DEFAULT 'coolify_worker',
|
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'starting',
|
|
||||||
release_channel_id BIGINT UNSIGNED NULL,
|
|
||||||
release_target_id BIGINT UNSIGNED NULL,
|
|
||||||
coolify_resource_uuid VARCHAR(128) NULL,
|
|
||||||
coolify_resource_type VARCHAR(32) NULL,
|
|
||||||
commit_sha VARCHAR(64) NULL,
|
|
||||||
poll_seconds INT UNSIGNED NOT NULL DEFAULT 15,
|
|
||||||
last_run_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
last_stale_run_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
||||||
last_error TEXT NULL,
|
|
||||||
started_at DATETIME NULL,
|
|
||||||
last_heartbeat_at DATETIME NULL,
|
|
||||||
last_loop_started_at DATETIME NULL,
|
|
||||||
last_loop_finished_at DATETIME NULL,
|
|
||||||
stopped_at DATETIME NULL,
|
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
KEY idx_cron_worker_state_heartbeat (last_heartbeat_at),
|
|
||||||
KEY idx_cron_worker_state_status (status),
|
|
||||||
KEY idx_cron_worker_state_release_target (release_target_id),
|
|
||||||
KEY idx_cron_worker_state_coolify_resource (coolify_resource_uuid)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
self::$initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 && $result->num_rows > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$db->query("ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
class cron_task_definition
|
|
||||||
{
|
|
||||||
public string $id;
|
|
||||||
public string $name;
|
|
||||||
public string $description;
|
|
||||||
public string $module;
|
|
||||||
public string $handler;
|
|
||||||
public array $schedule;
|
|
||||||
public bool $enabled;
|
|
||||||
public int $timeout_seconds;
|
|
||||||
public int $estimated_duration_ms;
|
|
||||||
public int $priority;
|
|
||||||
public ?string $legacy_name;
|
|
||||||
|
|
||||||
public function __construct(array $definition)
|
|
||||||
{
|
|
||||||
$this->id = self::requiredString($definition, 'id');
|
|
||||||
$this->name = self::requiredString($definition, 'name');
|
|
||||||
$this->description = (string)($definition['description'] ?? '');
|
|
||||||
$this->module = self::requiredString($definition, 'module');
|
|
||||||
$this->handler = self::requiredString($definition, 'handler');
|
|
||||||
$this->schedule = cron_schedule::normalize($definition['schedule'] ?? []);
|
|
||||||
$this->enabled = (bool)($definition['enabled'] ?? true);
|
|
||||||
$this->timeout_seconds = max(30, (int)($definition['timeout_seconds'] ?? 600));
|
|
||||||
$this->estimated_duration_ms = max(0, (int)($definition['estimated_duration_ms'] ?? 0));
|
|
||||||
$this->priority = (int)($definition['priority'] ?? 100);
|
|
||||||
$legacy_name = trim((string)($definition['legacy_name'] ?? ''));
|
|
||||||
$this->legacy_name = $legacy_name !== '' ? $legacy_name : null;
|
|
||||||
|
|
||||||
if (!preg_match('/^[a-z0-9][a-z0-9_.-]{1,190}$/', $this->id)) {
|
|
||||||
throw new InvalidArgumentException('Invalid cron task id: ' . $this->id);
|
|
||||||
}
|
|
||||||
if (!preg_match('/^[a-z0-9][a-z0-9_-]{1,63}$/', $this->module)) {
|
|
||||||
throw new InvalidArgumentException('Invalid cron task module: ' . $this->module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asArray(?array $state = null, ?int $estimatedDurationMs = null): array
|
|
||||||
{
|
|
||||||
$schedule = is_array($state['schedule'] ?? null) && ($state['schedule'] ?? []) !== []
|
|
||||||
? $state['schedule']
|
|
||||||
: $this->schedule;
|
|
||||||
$enabled = array_key_exists('enabled', $state ?? [])
|
|
||||||
? (bool)$state['enabled']
|
|
||||||
: $this->enabled;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $this->id,
|
|
||||||
'name' => $this->name,
|
|
||||||
'description' => $this->description,
|
|
||||||
'module' => $this->module,
|
|
||||||
'handler' => $this->handler,
|
|
||||||
'schedule' => $schedule,
|
|
||||||
'default_schedule' => $this->schedule,
|
|
||||||
'enabled' => $enabled,
|
|
||||||
'default_enabled' => $this->enabled,
|
|
||||||
'timeout_seconds' => $this->timeout_seconds,
|
|
||||||
'estimated_duration_ms' => $estimatedDurationMs ?? $this->estimated_duration_ms,
|
|
||||||
'priority' => $this->priority,
|
|
||||||
'legacy_name' => $this->legacy_name,
|
|
||||||
'last_run_at' => $state['last_run_at'] ?? null,
|
|
||||||
'next_run_at' => $state['next_run_at'] ?? null,
|
|
||||||
'locked_until' => $state['locked_until'] ?? null,
|
|
||||||
'lock_owner' => $state['lock_owner'] ?? null,
|
|
||||||
'current_run_id' => $state['current_run_id'] ?? null,
|
|
||||||
'last_status' => $state['last_status'] ?? null,
|
|
||||||
'last_error' => $state['last_error'] ?? null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function requiredString(array $definition, string $key): string
|
|
||||||
{
|
|
||||||
$value = trim((string)($definition[$key] ?? ''));
|
|
||||||
if ($value === '') {
|
|
||||||
throw new InvalidArgumentException('Missing cron task definition field: ' . $key);
|
|
||||||
}
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
class cron_task_registry
|
|
||||||
{
|
|
||||||
private string $modules_root;
|
|
||||||
|
|
||||||
/** @var array<string, cron_task_definition>|null */
|
|
||||||
private ?array $definitions = null;
|
|
||||||
|
|
||||||
public function __construct(?string $modules_root = null)
|
|
||||||
{
|
|
||||||
$this->modules_root = $modules_root ?? (defined('WD') ? WD . '/modules' : dirname(__DIR__) . '/modules');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, cron_task_definition>
|
|
||||||
*/
|
|
||||||
public function definitions(): array
|
|
||||||
{
|
|
||||||
if ($this->definitions !== null) {
|
|
||||||
return $this->definitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
$definitions = [];
|
|
||||||
foreach ($this->definitionFiles() as $file) {
|
|
||||||
$module_definitions = require $file;
|
|
||||||
if (!is_array($module_definitions)) {
|
|
||||||
throw new InvalidArgumentException('Cron definition file must return an array: ' . $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($module_definitions as $definition) {
|
|
||||||
$task = new cron_task_definition($definition);
|
|
||||||
if (isset($definitions[$task->id])) {
|
|
||||||
throw new InvalidArgumentException('Duplicate cron task id: ' . $task->id);
|
|
||||||
}
|
|
||||||
$definitions[$task->id] = $task;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uasort($definitions, static function (cron_task_definition $left, cron_task_definition $right): int {
|
|
||||||
if ($left->priority !== $right->priority) {
|
|
||||||
return $left->priority <=> $right->priority;
|
|
||||||
}
|
|
||||||
return strcmp($left->id, $right->id);
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->definitions = $definitions;
|
|
||||||
return $definitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(string $id_or_legacy_name): ?cron_task_definition
|
|
||||||
{
|
|
||||||
$normalized = trim($id_or_legacy_name);
|
|
||||||
if ($normalized === '') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$definitions = $this->definitions();
|
|
||||||
if (isset($definitions[$normalized])) {
|
|
||||||
return $definitions[$normalized];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($definitions as $definition) {
|
|
||||||
if ($definition->legacy_name !== null && hash_equals($definition->legacy_name, $normalized)) {
|
|
||||||
return $definition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, string>
|
|
||||||
*/
|
|
||||||
private function definitionFiles(): array
|
|
||||||
{
|
|
||||||
$files = glob($this->modules_root . '/*/cron/tasks.php') ?: [];
|
|
||||||
sort($files, SORT_STRING);
|
|
||||||
return $files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,322 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class cron_worker
|
|
||||||
{
|
|
||||||
private cron_scheduler $scheduler;
|
|
||||||
private string $worker_id;
|
|
||||||
private string $name;
|
|
||||||
private string $source;
|
|
||||||
private int $poll_seconds;
|
|
||||||
private int $heartbeat_seconds;
|
|
||||||
private int $max_runtime_seconds;
|
|
||||||
private bool $should_stop = false;
|
|
||||||
private int $last_heartbeat = 0;
|
|
||||||
|
|
||||||
public function __construct(?cron_scheduler $scheduler = null, array $options = [])
|
|
||||||
{
|
|
||||||
$this->scheduler = $scheduler ?? new cron_scheduler();
|
|
||||||
$this->name = $this->stringOption($options, 'name', 'CRON_WORKER_NAME', 'cron-worker');
|
|
||||||
$this->worker_id = $this->stringOption($options, 'worker_id', 'CRON_WORKER_ID', $this->name);
|
|
||||||
$this->source = $this->stringOption($options, 'source', 'CRON_WORKER_SOURCE', 'coolify_worker');
|
|
||||||
$this->poll_seconds = $this->intOption($options, 'poll_seconds', 'CRON_WORKER_POLL_SECONDS', 15, 1, 300);
|
|
||||||
$this->heartbeat_seconds = $this->intOption($options, 'heartbeat_seconds', 'CRON_WORKER_HEARTBEAT_SECONDS', 30, 5, 300);
|
|
||||||
$this->max_runtime_seconds = $this->intOption($options, 'max_runtime_seconds', 'CRON_WORKER_MAX_RUNTIME_SECONDS', 0, 0, 86400);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run(): int
|
|
||||||
{
|
|
||||||
if (!$this->boolOption('CRON_WORKER_ENABLED', true)) {
|
|
||||||
$this->heartbeat('disabled', 0, 0, null, true);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->registerSignalHandlers();
|
|
||||||
$started = time();
|
|
||||||
$this->heartbeat('starting', 0, 0, null, true);
|
|
||||||
|
|
||||||
while (!$this->should_stop) {
|
|
||||||
$result = $this->tick();
|
|
||||||
$this->writeStatusLine($result);
|
|
||||||
|
|
||||||
if ($this->max_runtime_seconds > 0 && time() - $started >= $this->max_runtime_seconds) {
|
|
||||||
$this->should_stop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->sleepUntilNextPoll();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->heartbeat('stopped', 0, 0, null, true, true);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function tick(): array
|
|
||||||
{
|
|
||||||
$this->heartbeat('running');
|
|
||||||
$loopStartedAt = date('Y-m-d H:i:s');
|
|
||||||
$staleRuns = 0;
|
|
||||||
$ran = ['count' => 0, 'ran' => []];
|
|
||||||
$error = null;
|
|
||||||
$status = 'running';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$staleRuns = $this->scheduler->markExpiredRunningRuns();
|
|
||||||
$ran = $this->scheduler->runDue($this->source);
|
|
||||||
} catch (Throwable $throwable) {
|
|
||||||
$status = 'failed';
|
|
||||||
$error = $throwable->getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->heartbeat($status, (int)($ran['count'] ?? 0), $staleRuns, $error, true, false, $loopStartedAt);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'worker_id' => $this->worker_id,
|
|
||||||
'status' => $status,
|
|
||||||
'ran' => (int)($ran['count'] ?? 0),
|
|
||||||
'stale_runs' => $staleRuns,
|
|
||||||
'error' => $error,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listWorkers(): array
|
|
||||||
{
|
|
||||||
cron_schema_bootstrap::ensureTables();
|
|
||||||
$rows = $this->fetchAll('SELECT * FROM cron_worker_state ORDER BY last_heartbeat_at DESC, worker_id');
|
|
||||||
$workers = [];
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$workers[] = $this->publicWorker($row);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'workers' => $workers,
|
|
||||||
'summary' => [
|
|
||||||
'total' => count($workers),
|
|
||||||
'running' => count(array_filter($workers, static fn(array $worker): bool => ($worker['status'] ?? '') === 'running')),
|
|
||||||
'stale' => count(array_filter($workers, static fn(array $worker): bool => (bool)($worker['stale'] ?? false))),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function registerSignalHandlers(): void
|
|
||||||
{
|
|
||||||
if (!function_exists('pcntl_signal')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function_exists('pcntl_async_signals')) {
|
|
||||||
pcntl_async_signals(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcntl_signal(SIGTERM, function (): void {
|
|
||||||
$this->should_stop = true;
|
|
||||||
});
|
|
||||||
pcntl_signal(SIGINT, function (): void {
|
|
||||||
$this->should_stop = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sleepUntilNextPoll(): void
|
|
||||||
{
|
|
||||||
$remaining = $this->poll_seconds;
|
|
||||||
while ($remaining > 0 && !$this->should_stop) {
|
|
||||||
$sleep = min(1, $remaining);
|
|
||||||
sleep($sleep);
|
|
||||||
$remaining -= $sleep;
|
|
||||||
if (time() - $this->last_heartbeat >= $this->heartbeat_seconds) {
|
|
||||||
$this->heartbeat('running');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function heartbeat(
|
|
||||||
string $status,
|
|
||||||
int $runCount = 0,
|
|
||||||
int $staleRunCount = 0,
|
|
||||||
?string $error = null,
|
|
||||||
bool $force = false,
|
|
||||||
bool $stopped = false,
|
|
||||||
?string $loopStartedAt = null
|
|
||||||
): void {
|
|
||||||
if (!$force && time() - $this->last_heartbeat < $this->heartbeat_seconds) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cron_schema_bootstrap::ensureTables();
|
|
||||||
$this->last_heartbeat = time();
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
$workerId = $this->sql($this->worker_id);
|
|
||||||
$name = $this->sql($this->name);
|
|
||||||
$hostname = $this->nullableSql(gethostname() ?: null);
|
|
||||||
$pid = getmypid() ?: 0;
|
|
||||||
$source = $this->sql($this->source);
|
|
||||||
$statusSql = $this->sql($status);
|
|
||||||
$releaseChannelId = $this->nullableInt($this->env('CRON_WORKER_RELEASE_CHANNEL_ID'));
|
|
||||||
$releaseTargetId = $this->nullableInt($this->env('CRON_WORKER_RELEASE_TARGET_ID'));
|
|
||||||
$resourceUuid = $this->nullableSql($this->env('COOLIFY_RESOURCE_UUID') ?: $this->env('CRON_WORKER_COOLIFY_RESOURCE_UUID'));
|
|
||||||
$resourceType = $this->nullableSql($this->env('COOLIFY_RESOURCE_TYPE') ?: $this->env('CRON_WORKER_COOLIFY_RESOURCE_TYPE') ?: 'application');
|
|
||||||
$commitSha = $this->nullableSql($this->commitSha());
|
|
||||||
$errorSql = $this->nullableSql($error);
|
|
||||||
$loopStarted = $this->nullableSql($loopStartedAt);
|
|
||||||
$stoppedAt = $stopped ? $this->sql($now) : 'NULL';
|
|
||||||
|
|
||||||
$this->query(
|
|
||||||
"INSERT INTO cron_worker_state (
|
|
||||||
worker_id, name, hostname, pid, source, status, release_channel_id, release_target_id,
|
|
||||||
coolify_resource_uuid, coolify_resource_type, commit_sha, poll_seconds, last_run_count,
|
|
||||||
last_stale_run_count, last_error, started_at, last_heartbeat_at, last_loop_started_at,
|
|
||||||
last_loop_finished_at, stopped_at
|
|
||||||
) VALUES (
|
|
||||||
$workerId, $name, $hostname, $pid, $source, $statusSql, $releaseChannelId, $releaseTargetId,
|
|
||||||
$resourceUuid, $resourceType, $commitSha, $this->poll_seconds, $runCount,
|
|
||||||
$staleRunCount, $errorSql, $this->sql($now), $this->sql($now), $loopStarted,
|
|
||||||
$this->sql($now), $stoppedAt
|
|
||||||
)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
name = VALUES(name),
|
|
||||||
hostname = VALUES(hostname),
|
|
||||||
pid = VALUES(pid),
|
|
||||||
source = VALUES(source),
|
|
||||||
status = VALUES(status),
|
|
||||||
release_channel_id = VALUES(release_channel_id),
|
|
||||||
release_target_id = VALUES(release_target_id),
|
|
||||||
coolify_resource_uuid = VALUES(coolify_resource_uuid),
|
|
||||||
coolify_resource_type = VALUES(coolify_resource_type),
|
|
||||||
commit_sha = VALUES(commit_sha),
|
|
||||||
poll_seconds = VALUES(poll_seconds),
|
|
||||||
last_run_count = VALUES(last_run_count),
|
|
||||||
last_stale_run_count = VALUES(last_stale_run_count),
|
|
||||||
last_error = VALUES(last_error),
|
|
||||||
last_heartbeat_at = VALUES(last_heartbeat_at),
|
|
||||||
last_loop_started_at = COALESCE(VALUES(last_loop_started_at), last_loop_started_at),
|
|
||||||
last_loop_finished_at = VALUES(last_loop_finished_at),
|
|
||||||
stopped_at = VALUES(stopped_at)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function publicWorker(array $row): array
|
|
||||||
{
|
|
||||||
$heartbeatAt = (string)($row['last_heartbeat_at'] ?? '');
|
|
||||||
$heartbeatTs = strtotime($heartbeatAt);
|
|
||||||
$threshold = max(60, ((int)($row['poll_seconds'] ?? 15) * 4) + 30);
|
|
||||||
$age = $heartbeatTs !== false ? max(0, time() - $heartbeatTs) : null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'worker_id' => (string)($row['worker_id'] ?? ''),
|
|
||||||
'name' => (string)($row['name'] ?? ''),
|
|
||||||
'hostname' => $row['hostname'] ?? null,
|
|
||||||
'pid' => isset($row['pid']) ? (int)$row['pid'] : null,
|
|
||||||
'source' => (string)($row['source'] ?? ''),
|
|
||||||
'status' => (string)($row['status'] ?? 'unknown'),
|
|
||||||
'release_channel_id' => isset($row['release_channel_id']) ? (int)$row['release_channel_id'] : null,
|
|
||||||
'release_target_id' => isset($row['release_target_id']) ? (int)$row['release_target_id'] : null,
|
|
||||||
'coolify_resource_uuid' => $row['coolify_resource_uuid'] ?? null,
|
|
||||||
'coolify_resource_type' => $row['coolify_resource_type'] ?? null,
|
|
||||||
'commit_sha' => $row['commit_sha'] ?? null,
|
|
||||||
'poll_seconds' => (int)($row['poll_seconds'] ?? 0),
|
|
||||||
'last_run_count' => (int)($row['last_run_count'] ?? 0),
|
|
||||||
'last_stale_run_count' => (int)($row['last_stale_run_count'] ?? 0),
|
|
||||||
'last_error' => $row['last_error'] ?? null,
|
|
||||||
'started_at' => $row['started_at'] ?? null,
|
|
||||||
'last_heartbeat_at' => $heartbeatAt !== '' ? $heartbeatAt : null,
|
|
||||||
'last_heartbeat_age_seconds' => $age,
|
|
||||||
'last_loop_started_at' => $row['last_loop_started_at'] ?? null,
|
|
||||||
'last_loop_finished_at' => $row['last_loop_finished_at'] ?? null,
|
|
||||||
'stopped_at' => $row['stopped_at'] ?? null,
|
|
||||||
'stale' => $age === null || $age > $threshold,
|
|
||||||
'stale_after_seconds' => $threshold,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function writeStatusLine(array $result): void
|
|
||||||
{
|
|
||||||
echo '[' . date('Y-m-d H:i:s') . '][CRON_WORKER] '
|
|
||||||
. json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
|
|
||||||
. PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function stringOption(array $options, string $key, string $env, string $default): string
|
|
||||||
{
|
|
||||||
$value = trim((string)($options[$key] ?? $this->env($env) ?? ''));
|
|
||||||
return $value !== '' ? $value : $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function intOption(array $options, string $key, string $env, int $default, int $min, int $max): int
|
|
||||||
{
|
|
||||||
$value = (int)($options[$key] ?? $this->env($env) ?? $default);
|
|
||||||
return max($min, min($max, $value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function boolOption(string $env, bool $default): bool
|
|
||||||
{
|
|
||||||
$value = $this->env($env);
|
|
||||||
if ($value === null || trim($value) === '') {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return in_array(strtolower(trim($value)), ['1', 'true', 'yes', 'on'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function commitSha(): string
|
|
||||||
{
|
|
||||||
foreach (['CRON_WORKER_COMMIT_SHA', 'API_COMMIT_SHA', 'RELEASE_COMMIT_SHA', 'COMMIT_SHA', 'GITHUB_SHA'] as $key) {
|
|
||||||
$value = trim((string)($this->env($key) ?? ''));
|
|
||||||
if ($value !== '') {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function env(string $key): ?string
|
|
||||||
{
|
|
||||||
$value = getenv($key);
|
|
||||||
if ($value !== false) {
|
|
||||||
return (string)$value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset($_SERVER[$key]) ? (string)$_SERVER[$key] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function nullableInt(?string $value): string
|
|
||||||
{
|
|
||||||
$value = trim((string)$value);
|
|
||||||
if ($value === '' || filter_var($value, FILTER_VALIDATE_INT) === false) {
|
|
||||||
return 'NULL';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string)max(0, (int)$value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function nullableSql(?string $value): string
|
|
||||||
{
|
|
||||||
$value = $value !== null ? trim($value) : '';
|
|
||||||
return $value === '' ? 'NULL' : $this->sql($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function fetchAll(string $sql): array
|
|
||||||
{
|
|
||||||
$result = $this->query($sql);
|
|
||||||
if ($result === false || $result === true) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result->fetch_all(MYSQLI_ASSOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function query(string $sql): \mysqli_result|bool
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return $db->query($sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sql(string $value): string
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return "'" . $db->escape_string($value) . "'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
class customer_order_product_policy
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
$customerNumber = self::loadOrderCustomerNumber($orderId);
|
|
||||||
if ($customerNumber === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$violation = (new customer_rule_product_restriction_service())
|
|
||||||
->violationForCustomerProduct($customerNumber, $productId);
|
|
||||||
return $violation === null ? null : (string)$violation['message'];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function loadOrderCustomerNumber(int $orderId): ?int
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
if ($orderId < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $db->query("SELECT customer_id FROM orders WHERE id = {$orderId} LIMIT 1");
|
|
||||||
if (!$result || $result->num_rows < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$row = $result->fetch_assoc();
|
|
||||||
$customerNumber = (int)($row['customer_id'] ?? 0);
|
|
||||||
return $customerNumber > 0 ? $customerNumber : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use objects\orders_o;
|
|
||||||
|
|
||||||
class customer_product_rule_service
|
|
||||||
{
|
|
||||||
public const BLOCK_MESSAGE = 'This product is not allowed for the selected customer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array{rule:string,rules:list<string>,collections:list<int>,product_id:int,code: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;
|
|
||||||
}
|
|
||||||
$violation = (new customer_rule_product_restriction_service())->violationForCustomerProduct(
|
|
||||||
(int)$order->customer_id->value(),
|
|
||||||
$productId
|
|
||||||
);
|
|
||||||
if ($violation === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep the singular key during the API migration for existing invoice
|
|
||||||
// and logging consumers while also returning every matching rule.
|
|
||||||
return ['rule' => (string)$violation['rules'][0]] + $violation;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use RuntimeException;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Additive schema and the one-time legacy-to-exact-product migration for
|
|
||||||
* customer-rule product restrictions.
|
|
||||||
*/
|
|
||||||
class customer_rule_product_restriction_schema_bootstrap
|
|
||||||
{
|
|
||||||
public const LEGACY_SEED_KEY = 'legacy_exact_product_sets_v1';
|
|
||||||
|
|
||||||
private static bool $initialized = false;
|
|
||||||
|
|
||||||
/** @var array<string, string> */
|
|
||||||
private const RULES = [
|
|
||||||
'restrictAdditionalServices' => 'Additional services',
|
|
||||||
'restrictTankCleaning' => 'Tank cleaning',
|
|
||||||
'restrictSpotFree' => 'SpotFree',
|
|
||||||
'restrictInteriorCleaning' => 'Interior cleaning',
|
|
||||||
'onlyTankCleaning' => 'Non-tank products',
|
|
||||||
];
|
|
||||||
|
|
||||||
public static function ensureSchema(): void
|
|
||||||
{
|
|
||||||
if (self::$initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
global $db;
|
|
||||||
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::createTables($db);
|
|
||||||
self::deduplicateCustomerAttributes($db);
|
|
||||||
self::seedLegacyProductSets($db);
|
|
||||||
self::$initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function createTables(object $db): void
|
|
||||||
{
|
|
||||||
$statements = [
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_rule_product_restrictions (
|
|
||||||
attribute VARCHAR(191) NOT NULL,
|
|
||||||
version INT UNSIGNED NOT NULL DEFAULT 1,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (attribute)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_rule_product_collections (
|
|
||||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
||||||
attribute VARCHAR(191) NOT NULL,
|
|
||||||
name VARCHAR(191) NOT NULL,
|
|
||||||
sort_order INT NOT NULL DEFAULT 0,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uniq_customer_rule_collection_name (attribute, name),
|
|
||||||
KEY idx_customer_rule_collection_attribute_order (attribute, sort_order, id),
|
|
||||||
CONSTRAINT fk_customer_rule_collection_attribute
|
|
||||||
FOREIGN KEY (attribute) REFERENCES customer_rule_product_restrictions(attribute)
|
|
||||||
ON DELETE CASCADE ON UPDATE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_rule_product_collection_products (
|
|
||||||
collection_id INT UNSIGNED NOT NULL,
|
|
||||||
product_id INT UNSIGNED NOT NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (collection_id, product_id),
|
|
||||||
KEY idx_customer_rule_collection_product (product_id, collection_id),
|
|
||||||
CONSTRAINT fk_customer_rule_collection_product_collection
|
|
||||||
FOREIGN KEY (collection_id) REFERENCES customer_rule_product_collections(id)
|
|
||||||
ON DELETE CASCADE ON UPDATE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_rule_product_migrations (
|
|
||||||
migration_key VARCHAR(191) NOT NULL,
|
|
||||||
details_json LONGTEXT NULL,
|
|
||||||
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (migration_key)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_rule_product_audit_logs (
|
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
||||||
actor_user_id INT UNSIGNED NULL,
|
|
||||||
attribute VARCHAR(191) NOT NULL,
|
|
||||||
old_version INT UNSIGNED NOT NULL,
|
|
||||||
new_version INT UNSIGNED NOT NULL,
|
|
||||||
changes_json LONGTEXT NOT NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
KEY idx_customer_rule_product_audit_attribute (attribute, created_at),
|
|
||||||
KEY idx_customer_rule_product_audit_actor (actor_user_id, created_at)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($statements as $statement) {
|
|
||||||
if ($db->query($statement) === false) {
|
|
||||||
throw new RuntimeException('Unable to initialize customer-rule product restriction schema');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function deduplicateCustomerAttributes(object $db): void
|
|
||||||
{
|
|
||||||
if (!self::tableExists($db, 'customer_attributes')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::indexExists($db, 'customer_attributes', 'uniq_customer_attributes_user_attribute')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($db->query(
|
|
||||||
'DELETE duplicate_row FROM customer_attributes duplicate_row
|
|
||||||
INNER JOIN customer_attributes keep_row
|
|
||||||
ON keep_row.user_id = duplicate_row.user_id
|
|
||||||
AND keep_row.attribute = duplicate_row.attribute
|
|
||||||
AND keep_row.id < duplicate_row.id'
|
|
||||||
) === false) {
|
|
||||||
throw new RuntimeException('Unable to deduplicate customer attributes');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($db->query(
|
|
||||||
'ALTER TABLE customer_attributes
|
|
||||||
ADD UNIQUE KEY uniq_customer_attributes_user_attribute (user_id, attribute)'
|
|
||||||
) === false) {
|
|
||||||
throw new RuntimeException('Unable to enforce unique customer attributes');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function seedLegacyProductSets(object $db): void
|
|
||||||
{
|
|
||||||
if (!self::tableExists($db, 'products') || !self::tableExists($db, 'categories')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$migrationKey = self::escape($db, self::LEGACY_SEED_KEY);
|
|
||||||
$existing = $db->query(
|
|
||||||
"SELECT migration_key FROM customer_rule_product_migrations WHERE migration_key = '{$migrationKey}' LIMIT 1"
|
|
||||||
);
|
|
||||||
if ($existing && (int)$existing->num_rows > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($db->query('START TRANSACTION') === false) {
|
|
||||||
throw new RuntimeException('Unable to start customer-rule product migration');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if ($db->query(
|
|
||||||
"INSERT IGNORE INTO customer_rule_product_migrations (migration_key, details_json)
|
|
||||||
VALUES ('{$migrationKey}', '{\"status\":\"in_progress\"}')"
|
|
||||||
) === false) {
|
|
||||||
throw new RuntimeException('Unable to claim customer-rule product migration');
|
|
||||||
}
|
|
||||||
if (self::affectedRows($db) === 0) {
|
|
||||||
$db->query('ROLLBACK');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (array_keys(self::RULES) as $attribute) {
|
|
||||||
$safeAttribute = self::escape($db, $attribute);
|
|
||||||
if ($db->query(
|
|
||||||
"INSERT IGNORE INTO customer_rule_product_restrictions (attribute, version)
|
|
||||||
VALUES ('{$safeAttribute}', 1)"
|
|
||||||
) === false) {
|
|
||||||
throw new RuntimeException("Unable to initialize restriction {$attribute}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$counts = [];
|
|
||||||
$seededProductIds = [];
|
|
||||||
foreach (self::RULES as $attribute => $collectionName) {
|
|
||||||
$safeAttribute = self::escape($db, $attribute);
|
|
||||||
$safeName = self::escape($db, 'Legacy migration: ' . $collectionName);
|
|
||||||
if ($db->query(
|
|
||||||
"INSERT INTO customer_rule_product_collections (attribute, name, sort_order)
|
|
||||||
VALUES ('{$safeAttribute}', '{$safeName}', 0)"
|
|
||||||
) === false) {
|
|
||||||
throw new RuntimeException("Unable to create seed collection for {$attribute}");
|
|
||||||
}
|
|
||||||
$collectionId = (int)$db->insert_id();
|
|
||||||
if ($collectionId < 1) {
|
|
||||||
throw new RuntimeException("Unable to create seed collection for {$attribute}");
|
|
||||||
}
|
|
||||||
|
|
||||||
$predicate = self::legacyPredicate($db, $attribute);
|
|
||||||
$activePredicate = self::columnExists($db, 'products', 'deleted_at')
|
|
||||||
? 'p.deleted_at IS NULL'
|
|
||||||
: '1 = 1';
|
|
||||||
$insert = $db->query(
|
|
||||||
"INSERT IGNORE INTO customer_rule_product_collection_products (collection_id, product_id)
|
|
||||||
SELECT {$collectionId}, p.id
|
|
||||||
FROM products p
|
|
||||||
LEFT JOIN categories c ON c.id = p.category
|
|
||||||
WHERE ({$activePredicate}) AND ({$predicate})"
|
|
||||||
);
|
|
||||||
if ($insert === false) {
|
|
||||||
throw new RuntimeException("Unable to seed products for {$attribute}");
|
|
||||||
}
|
|
||||||
$counts[$attribute] = self::affectedRows($db);
|
|
||||||
$seeded = $db->query(
|
|
||||||
"SELECT product_id FROM customer_rule_product_collection_products
|
|
||||||
WHERE collection_id = {$collectionId} ORDER BY product_id"
|
|
||||||
);
|
|
||||||
$seededProductIds[$attribute] = [];
|
|
||||||
if ($seeded) {
|
|
||||||
while ($row = $seeded->fetch_assoc()) {
|
|
||||||
$seededProductIds[$attribute][] = (int)$row['product_id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = self::escape($db, (string)json_encode([
|
|
||||||
'counts' => $counts,
|
|
||||||
'product_ids' => $seededProductIds,
|
|
||||||
'seeded_at' => gmdate(DATE_ATOM),
|
|
||||||
], JSON_UNESCAPED_SLASHES));
|
|
||||||
if ($db->query(
|
|
||||||
"UPDATE customer_rule_product_migrations
|
|
||||||
SET details_json = '{$details}', applied_at = NOW()
|
|
||||||
WHERE migration_key = '{$migrationKey}'"
|
|
||||||
) === false) {
|
|
||||||
throw new RuntimeException('Unable to record customer-rule product migration');
|
|
||||||
}
|
|
||||||
if ($db->query('COMMIT') === false) {
|
|
||||||
throw new RuntimeException('Unable to commit customer-rule product migration');
|
|
||||||
}
|
|
||||||
} catch (Throwable $throwable) {
|
|
||||||
$db->query('ROLLBACK');
|
|
||||||
throw $throwable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function legacyPredicate(object $db, string $attribute): string
|
|
||||||
{
|
|
||||||
$text = "LOWER(CONCAT(COALESCE(p.name, ''), ' ', COALESCE(c.name, '')))";
|
|
||||||
|
|
||||||
return match ($attribute) {
|
|
||||||
'restrictAdditionalServices' => "p.category = 8 OR LOWER(COALESCE(c.name, '')) IN ('tillægsydelser', 'tillaegsydelser')" .
|
|
||||||
(self::tableExists($db, 'products_options') && self::columnExists($db, 'products_options', 'option_id')
|
|
||||||
? ' OR EXISTS (SELECT 1 FROM products_options po WHERE po.option_id = p.id)'
|
|
||||||
: ''),
|
|
||||||
'restrictTankCleaning' => "p.category = 5 OR {$text} LIKE '%tank cleaning%' OR {$text} LIKE '%tankcleaning%' OR {$text} LIKE '%tankrens%' OR {$text} LIKE '%tank rens%'",
|
|
||||||
'restrictSpotFree' => "p.id IN (23, 24) OR {$text} LIKE '%spot free%' OR {$text} LIKE '%spotfree%' OR {$text} LIKE '%skylning med ro%'",
|
|
||||||
'restrictInteriorCleaning' => "{$text} LIKE '%interior%' OR {$text} LIKE '%indvendig%'",
|
|
||||||
'onlyTankCleaning' => "NOT (p.category = 5 OR {$text} LIKE '%tank cleaning%' OR {$text} LIKE '%tankcleaning%' OR {$text} LIKE '%tankrens%' OR {$text} LIKE '%tank rens%')",
|
|
||||||
default => '0 = 1',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function tableExists(object $db, string $table): bool
|
|
||||||
{
|
|
||||||
$safeTable = self::escape($db, $table);
|
|
||||||
$result = $db->query("SHOW TABLES LIKE '{$safeTable}'");
|
|
||||||
return $result && (int)$result->num_rows > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function indexExists(object $db, string $table, string $index): bool
|
|
||||||
{
|
|
||||||
$safeTable = str_replace('`', '', $table);
|
|
||||||
$safeIndex = self::escape($db, $index);
|
|
||||||
$result = $db->query("SHOW INDEX FROM `{$safeTable}` WHERE Key_name = '{$safeIndex}'");
|
|
||||||
return $result && (int)$result->num_rows > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function columnExists(object $db, string $table, string $column): bool
|
|
||||||
{
|
|
||||||
$safeTable = str_replace('`', '', $table);
|
|
||||||
$safeColumn = self::escape($db, $column);
|
|
||||||
$result = $db->query("SHOW COLUMNS FROM `{$safeTable}` LIKE '{$safeColumn}'");
|
|
||||||
return $result && (int)$result->num_rows > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function escape(object $db, string $value): string
|
|
||||||
{
|
|
||||||
return method_exists($db, 'escape_string')
|
|
||||||
? $db->escape_string($value)
|
|
||||||
: addslashes($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function affectedRows(object $db): int
|
|
||||||
{
|
|
||||||
if (method_exists($db, 'conn')) {
|
|
||||||
$connection = $db->conn();
|
|
||||||
return (int)($connection->affected_rows ?? 0);
|
|
||||||
}
|
|
||||||
return (int)($db->affected_rows ?? 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,508 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use RuntimeException;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class customer_rule_product_restriction_exception extends RuntimeException
|
|
||||||
{
|
|
||||||
public function __construct(string $message, private readonly int $httpStatus = 422, string $code = 'INVALID_CUSTOMER_RULE_CONFIGURATION')
|
|
||||||
{
|
|
||||||
parent::__construct($message);
|
|
||||||
$this->restrictionCode = $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string $restrictionCode;
|
|
||||||
|
|
||||||
public function httpStatus(): int
|
|
||||||
{
|
|
||||||
return $this->httpStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function restrictionCode(): string
|
|
||||||
{
|
|
||||||
return $this->restrictionCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source of truth for globally configured customer-rule product collections.
|
|
||||||
*/
|
|
||||||
class customer_rule_product_restriction_service
|
|
||||||
{
|
|
||||||
/** @var list<string> */
|
|
||||||
public const PRODUCT_IMPACT_ATTRIBUTES = [
|
|
||||||
'restrictAdditionalServices',
|
|
||||||
'restrictTankCleaning',
|
|
||||||
'restrictSpotFree',
|
|
||||||
'restrictInteriorCleaning',
|
|
||||||
'onlyTankCleaning',
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @var list<string> */
|
|
||||||
public const SUPPORTED_ATTRIBUTES = [
|
|
||||||
'restrictAdditionalServices',
|
|
||||||
'restrictTankCleaning',
|
|
||||||
'restrictSpotFree',
|
|
||||||
'restrictInteriorCleaning',
|
|
||||||
'onlyTankCleaning',
|
|
||||||
'requiresReferenceNumber',
|
|
||||||
'requiresRegistrationNumbersInvoice',
|
|
||||||
'invoiceAllOrdersIndividually',
|
|
||||||
'invoiceWithStripe',
|
|
||||||
'showPricesOnBookingPage',
|
|
||||||
'usePONumbers',
|
|
||||||
'exemptFromAdministrationFee',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
customer_rule_product_restriction_schema_bootstrap::ensureSchema();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return array{rules:list<array<string,mixed>>,products:list<array<string,mixed>>} */
|
|
||||||
public function listConfiguration(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'rules' => array_map(fn(string $attribute): array => $this->ruleConfiguration($attribute), self::PRODUCT_IMPACT_ATTRIBUTES),
|
|
||||||
'products' => $this->productCatalog(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return array<string,mixed> */
|
|
||||||
public function ruleConfiguration(string $attribute): array
|
|
||||||
{
|
|
||||||
$this->assertSupportedAttribute($attribute);
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$safeAttribute = $this->escape($attribute);
|
|
||||||
$versionResult = $db->query(
|
|
||||||
"SELECT version FROM customer_rule_product_restrictions WHERE attribute = '{$safeAttribute}' LIMIT 1"
|
|
||||||
);
|
|
||||||
if (!$versionResult || $versionResult->num_rows < 1) {
|
|
||||||
throw new RuntimeException("Unable to load customer-rule restriction version for {$attribute}");
|
|
||||||
}
|
|
||||||
$versionRow = $versionResult->fetch_assoc();
|
|
||||||
|
|
||||||
$result = $db->query(
|
|
||||||
"SELECT c.id AS collection_id, c.name, c.sort_order, cp.product_id
|
|
||||||
FROM customer_rule_product_collections c
|
|
||||||
LEFT JOIN customer_rule_product_collection_products cp ON cp.collection_id = c.id
|
|
||||||
WHERE c.attribute = '{$safeAttribute}'
|
|
||||||
ORDER BY c.sort_order ASC, c.id ASC, cp.product_id ASC"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
throw new RuntimeException("Unable to load customer-rule restriction collections for {$attribute}");
|
|
||||||
}
|
|
||||||
|
|
||||||
$collections = [];
|
|
||||||
$disabled = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$collectionId = (int)$row['collection_id'];
|
|
||||||
if (!isset($collections[$collectionId])) {
|
|
||||||
$collections[$collectionId] = [
|
|
||||||
'id' => $collectionId,
|
|
||||||
'name' => (string)$row['name'],
|
|
||||||
'sort_order' => (int)$row['sort_order'],
|
|
||||||
'product_ids' => [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if ($row['product_id'] !== null) {
|
|
||||||
$productId = (int)$row['product_id'];
|
|
||||||
$collections[$collectionId]['product_ids'][] = $productId;
|
|
||||||
$disabled[$productId] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'attribute' => $attribute,
|
|
||||||
'version' => max(1, (int)($versionRow['version'] ?? 1)),
|
|
||||||
'collections' => array_values($collections),
|
|
||||||
'disabled_product_ids' => array_map('intval', array_keys($disabled)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string,mixed> $payload
|
|
||||||
* @return array<string,mixed>
|
|
||||||
*/
|
|
||||||
public function replaceRuleConfiguration(string $attribute, array $payload, int $actorUserId): array
|
|
||||||
{
|
|
||||||
$this->assertSupportedAttribute($attribute);
|
|
||||||
$expectedVersion = $this->positiveInt($payload['version'] ?? null, 'version');
|
|
||||||
$collections = $this->validateCollections($attribute, $payload['collections'] ?? null);
|
|
||||||
|
|
||||||
global $db;
|
|
||||||
$safeAttribute = $this->escape($attribute);
|
|
||||||
if ($db->query('START TRANSACTION') === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to start configuration transaction', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$versionResult = $db->query(
|
|
||||||
"SELECT version FROM customer_rule_product_restrictions
|
|
||||||
WHERE attribute = '{$safeAttribute}' FOR UPDATE"
|
|
||||||
);
|
|
||||||
if (!$versionResult || $versionResult->num_rows < 1) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Customer rule configuration was not found', 404, 'CUSTOMER_RULE_CONFIGURATION_NOT_FOUND');
|
|
||||||
}
|
|
||||||
$versionRow = $versionResult->fetch_assoc();
|
|
||||||
$currentVersion = (int)$versionRow['version'];
|
|
||||||
if ($currentVersion !== $expectedVersion) {
|
|
||||||
throw new customer_rule_product_restriction_exception(
|
|
||||||
'Customer rule configuration has changed; reload before saving',
|
|
||||||
409,
|
|
||||||
'CUSTOMER_RULE_CONFIGURATION_CONFLICT'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$old = $this->ruleConfiguration($attribute);
|
|
||||||
$existingIds = $this->existingCollectionIds($attribute);
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
if ($collection['id'] !== null && !isset($existingIds[$collection['id']])) {
|
|
||||||
throw new customer_rule_product_restriction_exception('A collection does not belong to this customer rule');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid temporary unique-name collisions while two collections swap names.
|
|
||||||
foreach ($existingIds as $collectionId => $_) {
|
|
||||||
$temporaryName = $this->escape('__pending_' . $collectionId . '_' . bin2hex(random_bytes(6)));
|
|
||||||
if ($db->query(
|
|
||||||
"UPDATE customer_rule_product_collections
|
|
||||||
SET name = '{$temporaryName}'
|
|
||||||
WHERE id = {$collectionId} AND attribute = '{$safeAttribute}'"
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to prepare collection update', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$keptIds = [];
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
$name = $this->escape($collection['name']);
|
|
||||||
$sortOrder = (int)$collection['sort_order'];
|
|
||||||
$collectionId = $collection['id'];
|
|
||||||
if ($collectionId === null) {
|
|
||||||
if ($db->query(
|
|
||||||
"INSERT INTO customer_rule_product_collections (attribute, name, sort_order)
|
|
||||||
VALUES ('{$safeAttribute}', '{$name}', {$sortOrder})"
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to create collection');
|
|
||||||
}
|
|
||||||
$collectionId = (int)$db->insert_id();
|
|
||||||
} else {
|
|
||||||
if ($db->query(
|
|
||||||
"UPDATE customer_rule_product_collections
|
|
||||||
SET name = '{$name}', sort_order = {$sortOrder}
|
|
||||||
WHERE id = {$collectionId} AND attribute = '{$safeAttribute}'"
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to update collection');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$keptIds[$collectionId] = true;
|
|
||||||
if ($db->query("DELETE FROM customer_rule_product_collection_products WHERE collection_id = {$collectionId}") === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to replace collection products', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
foreach ($collection['product_ids'] as $productId) {
|
|
||||||
if ($db->query(
|
|
||||||
"INSERT INTO customer_rule_product_collection_products (collection_id, product_id)
|
|
||||||
VALUES ({$collectionId}, {$productId})"
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to save collection products');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$removeIds = array_values(array_diff(array_keys($existingIds), array_keys($keptIds)));
|
|
||||||
if ($removeIds !== []) {
|
|
||||||
if ($db->query(
|
|
||||||
'DELETE FROM customer_rule_product_collections WHERE attribute = \'' . $safeAttribute . '\' AND id IN (' .
|
|
||||||
implode(',', array_map('intval', $removeIds)) . ')'
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to remove collections', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newVersion = $currentVersion + 1;
|
|
||||||
if ($db->query(
|
|
||||||
"UPDATE customer_rule_product_restrictions
|
|
||||||
SET version = {$newVersion}, updated_at = NOW()
|
|
||||||
WHERE attribute = '{$safeAttribute}'"
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to update configuration version', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
$new = $this->ruleConfiguration($attribute);
|
|
||||||
$changes = $this->escape((string)json_encode([
|
|
||||||
'before' => $old,
|
|
||||||
'after' => $new,
|
|
||||||
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
|
||||||
if ($db->query(
|
|
||||||
"INSERT INTO customer_rule_product_audit_logs
|
|
||||||
(actor_user_id, attribute, old_version, new_version, changes_json)
|
|
||||||
VALUES ({$actorUserId}, '{$safeAttribute}', {$currentVersion}, {$newVersion}, '{$changes}')"
|
|
||||||
) === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to audit configuration update', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
if ($db->query('COMMIT') === false) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unable to commit configuration update', 500, 'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED');
|
|
||||||
}
|
|
||||||
return $new;
|
|
||||||
} catch (Throwable $throwable) {
|
|
||||||
$db->query('ROLLBACK');
|
|
||||||
throw $throwable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return configured product restrictions for all active product-impact
|
|
||||||
* attributes belonging to any account with the customer number.
|
|
||||||
*
|
|
||||||
* @return list<array<string,mixed>>
|
|
||||||
*/
|
|
||||||
public function restrictionsForCustomerNumber(int $customerNumber): array
|
|
||||||
{
|
|
||||||
if ($customerNumber < 1) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
global $db;
|
|
||||||
$result = $db->query(
|
|
||||||
"SELECT DISTINCT ca.attribute
|
|
||||||
FROM users u
|
|
||||||
INNER JOIN customer_attributes ca ON ca.user_id = u.id
|
|
||||||
WHERE u.customer_number = {$customerNumber}"
|
|
||||||
);
|
|
||||||
if (!$result) {
|
|
||||||
throw new RuntimeException('Unable to load active customer-rule product restrictions');
|
|
||||||
}
|
|
||||||
$activeAttributes = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$attribute = (string)$row['attribute'];
|
|
||||||
if (in_array($attribute, self::PRODUCT_IMPACT_ATTRIBUTES, true)) {
|
|
||||||
$activeAttributes[$attribute] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$active = [];
|
|
||||||
foreach (self::PRODUCT_IMPACT_ATTRIBUTES as $attribute) {
|
|
||||||
if (isset($activeAttributes[$attribute])) {
|
|
||||||
$active[] = $this->ruleConfiguration($attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $active;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return array{rules:list<string>,collections:list<int>,message:string,code:string,product_id:int}|null */
|
|
||||||
public function violationForCustomerProduct(int $customerNumber, int $productId): ?array
|
|
||||||
{
|
|
||||||
if ($productId < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$rules = [];
|
|
||||||
$collections = [];
|
|
||||||
foreach ($this->restrictionsForCustomerNumber($customerNumber) as $restriction) {
|
|
||||||
if (!in_array($productId, $restriction['disabled_product_ids'], true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$rules[] = (string)$restriction['attribute'];
|
|
||||||
foreach ($restriction['collections'] as $collection) {
|
|
||||||
if (in_array($productId, $collection['product_ids'], true)) {
|
|
||||||
$collections[] = (int)$collection['id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($rules === []) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
'code' => 'CUSTOMER_RULE_PRODUCT_RESTRICTED',
|
|
||||||
'message' => customer_product_rule_service::BLOCK_MESSAGE,
|
|
||||||
'product_id' => $productId,
|
|
||||||
'rules' => array_values(array_unique($rules)),
|
|
||||||
'collections' => array_values(array_unique($collections)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param list<array<string,mixed>> $attributes
|
|
||||||
* @return list<array<string,mixed>>
|
|
||||||
*/
|
|
||||||
public function enrichAttributes(int $customerNumber, array $attributes): array
|
|
||||||
{
|
|
||||||
$restrictions = [];
|
|
||||||
foreach ($this->restrictionsForCustomerNumber($customerNumber) as $restriction) {
|
|
||||||
$restrictions[(string)$restriction['attribute']] = [
|
|
||||||
'attribute' => (string)$restriction['attribute'],
|
|
||||||
'version' => (int)$restriction['version'],
|
|
||||||
'collections' => $restriction['collections'],
|
|
||||||
'disabled_product_ids' => $restriction['disabled_product_ids'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($attributes as &$attribute) {
|
|
||||||
$key = (string)($attribute['attribute'] ?? '');
|
|
||||||
$attribute['product_restriction'] = $restrictions[$key] ?? null;
|
|
||||||
}
|
|
||||||
unset($attribute);
|
|
||||||
return $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return list<array<string,mixed>> */
|
|
||||||
private function productCatalog(): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
$activeExpression = $this->columnExists('products', 'deleted_at')
|
|
||||||
? 'CASE WHEN p.deleted_at IS NULL THEN 1 ELSE 0 END'
|
|
||||||
: '1';
|
|
||||||
$result = $db->query(
|
|
||||||
"SELECT p.id, p.name, p.category AS category_id, c.name AS category_name,
|
|
||||||
{$activeExpression} AS active
|
|
||||||
FROM products p
|
|
||||||
LEFT JOIN categories c ON c.id = p.category
|
|
||||||
ORDER BY c.name ASC, p.name ASC, p.id ASC"
|
|
||||||
);
|
|
||||||
if (!$result) {
|
|
||||||
throw new RuntimeException('Unable to load the customer-rule product catalog');
|
|
||||||
}
|
|
||||||
$products = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$products[] = [
|
|
||||||
'id' => (int)$row['id'],
|
|
||||||
'name' => (string)$row['name'],
|
|
||||||
'category_id' => (int)$row['category_id'],
|
|
||||||
'category_name' => (string)($row['category_name'] ?? ''),
|
|
||||||
'active' => (bool)$row['active'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return $products;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int,true>
|
|
||||||
*/
|
|
||||||
private function existingCollectionIds(string $attribute): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
$safeAttribute = $this->escape($attribute);
|
|
||||||
$result = $db->query("SELECT id FROM customer_rule_product_collections WHERE attribute = '{$safeAttribute}'");
|
|
||||||
if (!$result) {
|
|
||||||
throw new RuntimeException("Unable to load existing collections for {$attribute}");
|
|
||||||
}
|
|
||||||
$ids = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$ids[(int)$row['id']] = true;
|
|
||||||
}
|
|
||||||
return $ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return list<array{id:?int,name:string,sort_order:int,product_ids:list<int>}> */
|
|
||||||
private function validateCollections(string $attribute, mixed $value): array
|
|
||||||
{
|
|
||||||
if (!is_array($value)) {
|
|
||||||
throw new customer_rule_product_restriction_exception('collections must be an array');
|
|
||||||
}
|
|
||||||
$normalized = [];
|
|
||||||
$names = [];
|
|
||||||
$collectionIds = [];
|
|
||||||
$allProductIds = [];
|
|
||||||
foreach (array_values($value) as $index => $collection) {
|
|
||||||
if (!is_array($collection)) {
|
|
||||||
throw new customer_rule_product_restriction_exception("Collection {$index} must be an object");
|
|
||||||
}
|
|
||||||
$name = trim((string)($collection['name'] ?? ''));
|
|
||||||
if ($name === '' || mb_strlen($name) > 191) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Collection names must be between 1 and 191 characters');
|
|
||||||
}
|
|
||||||
$nameKey = mb_strtolower($name);
|
|
||||||
if (isset($names[$nameKey])) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Collection names must be unique within a rule');
|
|
||||||
}
|
|
||||||
$names[$nameKey] = true;
|
|
||||||
if (!isset($collection['product_ids']) || !is_array($collection['product_ids'])) {
|
|
||||||
throw new customer_rule_product_restriction_exception('product_ids must be an array');
|
|
||||||
}
|
|
||||||
$productIds = [];
|
|
||||||
foreach ($collection['product_ids'] as $productId) {
|
|
||||||
$id = $this->positiveInt($productId, 'product_id');
|
|
||||||
$productIds[$id] = true;
|
|
||||||
$allProductIds[$id] = true;
|
|
||||||
}
|
|
||||||
$id = isset($collection['id']) && $collection['id'] !== null
|
|
||||||
? $this->positiveInt($collection['id'], 'collection id')
|
|
||||||
: null;
|
|
||||||
if ($id !== null && isset($collectionIds[$id])) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Collection IDs must be unique within a rule');
|
|
||||||
}
|
|
||||||
if ($id !== null) {
|
|
||||||
$collectionIds[$id] = true;
|
|
||||||
}
|
|
||||||
$normalized[] = [
|
|
||||||
'id' => $id,
|
|
||||||
'name' => $name,
|
|
||||||
'sort_order' => isset($collection['sort_order']) && is_numeric($collection['sort_order'])
|
|
||||||
? (int)$collection['sort_order']
|
|
||||||
: $index,
|
|
||||||
'product_ids' => array_map('intval', array_keys($productIds)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertProductsExist(array_map('intval', array_keys($allProductIds)));
|
|
||||||
return $normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param list<int> $productIds */
|
|
||||||
private function assertProductsExist(array $productIds): void
|
|
||||||
{
|
|
||||||
if ($productIds === []) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
global $db;
|
|
||||||
$result = $db->query('SELECT id FROM products WHERE id IN (' . implode(',', $productIds) . ')');
|
|
||||||
if (!$result) {
|
|
||||||
throw new customer_rule_product_restriction_exception(
|
|
||||||
'Unable to validate collection products',
|
|
||||||
500,
|
|
||||||
'CUSTOMER_RULE_CONFIGURATION_SAVE_FAILED'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$found = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$found[(int)$row['id']] = true;
|
|
||||||
}
|
|
||||||
$missing = array_values(array_diff($productIds, array_keys($found)));
|
|
||||||
if ($missing !== []) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unknown product IDs: ' . implode(', ', $missing));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertSupportedAttribute(string $attribute): void
|
|
||||||
{
|
|
||||||
if (!in_array($attribute, self::PRODUCT_IMPACT_ATTRIBUTES, true)) {
|
|
||||||
throw new customer_rule_product_restriction_exception('Unsupported product-impact customer rule');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function positiveInt(mixed $value, string $field): int
|
|
||||||
{
|
|
||||||
if (!is_numeric($value) || (int)$value < 1 || (string)(int)$value !== trim((string)$value)) {
|
|
||||||
throw new customer_rule_product_restriction_exception("{$field} must be a positive integer");
|
|
||||||
}
|
|
||||||
return (int)$value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function escape(string $value): string
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
return method_exists($db, 'escape_string') ? $db->escape_string($value) : addslashes($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function columnExists(string $table, string $column): bool
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
$safeTable = str_replace('`', '', $table);
|
|
||||||
$safeColumn = $this->escape($column);
|
|
||||||
$result = $db->query("SHOW COLUMNS FROM `{$safeTable}` LIKE '{$safeColumn}'");
|
|
||||||
return $result && (int)$result->num_rows > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
@@ -177,19 +170,15 @@ class db
|
|||||||
return $this->database;
|
return $this->database;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPort(): int
|
|
||||||
{
|
|
||||||
return $this->port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSslMode(): string
|
|
||||||
{
|
|
||||||
return $this->ssl_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function backupDatabase(string $path): bool
|
public function backupDatabase(string $path): bool
|
||||||
{
|
{
|
||||||
|
// Save the database to the path
|
||||||
|
// Build a safe mysqldump command with configurable SSL (MariaDB-compatible flags)
|
||||||
$mode = strtoupper(trim($this->ssl_mode));
|
$mode = strtoupper(trim($this->ssl_mode));
|
||||||
|
// Map ssl_mode to MariaDB client flags
|
||||||
|
// DISABLED => --skip-ssl (no TLS)
|
||||||
|
// PREFERRED => (no flag; client decides)
|
||||||
|
// REQUIRED/VERIFY_* => --ssl (enable TLS without strict verification unless CA materials provided)
|
||||||
$sslFlag = '';
|
$sslFlag = '';
|
||||||
switch ($mode) {
|
switch ($mode) {
|
||||||
case 'DISABLED':
|
case 'DISABLED':
|
||||||
@@ -205,42 +194,17 @@ class db
|
|||||||
$sslFlag = '--ssl';
|
$sslFlag = '--ssl';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = escapeshellarg($this->host);
|
$host = escapeshellarg($this->host);
|
||||||
$user = escapeshellarg($this->user);
|
$user = escapeshellarg($this->user);
|
||||||
|
$pass = escapeshellarg($this->password);
|
||||||
$db = escapeshellarg($this->database);
|
$db = escapeshellarg($this->database);
|
||||||
$port = (int)$this->port;
|
$port = (int)$this->port;
|
||||||
|
$outfile = escapeshellarg($path);
|
||||||
$sslPart = $sslFlag !== '' ? ($sslFlag . ' ') : '';
|
$sslPart = $sslFlag !== '' ? ($sslFlag . ' ') : '';
|
||||||
$command = "mysqldump {$sslPart}--single-transaction --quick --routines --triggers --events --hex-blob -h $host -P $port -u $user $db";
|
$command = "mysqldump {$sslPart}-h $host -P $port -u $user --password=$pass $db > $outfile 2>&1";
|
||||||
|
exec($command, $output, $return);
|
||||||
$directory = dirname($path);
|
// Check if the command was successful
|
||||||
if (!is_dir($directory) && !mkdir($directory, 0770, true) && !is_dir($directory)) {
|
return $return === 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$environment = array_merge(getenv() ?: [], $_ENV);
|
|
||||||
$environment['MYSQL_PWD'] = $this->password;
|
|
||||||
$descriptors = [
|
|
||||||
0 => ['pipe', 'r'],
|
|
||||||
1 => ['file', $path, 'w'],
|
|
||||||
2 => ['pipe', 'w'],
|
|
||||||
];
|
|
||||||
|
|
||||||
$process = proc_open($command, $descriptors, $pipes, null, $environment);
|
|
||||||
if (!is_resource($process)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($pipes[0]);
|
|
||||||
$stderr = stream_get_contents($pipes[2]);
|
|
||||||
fclose($pipes[2]);
|
|
||||||
$return = proc_close($process);
|
|
||||||
|
|
||||||
if ($return !== 0 && is_string($stderr) && $stderr !== '') {
|
|
||||||
@file_put_contents($path . '.error.log', $stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return === 0 && is_file($path) && filesize($path) !== false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getView(string $view): array
|
public function getView(string $view): array
|
||||||
@@ -267,4 +231,4 @@ class db
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures additive schema for department-scoped customer price overrides.
|
|
||||||
*/
|
|
||||||
class department_customer_price_overrides_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 `department_customer_price_overrides` (
|
|
||||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
||||||
`department_id` INT NOT NULL,
|
|
||||||
`user_id` INT NOT NULL,
|
|
||||||
`is_category` TINYINT(1) NOT NULL DEFAULT 0,
|
|
||||||
`product_or_category_id` VARCHAR(191) NOT NULL,
|
|
||||||
`percentage` INT NOT NULL DEFAULT 0,
|
|
||||||
`fixed_price` INT NULL DEFAULT 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_department_customer_price_overrides_lookup` (`department_id`, `user_id`, `is_category`, `product_or_category_id`),
|
|
||||||
KEY `idx_department_customer_price_overrides_department` (`department_id`),
|
|
||||||
KEY `idx_department_customer_price_overrides_user` (`user_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
||||||
);
|
|
||||||
|
|
||||||
self::$initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,353 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use objects\department_customer_price_overrides_o;
|
|
||||||
use objects\departments_o;
|
|
||||||
use objects\products_o;
|
|
||||||
use objects\users_o;
|
|
||||||
|
|
||||||
class department_customer_pricing_service
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getPricing(int $departmentId, int $userId): array
|
|
||||||
{
|
|
||||||
$department = $this->department($departmentId);
|
|
||||||
$customer = $this->customer($userId);
|
|
||||||
$this->assertEnabled($department);
|
|
||||||
|
|
||||||
$overrides = (new department_customer_price_overrides_o())->getAllPrices($departmentId, $userId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'department' => $department,
|
|
||||||
'customer' => $customer,
|
|
||||||
'overrides' => $overrides,
|
|
||||||
'categories' => $this->catalog($departmentId, $customer['id']),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $payload
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function updatePricing(int $departmentId, int $userId, array $payload): array
|
|
||||||
{
|
|
||||||
$department = $this->department($departmentId);
|
|
||||||
$customer = $this->customer($userId);
|
|
||||||
$this->assertEnabled($department);
|
|
||||||
|
|
||||||
if (array_key_exists('department_id', $payload) && (int)$payload['department_id'] !== $departmentId) {
|
|
||||||
throw new limited_backoffice_exception('Department ID in body does not match the route.', 400);
|
|
||||||
}
|
|
||||||
if (array_key_exists('user_id', $payload) && (int)$payload['user_id'] !== $userId) {
|
|
||||||
throw new limited_backoffice_exception('User ID in body does not match the route.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$overrides = $payload['overrides'] ?? null;
|
|
||||||
if (!is_array($overrides)) {
|
|
||||||
throw new limited_backoffice_exception('Overrides are required.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$normalized = $this->normalizeOverrides($departmentId, $overrides);
|
|
||||||
$overrideObject = new department_customer_price_overrides_o();
|
|
||||||
$existingOverrides = $overrideObject->getAllPrices($departmentId, $customer['id']);
|
|
||||||
$normalizedKeys = [];
|
|
||||||
foreach ($normalized as $override) {
|
|
||||||
$normalizedKeys[$this->overrideKey((bool)$override['is_category'], $override['product_or_category_id'])] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
global $db;
|
|
||||||
$db->conn()->begin_transaction();
|
|
||||||
try {
|
|
||||||
$db->query(
|
|
||||||
'DELETE FROM `department_customer_price_overrides` WHERE `department_id` = '
|
|
||||||
. (int)$departmentId . ' AND `user_id` = ' . (int)$customer['id']
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($normalized as $override) {
|
|
||||||
$overrideObject->setPrice(
|
|
||||||
$departmentId,
|
|
||||||
$customer['id'],
|
|
||||||
(bool)$override['is_category'],
|
|
||||||
$override['product_or_category_id'],
|
|
||||||
(int)$override['percentage'],
|
|
||||||
$override['fixed_price']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$db->conn()->commit();
|
|
||||||
} catch (\Throwable) {
|
|
||||||
$db->conn()->rollback();
|
|
||||||
throw new limited_backoffice_exception('Unable to update department customer pricing.', 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($normalized as $override) {
|
|
||||||
$this->recordVersion($customer, $departmentId, $override);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($existingOverrides as $existingOverride) {
|
|
||||||
$key = $this->overrideKey((bool)$existingOverride['is_category'], $existingOverride['product_or_category_id']);
|
|
||||||
if (isset($normalizedKeys[$key])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->recordVersion($customer, $departmentId, [
|
|
||||||
'is_category' => (bool)$existingOverride['is_category'],
|
|
||||||
'product_or_category_id' => $existingOverride['product_or_category_id'],
|
|
||||||
'percentage' => 0,
|
|
||||||
'fixed_price' => null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->getPricing($departmentId, $customer['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array{id:int,name:string,description:string,custom_pricing_only:bool}
|
|
||||||
*/
|
|
||||||
private function department(int $departmentId): array
|
|
||||||
{
|
|
||||||
$department = (new departments_o())->getDepartmentById($departmentId);
|
|
||||||
if (!is_array($department) || empty($department)) {
|
|
||||||
throw new limited_backoffice_exception('Department not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => (int)$department['id'],
|
|
||||||
'name' => (string)$department['name'],
|
|
||||||
'description' => (string)($department['description'] ?? ''),
|
|
||||||
'custom_pricing_only' => (bool)(int)($department['custom_pricing_only'] ?? 0),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array{id:int,customer_number:int,display_name:string}
|
|
||||||
*/
|
|
||||||
private function customer(int $userId): array
|
|
||||||
{
|
|
||||||
$customer = (new users_o())->getUserById($userId);
|
|
||||||
if (!$customer->exists()) {
|
|
||||||
throw new limited_backoffice_exception('Customer not found', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => (int)$customer->id,
|
|
||||||
'customer_number' => (int)$customer->customer_number->value(),
|
|
||||||
'display_name' => (string)($customer->display_name->value() ?: ('Customer #' . $customer->customer_number->value())),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $department
|
|
||||||
*/
|
|
||||||
private function assertEnabled(array $department): void
|
|
||||||
{
|
|
||||||
if (!($department['custom_pricing_only'] ?? false)) {
|
|
||||||
throw new limited_backoffice_exception('Department customer pricing is disabled.', 409, [
|
|
||||||
'message' => 'Department customer pricing is disabled.',
|
|
||||||
'code' => 'department_customer_pricing_disabled',
|
|
||||||
'department' => $department,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, array<string, mixed>>
|
|
||||||
*/
|
|
||||||
private function catalog(int $departmentId, int $userId): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$sql = "
|
|
||||||
SELECT
|
|
||||||
c.`id` AS `category_id`,
|
|
||||||
c.`name` AS `category_name`,
|
|
||||||
c.`description` AS `category_description`,
|
|
||||||
p.*,
|
|
||||||
pdp.`price` AS `department_price`
|
|
||||||
FROM `department_categories` dc
|
|
||||||
INNER JOIN `categories` c ON c.`id` = dc.`category_id`
|
|
||||||
INNER JOIN `products` p ON p.`category` = dc.`category_id`
|
|
||||||
LEFT JOIN `product_department_prices` pdp
|
|
||||||
ON pdp.`department_id` = dc.`department_id`
|
|
||||||
AND pdp.`product_id` = p.`id`
|
|
||||||
WHERE dc.`department_id` = " . (int)$departmentId . "
|
|
||||||
AND dc.`deleted_at` IS NULL
|
|
||||||
ORDER BY c.`name` ASC, c.`id` ASC, p.`order_priority` ASC, p.`name` ASC, p.`id` ASC";
|
|
||||||
|
|
||||||
$result = $db->query($sql);
|
|
||||||
$rows = $result ? $db->fetch_all($result) : [];
|
|
||||||
$customer = (new users_o())->getUserById($userId);
|
|
||||||
$categories = [];
|
|
||||||
$seen = [];
|
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$productId = (int)$row['id'];
|
|
||||||
if (isset($seen[$productId])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$seen[$productId] = true;
|
|
||||||
|
|
||||||
$categoryId = (int)$row['category_id'];
|
|
||||||
if (!isset($categories[$categoryId])) {
|
|
||||||
$categories[$categoryId] = [
|
|
||||||
'id' => $categoryId,
|
|
||||||
'name' => (string)$row['category_name'],
|
|
||||||
'description' => (string)($row['category_description'] ?? ''),
|
|
||||||
'products' => [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$departmentPrice = $row['department_price'] === null ? null : (int)$row['department_price'];
|
|
||||||
$effectivePrice = products_o::CUSTOM_PRICING_MISSING_PRICE;
|
|
||||||
if ($departmentPrice !== null) {
|
|
||||||
$effectivePrice = $customer->applyProductCustomerPricing($productId, $departmentPrice, true, $departmentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$categories[$categoryId]['products'][] = [
|
|
||||||
'id' => $productId,
|
|
||||||
'name' => (string)$row['name'],
|
|
||||||
'description' => (string)($row['description'] ?? ''),
|
|
||||||
'category' => $categoryId,
|
|
||||||
'apply_category_discount' => (bool)$row['apply_category_discount'],
|
|
||||||
'base_price' => (int)$row['price'],
|
|
||||||
'department_price' => $departmentPrice,
|
|
||||||
'effective_price' => $effectivePrice,
|
|
||||||
'missing_department_price' => $departmentPrice === null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int, mixed> $overrides
|
|
||||||
* @return array<int, array{is_category:bool,product_or_category_id:int|string,percentage:int,fixed_price:int|null}>
|
|
||||||
*/
|
|
||||||
private function normalizeOverrides(int $departmentId, array $overrides): array
|
|
||||||
{
|
|
||||||
$normalized = [];
|
|
||||||
foreach ($overrides as $override) {
|
|
||||||
if (!is_array($override)) {
|
|
||||||
throw new limited_backoffice_exception('Invalid override payload.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$isCategory = (bool)($override['is_category'] ?? false);
|
|
||||||
$objectId = $override['product_or_category_id'] ?? $override['object_id'] ?? null;
|
|
||||||
if ($objectId === null || $objectId === '') {
|
|
||||||
throw new limited_backoffice_exception('Override object is required.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$percentage = filter_var($override['discount'] ?? $override['percentage'] ?? 0, FILTER_VALIDATE_INT);
|
|
||||||
if ($percentage === false || $percentage < 0 || $percentage > 100) {
|
|
||||||
throw new limited_backoffice_exception('Discount must be between 0 and 100.', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fixedPrice = null;
|
|
||||||
if (array_key_exists('fixed_price', $override) && $override['fixed_price'] !== null && $override['fixed_price'] !== '') {
|
|
||||||
$fixedPrice = filter_var($override['fixed_price'], FILTER_VALIDATE_INT);
|
|
||||||
if ($fixedPrice === false || $fixedPrice < 0) {
|
|
||||||
throw new limited_backoffice_exception('Fixed price must be zero or more.', 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isCategory) {
|
|
||||||
$fixedPrice = null;
|
|
||||||
$objectId = (string)$objectId;
|
|
||||||
if ($objectId !== 'global') {
|
|
||||||
$this->assertDepartmentCategory($departmentId, $objectId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$objectId = (int)$objectId;
|
|
||||||
$this->assertDepartmentProduct($departmentId, $objectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($percentage <= 0 && $fixedPrice === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $this->overrideKey($isCategory, $objectId);
|
|
||||||
$normalized[$key] = [
|
|
||||||
'is_category' => $isCategory,
|
|
||||||
'product_or_category_id' => $objectId,
|
|
||||||
'percentage' => (int)$percentage,
|
|
||||||
'fixed_price' => $fixedPrice === null ? null : (int)$fixedPrice,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($normalized);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array{id:int,customer_number:int,display_name:string} $customer
|
|
||||||
* @param array{is_category:bool,product_or_category_id:int|string,percentage:int,fixed_price:int|null} $override
|
|
||||||
*/
|
|
||||||
private function recordVersion(array $customer, int $departmentId, array $override): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
(new economic_v2_versioning_service())->recordDiscountOverrideVersion(
|
|
||||||
(int)$customer['id'],
|
|
||||||
(int)$customer['customer_number'],
|
|
||||||
(bool)$override['is_category'],
|
|
||||||
(string)$override['product_or_category_id'],
|
|
||||||
(int)$override['percentage'],
|
|
||||||
date('Y-m-d H:i:s'),
|
|
||||||
'live.department_discount_override.route',
|
|
||||||
1.0,
|
|
||||||
false,
|
|
||||||
[
|
|
||||||
'route' => 'department_customer_pricing',
|
|
||||||
'department_id' => $departmentId,
|
|
||||||
],
|
|
||||||
$override['fixed_price'],
|
|
||||||
$departmentId
|
|
||||||
);
|
|
||||||
} catch (\Throwable) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function overrideKey(bool $isCategory, int|string $objectId): string
|
|
||||||
{
|
|
||||||
return ((int)$isCategory) . ':' . (string)$objectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertDepartmentProduct(int $departmentId, int $productId): void
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$result = $db->query(
|
|
||||||
'SELECT p.`id`
|
|
||||||
FROM `department_categories` dc
|
|
||||||
INNER JOIN `products` p ON p.`category` = dc.`category_id`
|
|
||||||
WHERE dc.`department_id` = ' . (int)$departmentId . '
|
|
||||||
AND dc.`deleted_at` IS NULL
|
|
||||||
AND p.`id` = ' . (int)$productId . '
|
|
||||||
LIMIT 1'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$result || $result->num_rows < 1) {
|
|
||||||
throw new limited_backoffice_exception('Product is not available for this department.', 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertDepartmentCategory(int $departmentId, string $categoryId): void
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$categoryId = $db->escape_string($categoryId);
|
|
||||||
$result = $db->query(
|
|
||||||
"SELECT `id`
|
|
||||||
FROM `department_categories`
|
|
||||||
WHERE `department_id` = " . (int)$departmentId . "
|
|
||||||
AND `deleted_at` IS NULL
|
|
||||||
AND `category_id` = '{$categoryId}'
|
|
||||||
LIMIT 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$result || $result->num_rows < 1) {
|
|
||||||
throw new limited_backoffice_exception('Category is not available for this department.', 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,252 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
require_once WD . '/classes/selfserve_schema_bootstrap.php';
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
class department_wash_count_service
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function countInDateRange(string $date_start, string $date_end, int $department_id): int
|
|
||||||
{
|
|
||||||
$rows = $this->countByHourForDepartments($date_start, $date_end, [$department_id]);
|
|
||||||
$total = 0;
|
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$total += (int)($row['wash_count'] ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int|string> $department_ids
|
|
||||||
* @return array<int,array{department_id:int,hour_bucket:string,wash_count:int}>
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function countByHourForDepartments(string $date_start, string $date_end, array $department_ids): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$this->validateDateRange($date_start, $date_end);
|
|
||||||
$normalized_department_ids = $this->normalizeIds($department_ids);
|
|
||||||
if ($normalized_department_ids === []) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
selfserve_schema_bootstrap::ensureTables();
|
|
||||||
|
|
||||||
$department_ids_sql = implode(',', $normalized_department_ids);
|
|
||||||
$escaped_start = $db->escape_string($date_start);
|
|
||||||
$escaped_end = $db->escape_string($date_end);
|
|
||||||
$candidate_sql = $this->candidateUnionSql($department_ids_sql, $escaped_start, $escaped_end);
|
|
||||||
|
|
||||||
$sql = "SELECT deduped.department_id,
|
|
||||||
DATE_FORMAT(deduped.counted_at, '%Y-%m-%d %H:00:00') AS hour_bucket,
|
|
||||||
COUNT(*) AS wash_count
|
|
||||||
FROM (
|
|
||||||
SELECT dedupe_key,
|
|
||||||
department_id,
|
|
||||||
MIN(counted_at) AS counted_at
|
|
||||||
FROM ($candidate_sql) candidates
|
|
||||||
GROUP BY dedupe_key, department_id
|
|
||||||
) deduped
|
|
||||||
GROUP BY deduped.department_id, DATE_FORMAT(deduped.counted_at, '%Y-%m-%d %H:00:00')
|
|
||||||
ORDER BY deduped.department_id ASC, hour_bucket ASC";
|
|
||||||
|
|
||||||
$result = $db->query($sql);
|
|
||||||
if (!is_object($result) || $result->num_rows === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$rows[] = [
|
|
||||||
'department_id' => (int)($row['department_id'] ?? 0),
|
|
||||||
'hour_bucket' => (string)($row['hour_bucket'] ?? ''),
|
|
||||||
'wash_count' => (int)($row['wash_count'] ?? 0),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int|string> $department_ids
|
|
||||||
* @return array{quantity:int,products:int,earnings:int,washes:int}
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function transactionSummary(string $date_start, string $date_end, array $department_ids): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$this->validateDateRange($date_start, $date_end);
|
|
||||||
$normalized_department_ids = $this->normalizeIds($department_ids);
|
|
||||||
if ($normalized_department_ids === []) {
|
|
||||||
return [
|
|
||||||
'quantity' => 0,
|
|
||||||
'products' => 0,
|
|
||||||
'earnings' => 0,
|
|
||||||
'washes' => 0,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$department_ids_sql = implode(',', $normalized_department_ids);
|
|
||||||
$escaped_start = $db->escape_string($date_start);
|
|
||||||
$escaped_end = $db->escape_string($date_end);
|
|
||||||
|
|
||||||
$sql = "SELECT COUNT(DISTINCT o.id) AS quantity,
|
|
||||||
COALESCE(SUM(oi.quantity), 0) AS products,
|
|
||||||
COALESCE(SUM(oi.price * oi.quantity), 0) AS earnings
|
|
||||||
FROM orders o
|
|
||||||
JOIN order_items oi ON oi.order_id = o.id
|
|
||||||
WHERE o.department_id IN ($department_ids_sql)
|
|
||||||
AND o.created_at BETWEEN '$escaped_start' AND '$escaped_end'
|
|
||||||
AND o.deleted_at IS NULL
|
|
||||||
AND oi.deleted_at IS NULL";
|
|
||||||
|
|
||||||
$result = $db->query($sql);
|
|
||||||
$row = is_object($result) ? $result->fetch_assoc() : null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'quantity' => (int)($row['quantity'] ?? 0),
|
|
||||||
'products' => (int)($row['products'] ?? 0),
|
|
||||||
'earnings' => (int)round((float)($row['earnings'] ?? 0)),
|
|
||||||
'washes' => $this->countRows($date_start, $date_end, $normalized_department_ids),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int|string> $department_ids
|
|
||||||
* @return array<int,array{id:int,department_id:int,created_at:string}>
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function listTransactions(string $date_start, string $date_end, array $department_ids): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$this->validateDateRange($date_start, $date_end);
|
|
||||||
$normalized_department_ids = $this->normalizeIds($department_ids);
|
|
||||||
if ($normalized_department_ids === []) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
selfserve_schema_bootstrap::ensureTables();
|
|
||||||
|
|
||||||
$department_ids_sql = implode(',', $normalized_department_ids);
|
|
||||||
$escaped_start = $db->escape_string($date_start);
|
|
||||||
$escaped_end = $db->escape_string($date_end);
|
|
||||||
$candidate_sql = $this->candidateUnionSql($department_ids_sql, $escaped_start, $escaped_end);
|
|
||||||
|
|
||||||
$sql = "SELECT CAST(SUBSTRING_INDEX(GROUP_CONCAT(entity_id ORDER BY source_priority ASC, entity_id ASC), ',', 1) AS UNSIGNED) AS id,
|
|
||||||
department_id,
|
|
||||||
MIN(counted_at) AS created_at
|
|
||||||
FROM ($candidate_sql) candidates
|
|
||||||
GROUP BY dedupe_key, department_id
|
|
||||||
ORDER BY created_at ASC";
|
|
||||||
|
|
||||||
$result = $db->query($sql);
|
|
||||||
if (!is_object($result) || $result->num_rows === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = [];
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$rows[] = [
|
|
||||||
'id' => (int)($row['id'] ?? 0),
|
|
||||||
'department_id' => (int)($row['department_id'] ?? 0),
|
|
||||||
'created_at' => (string)($row['created_at'] ?? ''),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int> $department_ids
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function countRows(string $date_start, string $date_end, array $department_ids): int
|
|
||||||
{
|
|
||||||
$rows = $this->countByHourForDepartments($date_start, $date_end, $department_ids);
|
|
||||||
$total = 0;
|
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$total += (int)($row['wash_count'] ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $total;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function candidateUnionSql(string $department_ids_sql, string $escaped_start, string $escaped_end): string
|
|
||||||
{
|
|
||||||
return "SELECT CONCAT('order:', o.id) AS dedupe_key,
|
|
||||||
o.id AS entity_id,
|
|
||||||
o.department_id,
|
|
||||||
o.created_at AS counted_at,
|
|
||||||
0 AS source_priority
|
|
||||||
FROM orders o
|
|
||||||
JOIN order_items oi ON oi.order_id = o.id
|
|
||||||
JOIN products p ON p.id = oi.product_id
|
|
||||||
WHERE o.department_id IN ($department_ids_sql)
|
|
||||||
AND o.created_at BETWEEN '$escaped_start' AND '$escaped_end'
|
|
||||||
AND o.deleted_at IS NULL
|
|
||||||
AND oi.deleted_at IS NULL
|
|
||||||
AND p.is_wash = 1
|
|
||||||
UNION ALL
|
|
||||||
SELECT CASE
|
|
||||||
WHEN linked_o.id IS NOT NULL THEN CONCAT('order:', linked_o.id)
|
|
||||||
ELSE CONCAT('selfserve:', s.id)
|
|
||||||
END AS dedupe_key,
|
|
||||||
CASE
|
|
||||||
WHEN linked_o.id IS NOT NULL THEN linked_o.id
|
|
||||||
ELSE s.id
|
|
||||||
END AS entity_id,
|
|
||||||
COALESCE(linked_o.department_id, s.department_id) AS department_id,
|
|
||||||
COALESCE(linked_o.created_at, s.completed_at) AS counted_at,
|
|
||||||
1 AS source_priority
|
|
||||||
FROM selfserve_wash_sessions s
|
|
||||||
LEFT JOIN orders linked_o
|
|
||||||
ON linked_o.id = s.order_id
|
|
||||||
AND linked_o.deleted_at IS NULL
|
|
||||||
WHERE COALESCE(linked_o.department_id, s.department_id) IN ($department_ids_sql)
|
|
||||||
AND COALESCE(linked_o.created_at, s.completed_at) BETWEEN '$escaped_start' AND '$escaped_end'
|
|
||||||
AND s.deleted_at IS NULL
|
|
||||||
AND s.completed_at IS NOT NULL
|
|
||||||
AND UPPER(TRIM(s.status)) = 'COMPLETED'";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int|string> $ids
|
|
||||||
* @return array<int>
|
|
||||||
*/
|
|
||||||
private function normalizeIds(array $ids): array
|
|
||||||
{
|
|
||||||
$normalized = [];
|
|
||||||
foreach ($ids as $id) {
|
|
||||||
$value = (int)$id;
|
|
||||||
if ($value > 0) {
|
|
||||||
$normalized[$value] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($normalized);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function validateDateRange(string $date_start, string $date_end): void
|
|
||||||
{
|
|
||||||
if (strtotime($date_start) === false || strtotime($date_end) === false) {
|
|
||||||
throw new Exception('Invalid date range provided');
|
|
||||||
}
|
|
||||||
if (strtotime($date_start) > strtotime($date_end)) {
|
|
||||||
throw new Exception('The start date cannot be after the end date');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class economic_v2_distribution_service
|
|||||||
|
|
||||||
public function __construct(?economic_v2_versioning_service $versioning = null, ?economic $economic = null)
|
public function __construct(?economic_v2_versioning_service $versioning = null, ?economic $economic = null)
|
||||||
{
|
{
|
||||||
department_customer_price_overrides_schema_bootstrap::ensureTables();
|
|
||||||
$this->versioning = $versioning ?? new economic_v2_versioning_service();
|
$this->versioning = $versioning ?? new economic_v2_versioning_service();
|
||||||
$this->economic = $economic;
|
$this->economic = $economic;
|
||||||
}
|
}
|
||||||
@@ -389,16 +388,7 @@ class economic_v2_distribution_service
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$discount_row = $this->resolveDiscountForProduct($customer_number, $product_id, $created_at, $department_id);
|
$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;
|
||||||
@@ -902,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 '
|
||||||
@@ -911,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,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1076,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) {
|
||||||
@@ -1108,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])) {
|
||||||
@@ -1159,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,
|
||||||
@@ -1214,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;
|
||||||
}
|
}
|
||||||
@@ -1415,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) {
|
||||||
@@ -1426,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,13 +1464,7 @@ class economic_v2_distribution_service
|
|||||||
$line_price = ((float)$this->getProductDepartmentPrice($product_id, $department_id)) * $quantity;
|
$line_price = ((float)$this->getProductDepartmentPrice($product_id, $department_id)) * $quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
$discount_row = $this->resolveDiscountForProduct($customer_number, $product_id, $timestamp, $department_id);
|
$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));
|
||||||
@@ -1545,22 +1474,15 @@ class economic_v2_distribution_service
|
|||||||
return $total;
|
return $total;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function resolveDiscountForProduct(int $customer_number, int $product_id, string $timestamp, ?int $department_id = null): ?array
|
protected function resolveDiscountForProduct(int $customer_number, int $product_id, string $timestamp): ?array
|
||||||
{
|
{
|
||||||
$cache_key = $customer_number . '|' . $product_id . '|' . (int)($department_id ?? 0) . '|' . substr($timestamp, 0, 19);
|
$cache_key = $customer_number . '|' . $product_id . '|' . substr($timestamp, 0, 19);
|
||||||
if (array_key_exists($cache_key, $this->discount_resolution_cache)) {
|
if (array_key_exists($cache_key, $this->discount_resolution_cache)) {
|
||||||
return $this->discount_resolution_cache[$cache_key];
|
return $this->discount_resolution_cache[$cache_key];
|
||||||
}
|
}
|
||||||
|
|
||||||
$scopedDepartmentId = $department_id !== null && (new \objects\departments_o())->isCustomPricingOnly((int)$department_id)
|
$direct = $this->versioning->resolveDiscountOverrideAt($customer_number, false, (string)$product_id, $timestamp);
|
||||||
? (int)$department_id
|
if ($direct !== null && (int)($direct['discount'] ?? 0) > 0) {
|
||||||
: null;
|
|
||||||
|
|
||||||
$direct = $this->versioning->resolveDiscountOverrideAt($customer_number, false, (string)$product_id, $timestamp, $scopedDepartmentId);
|
|
||||||
if ($direct !== null && (
|
|
||||||
(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1568,20 +1490,13 @@ class economic_v2_distribution_service
|
|||||||
if ($product !== null) {
|
if ($product !== null) {
|
||||||
$category = (string)$product->category->value();
|
$category = (string)$product->category->value();
|
||||||
if ($category !== '') {
|
if ($category !== '') {
|
||||||
$category_discount = $this->versioning->resolveDiscountOverrideAt($customer_number, true, $category, $timestamp, $scopedDepartmentId);
|
$category_discount = $this->versioning->resolveDiscountOverrideAt($customer_number, true, $category, $timestamp);
|
||||||
if ($category_discount !== null && (int)($category_discount['discount'] ?? 0) > 0) {
|
if ($category_discount !== null && (int)($category_discount['discount'] ?? 0) > 0) {
|
||||||
return $this->discount_resolution_cache[$cache_key] = $category_discount;
|
return $this->discount_resolution_cache[$cache_key] = $category_discount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scopedDepartmentId !== null) {
|
|
||||||
$global_discount = $this->versioning->resolveDiscountOverrideAt($customer_number, true, 'global', $timestamp, $scopedDepartmentId);
|
|
||||||
if ($global_discount !== null && (int)($global_discount['discount'] ?? 0) > 0) {
|
|
||||||
return $this->discount_resolution_cache[$cache_key] = $global_discount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->discount_resolution_cache[$cache_key] = null;
|
return $this->discount_resolution_cache[$cache_key] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,13 +60,11 @@ class economic_v2_schema_bootstrap
|
|||||||
|
|
||||||
"CREATE TABLE IF NOT EXISTS customer_discount_override_versions (
|
"CREATE TABLE IF NOT EXISTS customer_discount_override_versions (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
department_id INT NULL DEFAULT NULL,
|
|
||||||
user_id INT NOT NULL,
|
user_id INT NOT NULL,
|
||||||
customer_number INT NOT NULL,
|
customer_number INT NOT NULL,
|
||||||
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',
|
||||||
@@ -76,7 +74,6 @@ class economic_v2_schema_bootstrap
|
|||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
INDEX idx_discount_override_versions_lookup (customer_number, is_category, object_id, effective_from, effective_to),
|
INDEX idx_discount_override_versions_lookup (customer_number, is_category, object_id, effective_from, effective_to),
|
||||||
INDEX idx_discount_override_versions_department_lookup (department_id, customer_number, is_category, object_id, effective_from, effective_to),
|
|
||||||
INDEX idx_discount_override_versions_user (user_id),
|
INDEX idx_discount_override_versions_user (user_id),
|
||||||
INDEX idx_discount_override_versions_source (source)
|
INDEX idx_discount_override_versions_source (source)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
||||||
@@ -86,22 +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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self::tableHasColumn('customer_discount_override_versions', 'department_id')) {
|
|
||||||
$db->query(
|
|
||||||
"ALTER TABLE customer_discount_override_versions
|
|
||||||
ADD COLUMN department_id INT NULL DEFAULT NULL
|
|
||||||
AFTER id"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$initialized = true;
|
self::$initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,3 +106,4 @@ class economic_v2_schema_bootstrap
|
|||||||
return ((int)($row['c'] ?? 0)) > 0;
|
return ((int)($row['c'] ?? 0)) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,9 +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,
|
|
||||||
?int $department_id = null
|
|
||||||
): array {
|
): array {
|
||||||
$identity = [
|
$identity = [
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
@@ -145,11 +143,8 @@ class economic_v2_versioning_service
|
|||||||
'is_category' => (int)$is_category,
|
'is_category' => (int)$is_category,
|
||||||
'object_id' => (string)$object_id,
|
'object_id' => (string)$object_id,
|
||||||
];
|
];
|
||||||
if ($department_id !== null) {
|
|
||||||
$identity['department_id'] = (int)$department_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,
|
||||||
@@ -166,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,
|
||||||
@@ -244,21 +238,15 @@ class economic_v2_versioning_service
|
|||||||
int $customer_number,
|
int $customer_number,
|
||||||
bool $is_category,
|
bool $is_category,
|
||||||
int|string $object_id,
|
int|string $object_id,
|
||||||
string $timestamp,
|
string $timestamp
|
||||||
?int $department_id = null
|
|
||||||
): ?array {
|
): ?array {
|
||||||
$identity = [
|
|
||||||
'customer_number' => $customer_number,
|
|
||||||
'is_category' => (int)$is_category,
|
|
||||||
'object_id' => (string)$object_id,
|
|
||||||
];
|
|
||||||
if ($department_id !== null) {
|
|
||||||
$identity['department_id'] = (int)$department_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = $this->resolveActiveVersions(
|
$rows = $this->resolveActiveVersions(
|
||||||
'customer_discount_override_versions',
|
'customer_discount_override_versions',
|
||||||
$identity,
|
[
|
||||||
|
'customer_number' => $customer_number,
|
||||||
|
'is_category' => (int)$is_category,
|
||||||
|
'object_id' => (string)$object_id,
|
||||||
|
],
|
||||||
$timestamp,
|
$timestamp,
|
||||||
'effective_from DESC, id DESC',
|
'effective_from DESC, id DESC',
|
||||||
1
|
1
|
||||||
@@ -423,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
|
||||||
@@ -449,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');
|
||||||
}
|
}
|
||||||
@@ -723,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') ?: ''));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ use fxratesapi\actions\convert_rate_a;
|
|||||||
use fxratesapi\fxratesapi_c;
|
use fxratesapi\fxratesapi_c;
|
||||||
use interfaces\fxratesapi_i;
|
use interfaces\fxratesapi_i;
|
||||||
use objects\fxratesapi_conversion_rates_o;
|
use objects\fxratesapi_conversion_rates_o;
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class fxratesapi implements fxratesapi_i
|
class fxratesapi implements fxratesapi_i
|
||||||
{
|
{
|
||||||
@@ -118,10 +117,10 @@ class fxratesapi implements fxratesapi_i
|
|||||||
// Validate the base and target currencies
|
// Validate the base and target currencies
|
||||||
self::requireValidCurrency($base);
|
self::requireValidCurrency($base);
|
||||||
self::requireValidCurrency($target);
|
self::requireValidCurrency($target);
|
||||||
|
// Validate the daily limit
|
||||||
|
self::requireDailyLimitNotExceeded();
|
||||||
// Validate the secret key
|
// Validate the secret key
|
||||||
self::requireValidSecretKey();
|
self::requireValidSecretKey();
|
||||||
// Reserve quota for the outbound provider call. Cached conversion reads return before this point.
|
|
||||||
$this->reserveRateFetchQuota($base, $target, $endpoint, $method);
|
|
||||||
// Send the request
|
// Send the request
|
||||||
$response = match ($method) {
|
$response = match ($method) {
|
||||||
'GET' => self::sendGetRequest($base, $target, $endpoint, $data),
|
'GET' => self::sendGetRequest($base, $target, $endpoint, $data),
|
||||||
@@ -168,7 +167,7 @@ class fxratesapi implements fxratesapi_i
|
|||||||
function requireDailyLimitNotExceeded(): void
|
function requireDailyLimitNotExceeded(): void
|
||||||
{
|
{
|
||||||
// Check if the daily limit is exceeded
|
// Check if the daily limit is exceeded
|
||||||
if ($this->getDailyRequestCounter() >= (int)$this->config->daily_limit->getVariableValue()) {
|
if ($this->getDailyRequestCounter() >= $this->config->daily_limit->getVariableValue()) {
|
||||||
throw new Exception('Daily limit exceeded');
|
throw new Exception('Daily limit exceeded');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,30 +177,12 @@ class fxratesapi implements fxratesapi_i
|
|||||||
*/
|
*/
|
||||||
function getDailyRequestCounter(): int
|
function getDailyRequestCounter(): int
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
return (new module_usage_service())->currentUsedQuantity('fxratesapi', 'rate_fetch_calls');
|
|
||||||
} catch (Throwable) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count the rows from the fxratesapi request log that was made today
|
// Count the rows from the fxratesapi request log that was made today
|
||||||
$fxratesapi_lookups = new fxratesapi_conversion_rates_o();
|
$fxratesapi_lookups = new fxratesapi_conversion_rates_o();
|
||||||
$fxratesapi_lookups->getTodayCount();
|
$fxratesapi_lookups->getTodayCount();
|
||||||
return $fxratesapi_lookups->getTodayCount();
|
return $fxratesapi_lookups->getTodayCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function reserveRateFetchQuota(string $base, string $target, string $endpoint, string $method): void
|
|
||||||
{
|
|
||||||
(new module_usage_service())->reserveOrFail('fxratesapi', 'rate_fetch_calls', 1, [
|
|
||||||
'base' => $base,
|
|
||||||
'target' => $target,
|
|
||||||
'endpoint' => $endpoint,
|
|
||||||
'method' => strtoupper($method),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@@ -534,4 +515,4 @@ class fxratesapi implements fxratesapi_i
|
|||||||
throw new Exception('Failed to add request to log');
|
throw new Exception('Failed to add request to log');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,617 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace classes;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use objects\collected_order_invoices_o;
|
|
||||||
use objects\logs_o;
|
|
||||||
use objects\order_items_o;
|
|
||||||
use objects\orders_o;
|
|
||||||
use objects\products_o;
|
|
||||||
use objects\users_o;
|
|
||||||
|
|
||||||
class invoice_collection_bulk_action_service
|
|
||||||
{
|
|
||||||
public const ACTION_CLEAN_CUSTOMER_RULES = 'remove_customer_rule_violations';
|
|
||||||
public const ACTION_MERGE = 'merge_collections';
|
|
||||||
public const ACTION_SPLIT_BY_MONTH = 'split_by_month';
|
|
||||||
public const ACTION_RESET_HIDDEN_PRICES = 'reset_hidden_item_prices';
|
|
||||||
public const ACTION_QUEUE_ECONOMIC = 'queue_economic';
|
|
||||||
|
|
||||||
private const PREVIEW_TTL_SECONDS = 600;
|
|
||||||
private const MAX_COLLECTIONS = 100;
|
|
||||||
private const CONFIRMATION_PHRASES = [
|
|
||||||
'da' => 'Bekræft',
|
|
||||||
'en' => 'Confirm',
|
|
||||||
'sv' => 'Bekräfta',
|
|
||||||
'no' => 'Bekreft',
|
|
||||||
'de' => 'Bestätigen',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function preview(string $action, array $invoiceCollectionIds, array $options = [], string $locale = 'da'): array
|
|
||||||
{
|
|
||||||
$action = $this->normalizeAction($action);
|
|
||||||
$invoiceCollectionIds = $this->normalizeInvoiceCollectionIds($invoiceCollectionIds);
|
|
||||||
$options = $this->normalizeOptions($options);
|
|
||||||
|
|
||||||
$preview = $this->buildPreview($action, $invoiceCollectionIds, $options, $locale);
|
|
||||||
$previewId = $this->previewId();
|
|
||||||
$preview['preview_id'] = $previewId;
|
|
||||||
$preview['selection_hash'] = $this->selectionHash($action, $invoiceCollectionIds, $options);
|
|
||||||
$preview['confirmation_phrase'] = $this->confirmationPhrase($locale);
|
|
||||||
|
|
||||||
$this->cachePreview($previewId, [
|
|
||||||
'action' => $action,
|
|
||||||
'invoice_collection_ids' => $invoiceCollectionIds,
|
|
||||||
'options' => $options,
|
|
||||||
'locale' => $locale,
|
|
||||||
'selection_hash' => $preview['selection_hash'],
|
|
||||||
'preview' => $preview,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $preview;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function apply(
|
|
||||||
string $previewId,
|
|
||||||
string $action,
|
|
||||||
array $invoiceCollectionIds,
|
|
||||||
array $options,
|
|
||||||
string $confirmationText,
|
|
||||||
int $actorUserId,
|
|
||||||
string $locale = 'da'
|
|
||||||
): array {
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
$action = $this->normalizeAction($action);
|
|
||||||
$invoiceCollectionIds = $this->normalizeInvoiceCollectionIds($invoiceCollectionIds);
|
|
||||||
$options = $this->normalizeOptions($options);
|
|
||||||
$cached = $this->getCachedPreview($previewId);
|
|
||||||
$selectionHash = $this->selectionHash($action, $invoiceCollectionIds, $options);
|
|
||||||
|
|
||||||
if (!$cached || ($cached['selection_hash'] ?? '') !== $selectionHash) {
|
|
||||||
throw new Exception('Preview is missing, expired, or no longer matches the selected invoice collections.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$expectedConfirmation = (string)($cached['preview']['confirmation_phrase'] ?? $this->confirmationPhrase($locale));
|
|
||||||
if (trim($confirmationText) !== $expectedConfirmation) {
|
|
||||||
throw new Exception('Confirmation text does not match.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$freshPreview = $this->buildPreview($action, $invoiceCollectionIds, $options, (string)($cached['locale'] ?? $locale));
|
|
||||||
if (!empty($freshPreview['blockers'])) {
|
|
||||||
throw new Exception('Action cannot be applied while blockers are present.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$db->conn()->begin_transaction();
|
|
||||||
try {
|
|
||||||
$result = match ($action) {
|
|
||||||
self::ACTION_CLEAN_CUSTOMER_RULES => $this->applyCleanCustomerRules($freshPreview),
|
|
||||||
self::ACTION_MERGE => $this->applyMerge($freshPreview, $options),
|
|
||||||
self::ACTION_SPLIT_BY_MONTH => $this->applySplitByMonth($freshPreview),
|
|
||||||
self::ACTION_RESET_HIDDEN_PRICES => $this->applyResetHiddenPrices($freshPreview),
|
|
||||||
self::ACTION_QUEUE_ECONOMIC => [
|
|
||||||
'queued_invoice_collection_ids' => $invoiceCollectionIds,
|
|
||||||
'changed_count' => count($invoiceCollectionIds),
|
|
||||||
],
|
|
||||||
default => throw new Exception('Unsupported action'),
|
|
||||||
};
|
|
||||||
(new logs_o())->add(
|
|
||||||
'orderInvoices',
|
|
||||||
'global',
|
|
||||||
1,
|
|
||||||
$actorUserId,
|
|
||||||
'APPLY_COLLECTED_INVOICE_BULK_ACTION',
|
|
||||||
'Applied collected invoice bulk action ' . $action . ' to ' . count($invoiceCollectionIds) . ' invoice collections'
|
|
||||||
);
|
|
||||||
$db->conn()->commit();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$db->conn()->rollback();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->deleteCachedPreview($previewId);
|
|
||||||
|
|
||||||
return [
|
|
||||||
...$freshPreview,
|
|
||||||
'preview' => false,
|
|
||||||
'result' => $result,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildPreview(string $action, array $invoiceCollectionIds, array $options, string $locale): array
|
|
||||||
{
|
|
||||||
$collections = $this->loadCollections($invoiceCollectionIds);
|
|
||||||
$base = [
|
|
||||||
'action' => $action,
|
|
||||||
'preview' => true,
|
|
||||||
'confirmation_phrase' => $this->confirmationPhrase($locale),
|
|
||||||
'invoice_collection_ids' => $invoiceCollectionIds,
|
|
||||||
'collections' => array_map(fn(collected_order_invoices_o $collection): array => $this->collectionSummary($collection), $collections),
|
|
||||||
'warnings' => [],
|
|
||||||
'blockers' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
return match ($action) {
|
|
||||||
self::ACTION_CLEAN_CUSTOMER_RULES => $this->previewCleanCustomerRules($base, $collections),
|
|
||||||
self::ACTION_MERGE => $this->previewMerge($base, $collections, $options),
|
|
||||||
self::ACTION_SPLIT_BY_MONTH => $this->previewSplitByMonth($base, $collections),
|
|
||||||
self::ACTION_RESET_HIDDEN_PRICES => $this->previewResetHiddenPrices($base, $collections),
|
|
||||||
self::ACTION_QUEUE_ECONOMIC => $this->previewQueueEconomic($base, $collections),
|
|
||||||
default => throw new Exception('Unsupported action'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewCleanCustomerRules(array $preview, array $collections): array
|
|
||||||
{
|
|
||||||
$items = [];
|
|
||||||
$blockers = [];
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
$blockers = [...$blockers, ...$this->contentMutationBlockers($collection)];
|
|
||||||
$rows = $this->orderItemRows((int)$collection->id);
|
|
||||||
$violatingItemIds = [];
|
|
||||||
$includedItemIds = [];
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$violation = (new customer_product_rule_service())->firstViolationForOrderItem(
|
|
||||||
(int)$row['order_id'],
|
|
||||||
(int)$row['product_id'],
|
|
||||||
empty($row['related_item_id']) ? null : (int)$row['related_item_id']
|
|
||||||
);
|
|
||||||
if ($violation === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$violatingItemIds[] = (int)$row['order_item_id'];
|
|
||||||
$includedItemIds[] = (int)$row['order_item_id'];
|
|
||||||
$items[] = [
|
|
||||||
'invoice_collection_id' => (int)$collection->id,
|
|
||||||
'order_id' => (int)$row['order_id'],
|
|
||||||
'order_item_id' => (int)$row['order_item_id'],
|
|
||||||
'product_id' => (int)$row['product_id'],
|
|
||||||
'product_name' => (string)$row['product_name'],
|
|
||||||
'rule' => (string)$violation['rule'],
|
|
||||||
'price' => (int)$row['price'],
|
|
||||||
'quantity' => (int)$row['quantity'],
|
|
||||||
'will_soft_delete' => true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$orderItemId = (int)$row['order_item_id'];
|
|
||||||
$relatedItemId = empty($row['related_item_id']) ? null : (int)$row['related_item_id'];
|
|
||||||
if ($relatedItemId === null || !in_array($relatedItemId, $violatingItemIds, true) || in_array($orderItemId, $includedItemIds, true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$includedItemIds[] = $orderItemId;
|
|
||||||
$items[] = [
|
|
||||||
'invoice_collection_id' => (int)$collection->id,
|
|
||||||
'order_id' => (int)$row['order_id'],
|
|
||||||
'order_item_id' => $orderItemId,
|
|
||||||
'product_id' => (int)$row['product_id'],
|
|
||||||
'product_name' => (string)$row['product_name'],
|
|
||||||
'rule' => 'related_to_removed_item',
|
|
||||||
'price' => (int)$row['price'],
|
|
||||||
'quantity' => (int)$row['quantity'],
|
|
||||||
'will_soft_delete' => true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
...$preview,
|
|
||||||
'order_items' => $items,
|
|
||||||
'summary' => [
|
|
||||||
'collections' => count($collections),
|
|
||||||
'order_items' => count($items),
|
|
||||||
'changed_count' => count($items),
|
|
||||||
],
|
|
||||||
'blockers' => $blockers,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewMerge(array $preview, array $collections, array $options): array
|
|
||||||
{
|
|
||||||
$targetId = (int)($options['target_invoice_collection_id'] ?? 0);
|
|
||||||
$blockers = [];
|
|
||||||
if (count($collections) < 2) {
|
|
||||||
$blockers[] = ['code' => 'merge_requires_multiple_collections', 'message' => 'Merge requires at least two invoice collections.'];
|
|
||||||
}
|
|
||||||
if ($targetId < 1 || !in_array($targetId, array_map(static fn($collection): int => (int)$collection->id, $collections), true)) {
|
|
||||||
$blockers[] = ['code' => 'invalid_merge_target', 'message' => 'A selected invoice collection must be chosen as merge target.'];
|
|
||||||
}
|
|
||||||
$customerNumbers = array_values(array_unique(array_map(static fn($collection): int => (int)$collection->customer_number->value(), $collections)));
|
|
||||||
if (count($customerNumbers) !== 1) {
|
|
||||||
$blockers[] = ['code' => 'merge_cross_customer', 'message' => 'Only invoice collections for the same customer can be merged.'];
|
|
||||||
}
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
$blockers = [...$blockers, ...$this->contentMutationBlockers($collection)];
|
|
||||||
}
|
|
||||||
|
|
||||||
$ordersToMove = [];
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
if ((int)$collection->id === $targetId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($collection->getOrderIds() as $orderIdRow) {
|
|
||||||
$ordersToMove[] = [
|
|
||||||
'order_id' => (int)$orderIdRow['id'],
|
|
||||||
'source_invoice_collection_id' => (int)$collection->id,
|
|
||||||
'target_invoice_collection_id' => $targetId,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
...$preview,
|
|
||||||
'target_invoice_collection_id' => $targetId,
|
|
||||||
'orders' => $ordersToMove,
|
|
||||||
'summary' => [
|
|
||||||
'collections' => count($collections),
|
|
||||||
'orders_to_move' => count($ordersToMove),
|
|
||||||
'changed_count' => count($ordersToMove),
|
|
||||||
],
|
|
||||||
'blockers' => $blockers,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewSplitByMonth(array $preview, array $collections): array
|
|
||||||
{
|
|
||||||
$items = [];
|
|
||||||
$changed = [];
|
|
||||||
$skipped = [];
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
try {
|
|
||||||
$item = [
|
|
||||||
'invoice_collection_id' => (int)$collection->id,
|
|
||||||
...$collection->previewSplitByOrderMonth(),
|
|
||||||
];
|
|
||||||
if (($item['status'] ?? '') === 'changed') {
|
|
||||||
$changed[] = $item;
|
|
||||||
} else {
|
|
||||||
$skipped[] = $item;
|
|
||||||
}
|
|
||||||
$items[] = $item;
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$item = [
|
|
||||||
'status' => 'skipped',
|
|
||||||
'invoice_collection_id' => (int)$collection->id,
|
|
||||||
'reason' => 'not_splittable',
|
|
||||||
'message' => $e->getMessage(),
|
|
||||||
];
|
|
||||||
$skipped[] = $item;
|
|
||||||
$items[] = $item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
...$preview,
|
|
||||||
'items' => $items,
|
|
||||||
'changed' => $changed,
|
|
||||||
'skipped' => $skipped,
|
|
||||||
'summary' => [
|
|
||||||
'collections' => count($collections),
|
|
||||||
'changed_count' => count($changed),
|
|
||||||
'skipped_count' => count($skipped),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewResetHiddenPrices(array $preview, array $collections): array
|
|
||||||
{
|
|
||||||
$items = [];
|
|
||||||
$blockers = [];
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
$blockers = [...$blockers, ...$this->contentMutationBlockers($collection)];
|
|
||||||
foreach ($this->orderItemRows((int)$collection->id, true) as $row) {
|
|
||||||
if ((int)$row['include_in_invoice'] !== 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$order = (new orders_o())->select((int)$row['order_id']);
|
|
||||||
$product = (new products_o())->select((int)$row['product_id']);
|
|
||||||
if (!$order->exists() || !$product->exists()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$newPrice = (int)$order->getCustomerProductPrice($product);
|
|
||||||
if ((int)$row['price'] === $newPrice) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$items[] = [
|
|
||||||
'invoice_collection_id' => (int)$collection->id,
|
|
||||||
'order_id' => (int)$row['order_id'],
|
|
||||||
'order_item_id' => (int)$row['order_item_id'],
|
|
||||||
'product_id' => (int)$row['product_id'],
|
|
||||||
'product_name' => (string)$row['product_name'],
|
|
||||||
'current_price' => (int)$row['price'],
|
|
||||||
'new_price' => $newPrice,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
...$preview,
|
|
||||||
'order_items' => $items,
|
|
||||||
'summary' => [
|
|
||||||
'collections' => count($collections),
|
|
||||||
'order_items' => count($items),
|
|
||||||
'changed_count' => count($items),
|
|
||||||
],
|
|
||||||
'blockers' => $blockers,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewQueueEconomic(array $preview, array $collections): array
|
|
||||||
{
|
|
||||||
$blockers = [];
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
if (!empty($collection->booked_invoice_id->value())) {
|
|
||||||
$blockers[] = [
|
|
||||||
'code' => 'collection_booked',
|
|
||||||
'invoice_collection_id' => (int)$collection->id,
|
|
||||||
'message' => 'Invoice collection is already booked.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...$preview,
|
|
||||||
'summary' => [
|
|
||||||
'collections' => count($collections),
|
|
||||||
'changed_count' => count($collections),
|
|
||||||
],
|
|
||||||
'blockers' => $blockers,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function applyCleanCustomerRules(array $preview): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
$itemIds = array_values(array_unique(array_map(static fn(array $item): int => (int)$item['order_item_id'], $preview['order_items'] ?? [])));
|
|
||||||
if ($itemIds === []) {
|
|
||||||
return ['changed_count' => 0, 'order_item_ids' => []];
|
|
||||||
}
|
|
||||||
$ids = implode(',', array_map('intval', $itemIds));
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
$safeNow = $db->escape_string($now);
|
|
||||||
$db->query("UPDATE order_items SET deleted_at = '$safeNow' WHERE deleted_at IS NULL AND id IN ($ids)");
|
|
||||||
$this->touchOrdersForItems($itemIds);
|
|
||||||
$this->touchCollections($preview['invoice_collection_ids'] ?? []);
|
|
||||||
return ['changed_count' => count($itemIds), 'order_item_ids' => $itemIds];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function applyMerge(array $preview, array $options): array
|
|
||||||
{
|
|
||||||
$targetId = (int)($options['target_invoice_collection_id'] ?? 0);
|
|
||||||
$moved = [];
|
|
||||||
foreach ($preview['orders'] ?? [] as $row) {
|
|
||||||
$order = (new orders_o())->select((int)$row['order_id']);
|
|
||||||
if (!$order->exists()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$order->assignToInvoiceCollection($targetId);
|
|
||||||
$moved[] = (int)$order->id;
|
|
||||||
}
|
|
||||||
$this->touchCollections($preview['invoice_collection_ids'] ?? []);
|
|
||||||
return ['changed_count' => count($moved), 'moved_order_ids' => $moved, 'target_invoice_collection_id' => $targetId];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function applySplitByMonth(array $preview): array
|
|
||||||
{
|
|
||||||
$changed = [];
|
|
||||||
$skipped = [];
|
|
||||||
foreach ($preview['items'] ?? [] as $item) {
|
|
||||||
$invoiceCollectionId = (int)($item['invoice_collection_id'] ?? 0);
|
|
||||||
if (($item['status'] ?? '') !== 'changed' || $invoiceCollectionId < 1) {
|
|
||||||
$skipped[] = $item;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$changed[] = (new collected_order_invoices_o())->select($invoiceCollectionId)->splitByOrderMonth();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$skipped[] = [
|
|
||||||
'invoice_collection_id' => $invoiceCollectionId,
|
|
||||||
'status' => 'skipped',
|
|
||||||
'message' => $e->getMessage(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ['changed_count' => count($changed), 'skipped_count' => count($skipped), 'changed' => $changed, 'skipped' => $skipped];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function applyResetHiddenPrices(array $preview): array
|
|
||||||
{
|
|
||||||
$changed = [];
|
|
||||||
foreach ($preview['order_items'] ?? [] as $item) {
|
|
||||||
$orderItem = (new order_items_o())->select((int)$item['order_item_id']);
|
|
||||||
if (!$orderItem->exists()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$orderItem->price->set((int)$item['new_price']);
|
|
||||||
$orderItem->objectChanged();
|
|
||||||
$changed[] = (int)$orderItem->id;
|
|
||||||
}
|
|
||||||
$this->touchCollections($preview['invoice_collection_ids'] ?? []);
|
|
||||||
return ['changed_count' => count($changed), 'order_item_ids' => $changed];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function contentMutationBlockers(collected_order_invoices_o $collection): array
|
|
||||||
{
|
|
||||||
$blockers = [];
|
|
||||||
if (!empty($collection->booked_invoice_id->value())) {
|
|
||||||
$blockers[] = ['code' => 'collection_booked', 'invoice_collection_id' => (int)$collection->id, 'message' => 'Invoice collection is already booked.'];
|
|
||||||
}
|
|
||||||
if (!empty($collection->external_id->value())) {
|
|
||||||
$blockers[] = ['code' => 'collection_exported', 'invoice_collection_id' => (int)$collection->id, 'message' => 'Invoice collection already has an external invoice reference.'];
|
|
||||||
}
|
|
||||||
return $blockers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function orderItemRows(int $invoiceCollectionId, bool $includeHidden = false): array
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
$hiddenCondition = $includeHidden ? '' : 'AND oi.include_in_invoice = 1';
|
|
||||||
$sql = "
|
|
||||||
SELECT
|
|
||||||
oi.id AS order_item_id,
|
|
||||||
oi.order_id,
|
|
||||||
oi.product_id,
|
|
||||||
oi.related_item_id,
|
|
||||||
oi.include_in_invoice,
|
|
||||||
oi.price,
|
|
||||||
oi.quantity,
|
|
||||||
p.name AS product_name
|
|
||||||
FROM order_items oi
|
|
||||||
JOIN orders o ON o.id = oi.order_id
|
|
||||||
JOIN products p ON p.id = oi.product_id
|
|
||||||
WHERE o.invoice_collection_id = {$invoiceCollectionId}
|
|
||||||
AND o.deleted_at IS NULL
|
|
||||||
AND oi.deleted_at IS NULL
|
|
||||||
{$hiddenCondition}
|
|
||||||
ORDER BY o.id ASC, oi.id ASC
|
|
||||||
";
|
|
||||||
$result = $db->query($sql);
|
|
||||||
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function touchOrdersForItems(array $itemIds): void
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
if ($itemIds === []) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$ids = implode(',', array_map('intval', $itemIds));
|
|
||||||
$result = $db->query("SELECT DISTINCT order_id FROM order_items WHERE id IN ($ids)");
|
|
||||||
if (!$result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$order = (new orders_o())->select((int)$row['order_id']);
|
|
||||||
if ($order->exists()) {
|
|
||||||
$order->objectChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function touchCollections(array $invoiceCollectionIds): void
|
|
||||||
{
|
|
||||||
foreach ($invoiceCollectionIds as $invoiceCollectionId) {
|
|
||||||
$collection = (new collected_order_invoices_o())->select((int)$invoiceCollectionId);
|
|
||||||
if ($collection->exists()) {
|
|
||||||
$collection->objectChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function loadCollections(array $invoiceCollectionIds): array
|
|
||||||
{
|
|
||||||
return array_map(static function (int $invoiceCollectionId): collected_order_invoices_o {
|
|
||||||
$collection = (new collected_order_invoices_o())->select($invoiceCollectionId);
|
|
||||||
$collection->requireSelected();
|
|
||||||
return $collection;
|
|
||||||
}, $invoiceCollectionIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function collectionSummary(collected_order_invoices_o $collection): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => (int)$collection->id,
|
|
||||||
'customer_number' => (int)$collection->customer_number->value(),
|
|
||||||
'name' => (string)$collection->name->value(),
|
|
||||||
'created_at' => (string)$collection->created_at->value(),
|
|
||||||
'closed_at' => $collection->closed_at->value(),
|
|
||||||
'booked_invoice_id' => $collection->booked_invoice_id->value(),
|
|
||||||
'external_id' => $collection->external_id->value(),
|
|
||||||
'order_count' => (int)$collection->getOrders(true),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function normalizeAction(string $action): string
|
|
||||||
{
|
|
||||||
$action = trim($action);
|
|
||||||
if (!in_array($action, [
|
|
||||||
self::ACTION_CLEAN_CUSTOMER_RULES,
|
|
||||||
self::ACTION_MERGE,
|
|
||||||
self::ACTION_SPLIT_BY_MONTH,
|
|
||||||
self::ACTION_RESET_HIDDEN_PRICES,
|
|
||||||
self::ACTION_QUEUE_ECONOMIC,
|
|
||||||
], true)) {
|
|
||||||
throw new Exception('Invalid invoice collection bulk action.');
|
|
||||||
}
|
|
||||||
return $action;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function normalizeInvoiceCollectionIds(array $ids): array
|
|
||||||
{
|
|
||||||
$normalized = [];
|
|
||||||
foreach ($ids as $id) {
|
|
||||||
if (is_array($id) || is_object($id) || !is_numeric($id)) {
|
|
||||||
throw new Exception('invoice_collection_ids must contain only positive integer ids.');
|
|
||||||
}
|
|
||||||
$parsed = (int)$id;
|
|
||||||
if ($parsed < 1 || $parsed > 999999999) {
|
|
||||||
throw new Exception('invoice_collection_ids must contain only positive integer ids.');
|
|
||||||
}
|
|
||||||
$normalized[$parsed] = $parsed;
|
|
||||||
}
|
|
||||||
$normalized = array_values($normalized);
|
|
||||||
sort($normalized);
|
|
||||||
if ($normalized === []) {
|
|
||||||
throw new Exception('invoice_collection_ids must contain at least one id.');
|
|
||||||
}
|
|
||||||
if (count($normalized) > self::MAX_COLLECTIONS) {
|
|
||||||
throw new Exception('Too many invoice collections selected.');
|
|
||||||
}
|
|
||||||
return $normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function normalizeOptions(array $options): array
|
|
||||||
{
|
|
||||||
if (isset($options['target_invoice_collection_id'])) {
|
|
||||||
$options['target_invoice_collection_id'] = (int)$options['target_invoice_collection_id'];
|
|
||||||
}
|
|
||||||
ksort($options);
|
|
||||||
return $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function selectionHash(string $action, array $invoiceCollectionIds, array $options): string
|
|
||||||
{
|
|
||||||
return hash('sha256', json_encode([
|
|
||||||
'action' => $action,
|
|
||||||
'invoice_collection_ids' => $invoiceCollectionIds,
|
|
||||||
'options' => $options,
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function confirmationPhrase(string $locale): string
|
|
||||||
{
|
|
||||||
$language = strtolower(substr(trim($locale), 0, 2));
|
|
||||||
return self::CONFIRMATION_PHRASES[$language] ?? self::CONFIRMATION_PHRASES['en'];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewId(): string
|
|
||||||
{
|
|
||||||
return bin2hex(random_bytes(16));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function previewCacheKey(string $previewId): string
|
|
||||||
{
|
|
||||||
return 'collected_invoice_bulk_action_preview:' . preg_replace('/[^a-f0-9]/', '', strtolower($previewId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function cachePreview(string $previewId, array $payload): void
|
|
||||||
{
|
|
||||||
(new redis())->setEx($this->previewCacheKey($previewId), json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), self::PREVIEW_TTL_SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getCachedPreview(string $previewId): ?array
|
|
||||||
{
|
|
||||||
$raw = (new redis())->get($this->previewCacheKey($previewId));
|
|
||||||
if (!$raw) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$decoded = json_decode($raw, true);
|
|
||||||
return is_array($decoded) ? $decoded : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function deleteCachedPreview(string $previewId): void
|
|
||||||
{
|
|
||||||
(new redis())->delete($this->previewCacheKey($previewId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user