@@ -2,6 +2,90 @@ const fs = require("node:fs");
22const path = require ( "node:path" ) ;
33const 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