Files
flic-webhook-webpush/README.md
2025-03-26 19:12:43 +01:00

12 KiB

Flic to PWA WebPush Backend

This project provides a self-hosted backend service that listens for HTTP requests from Flic smart buttons and triggers Web Push notifications to specific Progressive Web App (PWA) instances. The goal is to allow a Flic button press (Single Click, Double Click, Hold) to trigger actions within the PWA via push messages handled by a Service Worker.

It's designed to be run as a Docker container and integrated with Traefik v3 for SSL termination and routing.

Features

  • Receives POST requests on /flic-webhook.
  • Parses button_id and click_type from the Flic request body.
  • Looks up the target PWA push subscription based on button_id in a JSON file.
  • Sends a Web Push notification containing the click details (action, button, timestamp) to the corresponding PWA subscription.
  • Integrates with Traefik v3 via Docker labels.
  • Configurable via environment variables (.env file).
  • Optional bearer token authentication for securing the Flic webhook endpoint.
  • CORS configuration for allowing requests (needed if your PWA management interface interacts with this service, although not strictly necessary for the Flic->Backend->PWA push flow itself).

Prerequisites

  • Docker: Install Docker
  • Traefik: A running Traefik v3 instance configured with SSL (Let's Encrypt recommended) and connected to a Docker network named traefik. You need to know your certificate resolver name.
  • Domain Name: A domain or subdomain pointing to your Traefik instance (e.g., webpush.virtonline.eu). This will be used for the webhook URL.
  • Flic Hub/Service: Configured to send HTTP requests for button actions. You'll need the serial number(s) of your Flic button(s).
  • Node.js & npm/npx (Optional): Needed only locally to generate VAPID keys easily. Not required for running the container.
  • PWA Push Subscription Details: You need to obtain the Push Subscription object (containing endpoint, keys.p256dh, keys.auth) from your PWA after the user grants notification permission.

Project Structure

Setup

  1. Clone the Repository:

    git clone https://gitea.virtonline.eu/2HoursProject/flic-webhook-webpush.git
    cd flic-webhook-webpush
    
  2. Generate VAPID Keys: Web Push requires VAPID keys for security. Generate them once and store them into .env. You can use npx:

    npx web-push generate-vapid-keys
    

    This will output a Public Key and a Private Key.

  3. Obtain PWA Push Subscription Details:

    • Your PWA needs to use the Push API to request notification permission from the user.
    • When permission is granted, the browser's push service provides a PushSubscription object.
    • This object typically looks like:
      {
        "endpoint": "https://updates.push.services.mozilla.com/...",
        "expirationTime": null,
        "keys": {
          "p256dh": "...",
          "auth": "..."
        }
      }
      
    • You need to get this JSON object from your PWA (e.g., display it to the user to copy, send it to a setup endpoint - though that's more complex).
  4. Configure Environment Variables:

    • Copy the example .env file:
      cp .env.example .env
      
    • Edit the .env file with your specific values:
      • VAPID_PUBLIC_KEY: The public key generated in step 2. Your PWA will also need this key when it subscribes to push notifications.
      • VAPID_PRIVATE_KEY: The private key generated in step 2. Keep this secret!
      • VAPID_SUBJECT: A mailto: or https: URL identifying you or your application (e.g., mailto:admin@yourdomain.com). Used by push services to contact you.
      • PORT: (Default: 3000) The internal port the Node.js app listens on. Traefik will map to this.
      • SUBSCRIPTIONS_FILE: (Default: /app/subscriptions.json) The path inside the container where the button-to-subscription mapping is stored.
      • FLIC_SECRET: (Optional) Set a strong, random secret string if you want to secure the webhook endpoint using Bearer token authentication. Generate with openssl rand -hex 32 or a password manager.
      • ALLOWED_ORIGINS: Comma-separated list of domains allowed by CORS. Include your PWA's domain if it needs to interact directly (e.g., for setup). Example: https://my-pwa.com.
      • ALLOWED_METHODS: (Default: POST,OPTIONS) Standard methods needed.
      • ALLOWED_HEADERS: (Default: Content-Type,Authorization) Standard headers needed.
      • TRAEFIK_SERVICE_HOST: Your public domain for this service (e.g., webpush.virtonline.eu).
      • TRAEFIK_CERT_RESOLVER: The name of your TLS certificate resolver configured in Traefik (e.g., le, myresolver).
  5. Configure Traefik Labels:

    • Copy the example labels file:
      cp labels.example labels
      
    • Important: Edit the labels file. Replace ${TRAEFIK_SERVICE_HOST}, ${TRAEFIK_CERT_RESOLVER}, and ${PORT} with the actual values from your .env file, as docker run does not substitute variables in label files.
      • Example replacement: Host(\${TRAEFIK_SERVICE_HOST}`)becomesHost(webpush.virtonline.eu)`.
      • traefik.http.routers.flic-webhook.tls.certresolver=${TRAEFIK_CERT_RESOLVER} becomes traefik.http.routers.flic-webhook.tls.certresolver=myresolver.
      • traefik.http.services.flic-webhook.loadbalancer.server.port=${PORT} becomes traefik.http.services.flic-webhook.loadbalancer.server.port=3000.
  6. Prepare Subscription Mapping File:

    • Create the subscriptions.json file (or edit the template provided).
    • Add entries mapping your Flic button's serial number (as a lowercase string key) to the PWA PushSubscription object obtained in step 3.
      {
        "80:e4:da:70:xx:xx:xx:xx": { // <-- Replace with your actual Flic Button Serial (lowercase recommended)
          "endpoint": "https://your_pwa_push_endpoint...",
          "expirationTime": null,
          "keys": {
            "p256dh": "YOUR_PWA_SUBSCRIPTION_P256DH_KEY",
            "auth": "YOUR_PWA_SUBSCRIPTION_AUTH_KEY"
          }
        }
        // Add more entries for other buttons if needed
      }
      
    • Ensure this file contains valid JSON.

Running the Service

  1. Build the Docker Image: Make sure you are in the flic-webhook-webpush directory.

    docker build -t flic-webhook-webpush:latest .
    
  2. Run the Container: This command runs the container in detached mode (-d), names it, connects it to the traefik network, passes environment variables from the .env file, applies the Traefik labels from the labels file, and mounts the subscriptions.json file into the container.

    docker run -d --name flic-webhook-webpush \
      --network traefik \
      --env-file .env \
      --label-file labels \
      --mount type=bind,src=./subscriptions.json,dst=/app/subscriptions.json,readonly \
      flic-webhook-webpush:latest
    
    • --network traefik: Connects to the Traefik network.
    • --env-file .env: Loads configuration from your .env file.
    • --label-file labels: Applies the Traefik routing rules from your edited labels file.
    • --mount ...: Makes your local subscriptions.json available inside the container at /app/subscriptions.json. readonly is recommended as the app only reads it.
    • flic-webhook-webpush:latest: The image built in the previous step.
  3. Check Logs: Monitor the container logs to ensure it started correctly and to see incoming webhook requests or errors.

    docker logs -f flic-webhook-webpush
    

    You should see messages indicating the server started, configuration details, and subscription loading status.

  4. Verify Traefik: Check your Traefik dashboard to ensure the flic-webhook-webpush service and router are discovered and healthy.

Flic Button Configuration

In your Flic app or Flic Hub SDK interface:

  1. Select your Flic button.
  2. Add an "Internet Request" action (or similar HTTP request action) for Single Click, Double Click, and/or Hold events.
  3. URL: https://<YOUR_TRAEFIK_SERVICE_HOST>/flic-webhook (e.g., https://webpush.virtonline.eu/flic-webhook)
  4. Method: POST
  5. Body Type: JSON (or application/json)
  6. Body: Configure the JSON body to include the button's serial number and the click type. Flic usually provides variables for these. The backend expects button_id and click_type. Adapt the keys if needed, or modify server.js to expect different keys (e.g., serialNumber).
    {
      "button_id": "{serialNumber}",
      "click_type": "{clickType}",
      "timestamp": "{timestamp}"
    }
    
    (Verify the exact variable names like {serialNumber}, {clickType}, {timestamp} within your specific Flic interface.)
  7. Headers:
    • Add Content-Type: application/json.
    • (Optional - if FLIC_SECRET is set): Add an Authorization header:
      • Key: Authorization
      • Value: Bearer <YOUR_FLIC_SECRET_VALUE> (Replace <YOUR_FLIC_SECRET_VALUE> with the actual secret from your .env file).

API Endpoint

  • POST /flic-webhook

    • Description: Receives Flic button events.
    • Authentication: Optional Bearer token via Authorization header if FLIC_SECRET is configured.
    • Request Body (JSON):
      {
        "button_id": "SERIAL_NUMBER_OF_FLIC_BUTTON",
        "click_type": "SingleClick | DoubleClick | Hold",
        "timestamp": "ISO_8601_TIMESTAMP_STRING (Optional)"
      }
      
    • Responses:
      • 200 OK: Webhook received, push notification sent successfully.
      • 400 Bad Request: Missing button_id or click_type in the request body.
      • 401 Unauthorized: Missing or invalid Bearer token (if FLIC_SECRET is enabled).
      • 404 Not Found: No subscription found in subscriptions.json for the given button_id.
      • 410 Gone: The push subscription associated with the button was rejected by the push service (likely expired or revoked).
      • 500 Internal Server Error: Failed to send the push notification for other reasons.
  • GET /health (Optional)

    • Description: Simple health check endpoint.
    • Response:
      {
        "status": "UP",
        "timestamp": "ISO_8601_TIMESTAMP_STRING"
      }
      

Troubleshooting

  • Check Backend Logs: docker logs flic-webhook-webpush. Look for errors related to configuration, file access, JSON parsing, authentication, or sending push notifications.
  • Check Traefik Logs: docker logs traefik. Look for routing errors or certificate issues.
  • Verify .env: Ensure all required variables are set correctly, especially VAPID keys and Traefik settings.
  • Verify labels: Double-check that variables were correctly substituted manually and match your .env and Traefik setup.
  • Verify subscriptions.json: Ensure it's valid JSON and the button serial number (key) matches exactly what Flic sends (check backend logs for "Received webhook: Button=..."). Check if the subscription details are correct. Case sensitivity matters for the JSON keys (button serials).
  • Check Flic Configuration: Ensure the URL, Method, Body, and Headers (especially Content-Type and Authorization if used) are correct in the Flic action setup. Use curl or Postman to test the endpoint manually first.
  • PWA Service Worker: Remember that the PWA needs a correctly registered Service Worker to receive and handle the incoming push messages. Ensure the PWA subscribes using the same VAPID_PUBLIC_KEY configured in the backend's .env.