Skip to content

Commit 2e18b98

Browse files
revert: restore CORS proxy for newsfeed and 3rd-party module compatibility
1 parent 5410bd9 commit 2e18b98

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

js/server.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const express = require("express");
66
const helmet = require("helmet");
77
const socketio = require("socket.io");
88
const Log = require("logger");
9-
const { getHtml, getVersion, getEnvVars } = require("#server_functions");
9+
const { getHtml, getVersion, getEnvVars, cors } = require("#server_functions");
1010

1111
const { ipAccessControl } = require(`${__dirname}/ip_access_control`);
1212

@@ -118,6 +118,8 @@ function Server (configObj) {
118118
};
119119
app.get("/config", (req, res) => getConfig(req, res));
120120

121+
app.get("/cors", async (req, res) => await cors(req, res));
122+
121123
app.get("/version", (req, res) => getVersion(req, res));
122124

123125
app.get("/startup", (req, res) => getStartup(req, res));

js/server_functions.js

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,90 @@ const fs = require("node:fs");
22
const path = require("node:path");
33
const Log = require("logger");
44

5+
/**
6+
* A method that forwards HTTP Get-methods to the internet to avoid CORS-errors.
7+
* Example input request url: /cors?sendheaders=header1:value1,header2:value2&expectedheaders=header1,header2&url=http://www.test.com/path?param1=value1
8+
* @param {Request} req - the request
9+
* @param {Response} res - the response
10+
* @returns {Promise<void>} Promise that resolves when response is sent
11+
*/
12+
async function cors (req, res) {
13+
try {
14+
const urlRegEx = "url=(.+?)$";
15+
let url;
16+
17+
const match = new RegExp(urlRegEx, "g").exec(req.url);
18+
if (!match) {
19+
url = `invalid url: ${req.url}`;
20+
Log.error(url);
21+
return res.status(400).send(url);
22+
} else {
23+
url = match[1];
24+
25+
const headersToSend = getHeadersToSend(req.url);
26+
const expectedReceivedHeaders = geExpectedReceivedHeaders(req.url);
27+
28+
Log.log(`cors url: ${url}`);
29+
30+
const response = await fetch(url, { headers: headersToSend });
31+
if (response.ok) {
32+
for (const header of expectedReceivedHeaders) {
33+
const headerValue = response.headers.get(header);
34+
if (header) res.set(header, headerValue);
35+
}
36+
const data = await response.text();
37+
res.send(data);
38+
} else {
39+
throw new Error(`Response status: ${response.status}`);
40+
}
41+
}
42+
} catch (error) {
43+
// Only log errors in non-test environments to keep test output clean
44+
if (process.env.mmTestMode !== "true") {
45+
Log.error(`Error in CORS request: ${error}`);
46+
}
47+
res.status(500).json({ error: error.message });
48+
}
49+
}
50+
51+
/**
52+
* Gets headers and values to attach to the web request.
53+
* @param {string} url - The url containing the headers and values to send.
54+
* @returns {object} An object specifying name and value of the headers.
55+
*/
56+
function getHeadersToSend (url) {
57+
const headersToSend = { "User-Agent": getUserAgent() };
58+
const headersToSendMatch = new RegExp("sendheaders=(.+?)(&|$)", "g").exec(url);
59+
if (headersToSendMatch) {
60+
const headers = headersToSendMatch[1].split(",");
61+
for (const header of headers) {
62+
const keyValue = header.split(":");
63+
if (keyValue.length !== 2) {
64+
throw new Error(`Invalid format for header ${header}`);
65+
}
66+
headersToSend[keyValue[0]] = decodeURIComponent(keyValue[1]);
67+
}
68+
}
69+
return headersToSend;
70+
}
71+
72+
/**
73+
* Gets the headers expected from the response.
74+
* @param {string} url - The url containing the expected headers from the response.
75+
* @returns {string[]} headers - The name of the expected headers.
76+
*/
77+
function geExpectedReceivedHeaders (url) {
78+
const expectedReceivedHeaders = ["Content-Type"];
79+
const expectedReceivedHeadersMatch = new RegExp("expectedheaders=(.+?)(&|$)", "g").exec(url);
80+
if (expectedReceivedHeadersMatch) {
81+
const headers = expectedReceivedHeadersMatch[1].split(",");
82+
for (const header of headers) {
83+
expectedReceivedHeaders.push(header);
84+
}
85+
}
86+
return expectedReceivedHeaders;
87+
}
88+
589
/**
690
* Gets the HTML to display the magic mirror.
791
* @param {Request} req - the request
@@ -89,4 +173,4 @@ function getConfigFilePath () {
89173
return path.resolve(global.configuration_file || `${global.root_path}/config/config.js`);
90174
}
91175

92-
module.exports = { getHtml, getVersion, getEnvVars, getEnvVarsAsObj, getUserAgent, getConfigFilePath };
176+
module.exports = { cors, getHtml, getVersion, getEnvVars, getEnvVarsAsObj, getUserAgent, getConfigFilePath };

0 commit comments

Comments
 (0)