diff --git a/CHANGELOG.md b/CHANGELOG.md index 2898eaee..27d00450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ planned for 2025-10-01 Thanks to: @dathbe. +### Added + +- Added configuration option for `User-Agent`, used by calendar & news module (#3255) + ### Changed - [clock] Add CSS to prevent line breaking of sunset/sunrise time display (#3816) diff --git a/js/app.js b/js/app.js index 22797195..5e7989aa 100644 --- a/js/app.js +++ b/js/app.js @@ -9,10 +9,9 @@ const Log = require("logger"); const Server = require(`${__dirname}/server`); const Utils = require(`${__dirname}/utils`); const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`); -const { getEnvVarsAsObj } = require(`${__dirname}/server_functions`); - // used to control fetch timeout for node_helpers const { setGlobalDispatcher, Agent } = require("undici"); +const { getEnvVarsAsObj } = require("#server_functions"); // common timeout value, provide environment override in case const fetch_timeout = process.env.mmFetchTimeout !== undefined ? process.env.mmFetchTimeout : 30000; diff --git a/js/server.js b/js/server.js index a15c811d..95fd2b82 100644 --- a/js/server.js +++ b/js/server.js @@ -7,7 +7,7 @@ const ipfilter = require("express-ipfilter").IpFilter; const helmet = require("helmet"); const socketio = require("socket.io"); const Log = require("logger"); -const { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars } = require("./server_functions"); +const { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars } = require("#server_functions"); const vendor = require(`${__dirname}/vendor`); diff --git a/js/server_functions.js b/js/server_functions.js index 07c6df53..1f206ccd 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -69,7 +69,7 @@ async function cors (req, res) { * @returns {object} An object specifying name and value of the headers. */ function getHeadersToSend (url) { - const headersToSend = { "User-Agent": `Mozilla/5.0 MagicMirror/${global.version}` }; + const headersToSend = { "User-Agent": getUserAgent() }; const headersToSendMatch = new RegExp("sendheaders=(.+?)(&|$)", "g").exec(url); if (headersToSendMatch) { const headers = headersToSendMatch[1].split(","); @@ -129,6 +129,27 @@ function getVersion (req, res) { res.send(global.version); } +/** + * Gets the preferred `User-Agent` + * @returns {string} `User-Agent` to be used + */ +function getUserAgent () { + const defaultUserAgent = `Mozilla/5.0 (Node.js ${Number(process.version.match(/^v(\d+\.\d+)/)[1])}) MagicMirror/${global.version}`; + + if (typeof config === "undefined") { + return defaultUserAgent; + } + + switch (typeof config.userAgent) { + case "function": + return config.userAgent(); + case "string": + return config.userAgent; + default: + return defaultUserAgent; + } +} + /** * Gets environment variables needed in the browser. * @returns {object} environment variables key: values @@ -155,4 +176,4 @@ function getEnvVars (req, res) { res.send(obj); } -module.exports = { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars, getEnvVarsAsObj }; +module.exports = { cors, getConfig, getHtml, getVersion, getStartup, getEnvVars, getEnvVarsAsObj, getUserAgent }; diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index b7499b0f..e9af5a16 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -3,6 +3,7 @@ const ical = require("node-ical"); const Log = require("logger"); const NodeHelper = require("node_helper"); const CalendarFetcherUtils = require("./calendarfetcherutils"); +const { getUserAgent } = require("#server_functions"); const { scheduleTimer } = require("#module_functions"); /** @@ -30,10 +31,9 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn const fetchCalendar = () => { clearTimeout(reloadTimer); reloadTimer = null; - const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); let httpsAgent = null; let headers = { - "User-Agent": `Mozilla/5.0 (Node.js ${nodeVersion}) MagicMirror/${global.version}` + "User-Agent": getUserAgent() }; if (selfSignedCert) { diff --git a/modules/default/newsfeed/newsfeedfetcher.js b/modules/default/newsfeed/newsfeedfetcher.js index bcebc271..9ddebe52 100644 --- a/modules/default/newsfeed/newsfeedfetcher.js +++ b/modules/default/newsfeed/newsfeedfetcher.js @@ -5,6 +5,7 @@ const iconv = require("iconv-lite"); const { htmlToText } = require("html-to-text"); const Log = require("logger"); const NodeHelper = require("node_helper"); +const { getUserAgent } = require("#server_functions"); const { scheduleTimer } = require("#module_functions"); /** @@ -101,9 +102,8 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings } }); - const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); const headers = { - "User-Agent": `Mozilla/5.0 (Node.js ${nodeVersion}) MagicMirror/${global.version}`, + "User-Agent": getUserAgent(), "Cache-Control": "max-age=0, no-cache, no-store, must-revalidate", Pragma: "no-cache" }; diff --git a/package.json b/package.json index a2e40025..740da796 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,9 @@ "imports": { "#module_functions": { "default": "./js/module_functions.js" + }, + "#server_functions": { + "default": "./js/server_functions.js" } }, "main": "js/electron.js", diff --git a/tests/unit/functions/server_functions_spec.js b/tests/unit/functions/server_functions_spec.js index d394d055..b7b6b06f 100644 --- a/tests/unit/functions/server_functions_spec.js +++ b/tests/unit/functions/server_functions_spec.js @@ -1,4 +1,5 @@ -const { cors } = require("../../../js/server_functions"); +const { expect } = require("playwright/test"); +const { cors, getUserAgent } = require("#server_functions"); describe("server_functions tests", () => { describe("The cors method", () => { @@ -142,5 +143,21 @@ describe("server_functions tests", () => { expect(corsResponse.set.mock.calls[2][0]).toBe("header2"); expect(corsResponse.set.mock.calls[2][1]).toBe("value2"); }); + + it("Gets User-Agent from configuration", async () => { + config = {}; + let userAgent; + + userAgent = getUserAgent(); + expect(userAgent).toContain("Mozilla/5.0 (Node.js "); + + config.userAgent = "Mozilla/5.0 (Foo)"; + userAgent = getUserAgent(); + expect(userAgent).toBe("Mozilla/5.0 (Foo)"); + + config.userAgent = () => "Mozilla/5.0 (Bar)"; + userAgent = getUserAgent(); + expect(userAgent).toBe("Mozilla/5.0 (Bar)"); + }); }); });