default button name
This commit is contained in:
@@ -9,6 +9,7 @@ VAPID_SUBJECT=mailto:mailto:admin@virtonline.eu # Contact email/URL for push ser
|
||||
# --- Server Configuration ---
|
||||
PORT=3000 # Internal port for the Node.js app
|
||||
SUBSCRIPTIONS_FILE=subscriptions.json # Path inside the container
|
||||
DEFAULT_BUTTON_NAME=game-button # Default button name to use when not specified
|
||||
|
||||
# --- Authentication (Optional) ---
|
||||
# If both USERNAME and PASSWORD are set, Basic Auth will be enabled for:
|
||||
|
||||
11
README.md
11
README.md
@@ -62,6 +62,7 @@ It's designed to be run as a Docker container and integrated with Traefik v3 for
|
||||
* `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.
|
||||
* `DEFAULT_BUTTON_NAME`: (Default: `game-button`) The default button name to use when the `Button-Name` header is not provided in the webhook request.
|
||||
* `BASIC_AUTH_USERNAME`: (Optional) Username for Basic Authentication. If set along with `BASIC_AUTH_PASSWORD`, authentication will be enabled for `/webhook` and `/subscribe`.
|
||||
* `BASIC_AUTH_PASSWORD`: (Optional) Password for Basic Authentication. If set along with `BASIC_AUTH_USERNAME`, authentication will be enabled.
|
||||
* `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`.
|
||||
@@ -150,11 +151,11 @@ In your Flic app or Flic Hub SDK interface:
|
||||
* **Description:** Adds or updates the Web Push subscription associated with a specific Flic button ID.
|
||||
* **Authentication:** Optional Basic Authentication via `Authorization` header if `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` are configured.
|
||||
* **Request Body:** JSON object containing:
|
||||
* `button_id` (string, required): The unique identifier for the Flic button (lowercase recommended, e.g., "game", "lights").
|
||||
* `button_id` (string, optional): The unique identifier for the Flic button (lowercase recommended, e.g., "game-button", "lights-button"). If not provided, the value of `DEFAULT_BUTTON_NAME` environment variable will be used as a fallback.
|
||||
* `subscription` (object, required): The [PushSubscription object](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription) obtained from the browser's Push API.
|
||||
```json
|
||||
{
|
||||
"button_id": "game",
|
||||
"button_id": "game-button", // Optional, defaults to DEFAULT_BUTTON_NAME environment variable
|
||||
"subscription": {
|
||||
"endpoint": "https://your_pwa_push_endpoint...",
|
||||
"expirationTime": null,
|
||||
@@ -167,7 +168,7 @@ In your Flic app or Flic Hub SDK interface:
|
||||
```
|
||||
* **Responses:**
|
||||
* `201 Created`: Subscription saved successfully.
|
||||
* `400 Bad Request`: Missing or invalid `button_id` or `subscription` object in the request body.
|
||||
* `400 Bad Request`: Missing or invalid `subscription` object in the request body.
|
||||
* `401 Unauthorized`: Missing or invalid Basic Authentication credentials (if authentication is enabled).
|
||||
* `500 Internal Server Error`: Failed to save the subscription to the file.
|
||||
|
||||
@@ -177,7 +178,7 @@ In your Flic app or Flic Hub SDK interface:
|
||||
* **URL Parameters:**
|
||||
* `click_type` (required): The type of button press (e.g., `SingleClick`, `DoubleClick`, or `Hold`).
|
||||
* **Required Headers:**
|
||||
* `Button-Name` (required): The identifier of the Flic button (sent by the Flic system).
|
||||
* `Button-Name`: The identifier of the Flic button (sent by the Flic system). If not provided, the value of `DEFAULT_BUTTON_NAME` environment variable will be used as a fallback.
|
||||
* **Optional Headers:**
|
||||
* `Timestamp`: Timestamp of the button event (sent by the Flic system).
|
||||
* `Button-Battery-Level`: The battery level percentage of the button (sent by the Flic system).
|
||||
@@ -202,7 +203,7 @@ In your Flic app or Flic Hub SDK interface:
|
||||
|
||||
Once your service is up and running, you can test the webhook endpoint using curl or any API testing tool. This example assumes Basic Authentication is enabled.
|
||||
|
||||
**Note:** Replace `<username>`, `<password>`, `<your_domain>`, and `<button_name>` with your actual values.
|
||||
**Note:** Replace `<username>`, `<password>`, `<your_domain>`, and `<button_name>` with your actual values. The `Button-Name` header is optional and will default to the value of `DEFAULT_BUTTON_NAME` if not provided.
|
||||
|
||||
```bash
|
||||
# Generate Base64 credentials (run this once)
|
||||
|
||||
23
server.js
23
server.js
@@ -14,6 +14,7 @@ const vapidPublicKey = process.env.VAPID_PUBLIC_KEY;
|
||||
const vapidPrivateKey = process.env.VAPID_PRIVATE_KEY;
|
||||
const vapidSubject = process.env.VAPID_SUBJECT; // mailto: or https:
|
||||
const subscriptionsFilePath = process.env.SUBSCRIPTIONS_FILE || path.join(__dirname, 'subscriptions.json');
|
||||
const defaultButtonName = process.env.DEFAULT_BUTTON_NAME || 'game-button';
|
||||
// Basic Authentication Credentials
|
||||
const basicAuthUsername = process.env.BASIC_AUTH_USERNAME;
|
||||
const basicAuthPassword = process.env.BASIC_AUTH_PASSWORD;
|
||||
@@ -233,20 +234,22 @@ const authenticateBasic = (req, res, next) => {
|
||||
// Subscribe endpoint: Add a new button->subscription mapping
|
||||
// Apply Basic Authentication
|
||||
app.post('/subscribe', authenticateBasic, async (req, res) => {
|
||||
const { button_id, subscription } = req.body;
|
||||
let { button_id, subscription } = req.body;
|
||||
|
||||
logger.debug('All headers received:');
|
||||
Object.keys(req.headers).forEach(headerName => {
|
||||
logger.debug(` ${headerName}: ${req.headers[headerName]}`);
|
||||
});
|
||||
|
||||
// If button_id is not provided, use defaultButtonName from environment variable
|
||||
if (!button_id || typeof button_id !== 'string' || button_id.trim() === '') {
|
||||
button_id = defaultButtonName;
|
||||
logger.info(`No button_id provided, using default button name: ${button_id}`);
|
||||
}
|
||||
|
||||
logger.info(`Received subscription request for button: ${button_id}`);
|
||||
|
||||
// Basic Validation
|
||||
if (!button_id || typeof button_id !== 'string' || button_id.trim() === '') {
|
||||
logger.warn('Subscription Error: Missing or invalid button_id');
|
||||
return res.status(400).json({ message: 'Bad Request: Missing or invalid button_id' });
|
||||
}
|
||||
// Basic Validation - now we only validate subscription since button_id will use default if not provided
|
||||
if (!subscription || typeof subscription !== 'object' || !subscription.endpoint || !subscription.keys || !subscription.keys.p256dh || !subscription.keys.auth) {
|
||||
logger.warn('Subscription Error: Missing or invalid subscription object structure');
|
||||
return res.status(400).json({ message: 'Bad Request: Missing or invalid subscription object' });
|
||||
@@ -273,7 +276,7 @@ app.post('/subscribe', authenticateBasic, async (req, res) => {
|
||||
// Apply Basic Authentication
|
||||
app.get('/webhook/:click_type', authenticateBasic, async (req, res) => {
|
||||
// Get buttonName from Header 'Button-Name' and timestamp from Header 'Timestamp'
|
||||
const buttonName = req.headers['button-name'];
|
||||
const buttonName = req.headers['button-name'] || defaultButtonName;
|
||||
const timestamp = req.headers['timestamp'];
|
||||
// Get click_type from URL path
|
||||
const click_type = req.params.click_type;
|
||||
@@ -291,9 +294,9 @@ app.get('/webhook/:click_type', authenticateBasic, async (req, res) => {
|
||||
logger.info(`Received GET webhook: Button=${buttonName}, Type=${click_type}, Timestamp=${timestamp || 'N/A'}`);
|
||||
|
||||
// Basic validation
|
||||
if (!buttonName || !click_type) {
|
||||
logger.warn(`Webhook Error: Missing Button-Name header or click_type query parameter`);
|
||||
return res.status(400).json({ message: 'Bad Request: Missing Button-Name header or click_type query parameter' });
|
||||
if (!click_type) {
|
||||
logger.warn(`Webhook Error: Missing click_type query parameter`);
|
||||
return res.status(400).json({ message: 'Bad Request: Missing click_type query parameter' });
|
||||
}
|
||||
|
||||
const normalizedButtonName = buttonName.toLowerCase(); // Use lowercase for lookup consistency
|
||||
|
||||
@@ -7,17 +7,20 @@ DefaultDependencies=no
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment="HOME=/root"
|
||||
Environment="APP_PATH=/virt/flic-webhook-webpush"
|
||||
|
||||
ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-flic-webhook-webpush 2>/dev/null || true'
|
||||
ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-flic-webhook-webpush 2>/dev/null || true'
|
||||
ExecStartPre=/usr/bin/env sh -c 'touch ${APP_PATH}/subscriptions.json'
|
||||
|
||||
ExecStart=/usr/bin/env docker run \
|
||||
--rm \
|
||||
--name=virt-flic-webhook-webpush \
|
||||
--log-driver=none \
|
||||
--log-driver=none \
|
||||
--network=traefik \
|
||||
--env-file=/virt/flic-webhook-webpush/.env \
|
||||
--label-file=/virt/flic-webhook-webpush/labels \
|
||||
--mount type=bind,src=/virt/flic-webhook-webpush/subscriptions.json,dst=/app/subscriptions.json \
|
||||
--env-file=${APP_PATH}/.env \
|
||||
--label-file=${APP_PATH}/labels \
|
||||
--mount type=bind,src=${APP_PATH}/subscriptions.json,dst=/app/subscriptions.json \
|
||||
flic-webhook-webpush
|
||||
|
||||
ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-flic-webhook-webpush 2>/dev/null || true'
|
||||
|
||||
Reference in New Issue
Block a user