Node/Express service that:
- Receives Appspace outbound webhooks, enriches with Workspace ONE MDM
data (matched by serial), and posts Adaptive Card alerts to Webex.
- Runs a Webex bot in WebSocket mode with two commands:
* `offline [filter]` - lists currently offline / lost / failed
Appspace devices, enriched with per-device MDM facts + console links.
* `restart-offline [filter]` - sends WS1 SoftReset (reboot) to every
currently-offline device that has a WS1 record. Capped at 50 per
invocation with bounded concurrency to protect the WS1 API.
Notes on hardening already applied:
- In-flight promise coalescing in mdm.js and index.js so burst webhook
traffic can't stampede the WS1 token / device-cache refresh or the
Appspace token refresh.
- Structured logger that serializes Error instances (message, stack,
code, axios response.status/data) instead of stringifying to "{}".
- Webex 7439-char message-limit handling: `offline` builds its body
incrementally against a character budget and reports accurate
"N more not shown" truncation.
- Uses string phrases for `framework.hears(...)` so the framework's
`(^| )phrase($| )` wrapper handles group-space @mentions correctly,
and a shared `extractFilterArg()` helper so filter parsing works
identically in DMs and mentioned messages.
Config, Docker, smoke-test profile, and healthcheck included.
Secrets are managed via `.env` (gitignored); see `.env.example`.
Co-authored-by: Cursor <cursoragent@cursor.com>
51 lines
No EOL
2.1 KiB
YAML
51 lines
No EOL
2.1 KiB
YAML
services:
|
|
app-dev:
|
|
build: .
|
|
container_name: appspace-webex-dev
|
|
ports:
|
|
- "1889:3000" # host 1889 → container 3000 (app always listens on 3000 inside)
|
|
env_file: .env.dev
|
|
environment:
|
|
- PORT=3000 # force internal listen port (overrides any PORT in .env.dev for container)
|
|
volumes:
|
|
- .:/app
|
|
- /app/node_modules
|
|
command: npm run dev
|
|
user: root # dev bind mounts often require root for host uid/perms; prod image uses non-root
|
|
restart: unless-stopped
|
|
profiles: ["dev"]
|
|
|
|
app-prod:
|
|
build: .
|
|
container_name: appspace-webex-prod
|
|
ports:
|
|
- "1889:3000" # host 1889 → container 3000 (app always listens on 3000 inside)
|
|
env_file: .env
|
|
environment:
|
|
- PORT=3000 # force internal listen port (overrides any PORT in .env for container)
|
|
restart: unless-stopped
|
|
# No `profiles:` key on this service, so `docker compose up -d` (or `docker compose up -d app-prod`) starts it by default.
|
|
# Use `docker compose --profile dev up -d app-dev` for development (only starts the dev service).
|
|
# Inherits non-root USER node from Dockerfile for security hardening
|
|
# Dockerfile HEALTHCHECK is automatically used by Docker
|
|
|
|
# Smoke test service: starts the prod image with minimal dummy envs
|
|
# (required vars are validated at startup). Used by `npm run docker:smoke`
|
|
# to verify the container builds, starts, and /health responds.
|
|
smoke-test:
|
|
build: .
|
|
container_name: appspace-smoke-test
|
|
environment:
|
|
- PORT=3000
|
|
- NODE_ENV=production
|
|
# Dummy values for required env vars (validation happens early)
|
|
- WEBEX_BOT_TOKEN=smoke-test-bot-token
|
|
- WEBEX_ROOM_ID=smoke-test-room-id
|
|
- APPSPACE_INSTANCE_URL=https://smoke.example.com
|
|
- APPSPACE_SUBJECT_ID=smoke-subject
|
|
- APPSPACE_REFRESH_TOKEN=smoke-refresh-token
|
|
- APPSPACE_API_BASE_URL=https://smoke.example.com
|
|
- SMOKE_TEST=true
|
|
# No ports published; we use docker inspect for the built-in healthcheck status.
|
|
profiles: ["smoke"]
|
|
# Uses the hardened Dockerfile (non-root, healthcheck, etc.) |