diff --git a/README.md b/README.md index 71481f803..12a64b240 100644 --- a/README.md +++ b/README.md @@ -154,17 +154,20 @@ If you are configuring PBS monitoring, you need a separate API token created wit 4. **Assign permissions** * Under `Configuration` → `Access Control`, select `Permissions`. * Click `Add` → `API Token Permission`. - * Path: `/datastore` (This grants access to view datastores and their contents, including backup snapshots). - * API Token: Select the token you created (e.g., "pulse-monitor@pam!pulse"). - * Role: `DatastoreAudit` (Provides read-only access to datastore contents and backups). + * **Path:** `/` (Grant permissions at the root level for broad access needed by Pulse). + * **API Token:** Select the token you created (e.g., "pulse-monitor@pam!pulse"). + * **Role:** `Audit` (This role provides necessary read-only access, including system status and task history). * Ensure `Propagate` is checked. * Click `Add`. - * *(Note: While `DatastoreAudit` on `/datastore` is usually sufficient for backup status, if you still encounter issues fetching task lists, you might need broader permissions like `Audit` on path `/` for the token, although this has shown inconsistent behaviour with API tokens vs user sessions in some PBS versions).* + * + * **Note on Permissions & API Behavior:** While minimal roles like `DatastoreAudit` might seem sufficient, testing (on PBS v3.3.4) revealed specific API behavior with tokens. GET requests to `/tasks` failed with a `400 Bad Request ("value does not match the regex pattern")` if the request included a `Content-Type` header (which some HTTP clients add by default), whereas requests omitting this header succeeded. Pulse now includes a workaround for this client-side. However, using the broader `Audit` role on the root path `/` also ensures Pulse can reliably access all necessary data (datastores, snapshots, task history) for full monitoring functionality. This role is still read-only. + 5. **Update your `server/.env` file** with the PBS `Token ID` (`PBS_TOKEN_ID`) and the `Secret` (`PBS_TOKEN_SECRET`). ### Required Permissions -The `PVEAuditor` role is recommended as it provides the necessary read-only permissions for Pulse to monitor your Proxmox environment: +**Proxmox VE:** +The `PVEAuditor` role is recommended as it provides the necessary read-only permissions for Pulse to monitor your Proxmox VE environment: - `Datastore.Audit` - `Permissions.Read` (implicitly included) - `Pool.Audit` diff --git a/server/index.js b/server/index.js index e2762faa7..ae85c07c5 100644 --- a/server/index.js +++ b/server/index.js @@ -280,12 +280,37 @@ async function initializeAllPbsClients() { httpsAgent: new https.Agent({ rejectUnauthorized: !config.allowSelfSignedCerts }), - headers: { 'Content-Type': 'application/json' } + // REMOVED default headers here + // headers: { 'Content-Type': 'application/json' } }); pbsAxiosInstance.interceptors.request.use(reqConfig => { // Correct PBS format: PBSAPIToken=TOKENID:TOKENSECRET reqConfig.headers.Authorization = `PBSAPIToken=${config.tokenId}:${config.tokenSecret}`; + + // ---> WORKAROUND for PBS API Bug <--- + // PBS API (tested on 3.3.4) incorrectly returns 400 Bad Request ("value does not match regex pattern") + // on GET requests (e.g., /tasks) if a 'Content-Type' header is present when using API Token authentication. + // Therefore, explicitly remove 'Content-Type' for GET requests. + // It seems fine/required for POST/PUT requests, so add it back for non-GET. + // See Bugzilla #6365 for related details. + if (reqConfig.method && reqConfig.method.toLowerCase() !== 'get') { + reqConfig.headers['Content-Type'] = 'application/json'; + } else { + // Ensure Content-Type is removed for GET requests + delete reqConfig.headers['Content-Type']; + } + // ---> END WORKAROUND <--- + + // ---> ADD: Set default Accept header (optional but good practice) + if (!reqConfig.headers['Accept']) { + reqConfig.headers['Accept'] = 'application/json, text/plain, */*'; + } + // ---> END ADD + + // ---> DEBUG: Log outgoing request headers (can be removed later) + // console.log(`DEBUG: Axios Request Headers for ${config.name}:`, JSON.stringify(reqConfig.headers)); // REMOVED + // ---> END DEBUG return reqConfig; }); @@ -1233,6 +1258,9 @@ async function fetchAllPbsTasksForProcessing(pbsClient, nodeName) { errors: 1, // RESTORED }; const paramsToSend = Object.keys(requestParams).length > 0 ? { params: requestParams } : {}; + // ---> DEBUG: Log outgoing request URL and params + // console.log(`DEBUG: Axios GET Request to ${pbsClient.config.name} - URL: ${requestUrl}, ParamsObj: ${JSON.stringify(paramsToSend)}`); // REMOVED + // ---> END DEBUG const response = await pbsClient.client.get(requestUrl, paramsToSend); const allTasks = response.data?.data ?? []; return { tasks: allTasks, error: false };