├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── build.sh ├── config.js ├── package-lock.json ├── package.json ├── readme.md ├── scripts.js ├── src ├── api.js ├── logger.js ├── modules │ ├── mask.js │ └── proxy.js └── proxy │ └── configured-domains.js └── static-test └── index.html /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !src 3 | !config.js 4 | !LICENSE 5 | !package.json 6 | !package-lock.json 7 | !readme.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build node_modules to avoid installing NPM (additinal container size) 2 | 3 | FROM alpine AS stage 4 | 5 | WORKDIR /app 6 | COPY . /app 7 | 8 | RUN apk add --update 'npm<13.0.0' 9 | RUN npm install 10 | 11 | # Build aclual container 12 | 13 | FROM alpine 14 | 15 | COPY . /app 16 | COPY --from=stage /app/node_modules /app/node_modules 17 | WORKDIR /app 18 | 19 | RUN apk add --update 'nodejs<13.0.0' 20 | 21 | EXPOSE 80 22 | 23 | ENV APP__ENV_NAME=prod 24 | CMD node --max-http-header-size=1048576 -r esm src/api.js 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2019 Nikita Savchenko (https://nikita.tk) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Usage: ./build.sh 1.0.9 4 | 5 | HUB=zitros/analytics-saviour 6 | 7 | docker build --rm -t image . 8 | 9 | if [ $# -eq 1 ] 10 | then 11 | docker tag image $HUB:$1 12 | fi 13 | docker tag image $HUB:latest 14 | 15 | docker push $HUB:latest 16 | if [ $# -eq 1 ] 17 | then 18 | docker push $HUB:$1 19 | fi -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const MATCH_EVERYTHING_STRING = '.*'; 2 | const env = process.env.APP__ENV_NAME || "local"; 3 | const isLocal = env === "local" || env === "test"; 4 | const proxyDomain = process.env.APP__PROXY_DOMAIN || ''; 5 | const strippedPath = process.env.APP__STRIPPED_PATH || ''; 6 | const hostsWhitelistRegex = (() => { 7 | try { 8 | return new RegExp(process.env.APP__HOSTS_WHITELIST_REGEX || MATCH_EVERYTHING_STRING); 9 | } catch (e) { 10 | console.error(`APP__HOSTS_WHITELIST_REGEX=${process.env.APP__HOSTS_WHITELIST_REGEX} cannot be converted to RegExp:`, e, '\nUsing the default.'); 11 | return new RegExp(MATCH_EVERYTHING_STRING); 12 | } 13 | })(); 14 | 15 | console.log("Environment variables:"); 16 | console.log(`APP__STRIPPED_PATH=${strippedPath} (a prefix path added to the original host, which will be removed in the proxy request)`); 17 | console.log(`APP__PROXY_DOMAIN=${proxyDomain} (an optional proxy domain which will be used for client-side requests instead of the current domain)`); 18 | console.log(`APP__ENV_NAME=${env} (should not be local nor test in production)`); 19 | console.log(`APP__HOSTS_WHITELIST_REGEX=${hostsWhitelistRegex}${hostsWhitelistRegex.toString() === `/${MATCH_EVERYTHING_STRING}/` ? ' (YAY!! Anyone can use your proxy!)' : ''}`); 20 | 21 | export default { 22 | isLocalEnv: isLocal, 23 | httpPort: process.env.PORT || 80, 24 | strippedPath, 25 | hostsWhitelistRegex: hostsWhitelistRegex, 26 | proxyDomain, // Domain to proxy calls through. Leave it empty to use the requested domain as a proxy domain 27 | proxy: { // Proxy configuration is here 28 | domains: [ // These domains are replaced in any proxied response (including scripts, URLs and redirects) 29 | "adservice.google.com", 30 | "www.google-analytics.com", 31 | "analytics.google.com", 32 | "www.googleadservices.com", 33 | "www.googletagmanager.com", 34 | "google-analytics.bi.owox.com", 35 | "googleads.g.doubleclick.net", 36 | "stats.g.doubleclick.net", 37 | "ampcid.google.com", 38 | "www.google.%", 39 | "www.google.com", 40 | "bat.bing.com", 41 | "static.hotjar.com", 42 | "trackcmp.net", 43 | "connect.facebook.net", 44 | "www.facebook.com", 45 | "rum-static.pingdom.net", 46 | "s.adroll.com", 47 | "d.adroll.com", 48 | "bid.g.doubleclick.net", 49 | "rum-collector-2.pingdom.net", 50 | "script.hotjar.com", 51 | "vars.hotjar.com", 52 | "pixel.advertising.com", 53 | "dsum-sec.casalemedia.com", 54 | "pixel.rubiconproject.com", 55 | "sync.outbrain.com", 56 | "simage2.pubmatic.com", 57 | "trc.taboola.com", 58 | "eb2.3lift.com", 59 | "ads.yahoo.com", 60 | "x.bidswitch.net", 61 | "ib.adnxs.com", 62 | "idsync.rlcdn.com", 63 | "us-u.openx.net", 64 | "cm.g.doubleclick.net" 65 | ], 66 | specialContentReplace: { // Special regex rules for domains 67 | "www.googletagmanager.com": [ 68 | { 69 | regex: /"https\:\/\/s","http:\/\/a","\.adroll\.com/, 70 | replace: ({ host }) => `"https://${ host }/s","http://${ host }/a",".adroll.com` 71 | } 72 | ], 73 | "eb2.3lift.com": [ 74 | { // Because eb2.3lift.com/xuid?mid=_&xuid=_&dongle=_ redirects to "/xuid" which doesn't exists 75 | regex: /^\/xuid/, 76 | replace: "https://eb2.3lift.com/xuid" // eb2.3lift.com is replaced then again with the correct proxy 77 | } 78 | ] 79 | }, 80 | ipOverrides: { // IP override rules for domains 81 | "google-analytics.bi.owox.com": { // Currently, this is useless as owox.com is having problems with overriding IP addresses, even though they state that they support everything from the Google Measurement Protocol. 82 | urlMatch: /\/collect/, 83 | queryParameterName: ["uip", "device.ip"] 84 | }, 85 | "www.google-analytics.com": { 86 | urlMatch: /\/collect/, 87 | queryParameterName: ["uip", "_uip"] 88 | } 89 | }, 90 | maskPaths: [ // Paths which are masked in URLs and redirects in order to avoid firing ad-blocking rules 91 | "/google-analytics", 92 | "/www.google-analytics.com", 93 | "/adsbygoogle", 94 | "/gtag/js", 95 | "/googleads", 96 | "/log_event\\?", 97 | "/r/collect", 98 | "/j/collect", 99 | "/g/collect", // As of Dec 2020, Google Measurement Protocol v2 doesn't yet support location overwriting. 100 | "/collect", 101 | "/pageread/conversion", 102 | "/pagead/conversion", 103 | "/googleads", 104 | "/prum", 105 | "/beacon", 106 | "/pixel", 107 | "/AdServer", 108 | "/ads/", 109 | "/gtm.js", 110 | "openx\\." 111 | ], 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-tag-manager-proxy", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.8", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 10 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 11 | "requires": { 12 | "mime-types": "~2.1.34", 13 | "negotiator": "0.6.3" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 20 | }, 21 | "body-parser": { 22 | "version": "1.20.1", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 24 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 25 | "requires": { 26 | "bytes": "3.1.2", 27 | "content-type": "~1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "2.0.0", 30 | "destroy": "1.2.0", 31 | "http-errors": "2.0.0", 32 | "iconv-lite": "0.4.24", 33 | "on-finished": "2.4.1", 34 | "qs": "6.11.0", 35 | "raw-body": "2.5.1", 36 | "type-is": "~1.6.18", 37 | "unpipe": "1.0.0" 38 | }, 39 | "dependencies": { 40 | "bytes": { 41 | "version": "3.1.2", 42 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 43 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 44 | }, 45 | "depd": { 46 | "version": "2.0.0", 47 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 48 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 49 | }, 50 | "http-errors": { 51 | "version": "2.0.0", 52 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 53 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 54 | "requires": { 55 | "depd": "2.0.0", 56 | "inherits": "2.0.4", 57 | "setprototypeof": "1.2.0", 58 | "statuses": "2.0.1", 59 | "toidentifier": "1.0.1" 60 | } 61 | }, 62 | "inherits": { 63 | "version": "2.0.4", 64 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 65 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 66 | }, 67 | "raw-body": { 68 | "version": "2.5.1", 69 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 70 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 71 | "requires": { 72 | "bytes": "3.1.2", 73 | "http-errors": "2.0.0", 74 | "iconv-lite": "0.4.24", 75 | "unpipe": "1.0.0" 76 | } 77 | }, 78 | "setprototypeof": { 79 | "version": "1.2.0", 80 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 81 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 82 | }, 83 | "statuses": { 84 | "version": "2.0.1", 85 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 86 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 87 | }, 88 | "toidentifier": { 89 | "version": "1.0.1", 90 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 91 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 92 | } 93 | } 94 | }, 95 | "bytes": { 96 | "version": "3.1.0", 97 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 98 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 99 | }, 100 | "call-bind": { 101 | "version": "1.0.2", 102 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 103 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 104 | "requires": { 105 | "function-bind": "^1.1.1", 106 | "get-intrinsic": "^1.0.2" 107 | } 108 | }, 109 | "content-disposition": { 110 | "version": "0.5.4", 111 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 112 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 113 | "requires": { 114 | "safe-buffer": "5.2.1" 115 | } 116 | }, 117 | "content-type": { 118 | "version": "1.0.4", 119 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 120 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 121 | }, 122 | "cookie": { 123 | "version": "0.5.0", 124 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 125 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" 126 | }, 127 | "cookie-signature": { 128 | "version": "1.0.6", 129 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 130 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 131 | }, 132 | "debug": { 133 | "version": "2.6.9", 134 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 135 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 136 | "requires": { 137 | "ms": "2.0.0" 138 | } 139 | }, 140 | "depd": { 141 | "version": "1.1.2", 142 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 143 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 144 | }, 145 | "destroy": { 146 | "version": "1.2.0", 147 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 148 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 149 | }, 150 | "ee-first": { 151 | "version": "1.1.1", 152 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 153 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 154 | }, 155 | "encodeurl": { 156 | "version": "1.0.2", 157 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 158 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 159 | }, 160 | "es6-promise": { 161 | "version": "4.2.8", 162 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 163 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" 164 | }, 165 | "escape-html": { 166 | "version": "1.0.3", 167 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 168 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 169 | }, 170 | "esm": { 171 | "version": "3.2.25", 172 | "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", 173 | "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" 174 | }, 175 | "etag": { 176 | "version": "1.8.1", 177 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 178 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 179 | }, 180 | "express": { 181 | "version": "4.18.2", 182 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 183 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 184 | "requires": { 185 | "accepts": "~1.3.8", 186 | "array-flatten": "1.1.1", 187 | "body-parser": "1.20.1", 188 | "content-disposition": "0.5.4", 189 | "content-type": "~1.0.4", 190 | "cookie": "0.5.0", 191 | "cookie-signature": "1.0.6", 192 | "debug": "2.6.9", 193 | "depd": "2.0.0", 194 | "encodeurl": "~1.0.2", 195 | "escape-html": "~1.0.3", 196 | "etag": "~1.8.1", 197 | "finalhandler": "1.2.0", 198 | "fresh": "0.5.2", 199 | "http-errors": "2.0.0", 200 | "merge-descriptors": "1.0.1", 201 | "methods": "~1.1.2", 202 | "on-finished": "2.4.1", 203 | "parseurl": "~1.3.3", 204 | "path-to-regexp": "0.1.7", 205 | "proxy-addr": "~2.0.7", 206 | "qs": "6.11.0", 207 | "range-parser": "~1.2.1", 208 | "safe-buffer": "5.2.1", 209 | "send": "0.18.0", 210 | "serve-static": "1.15.0", 211 | "setprototypeof": "1.2.0", 212 | "statuses": "2.0.1", 213 | "type-is": "~1.6.18", 214 | "utils-merge": "1.0.1", 215 | "vary": "~1.1.2" 216 | }, 217 | "dependencies": { 218 | "depd": { 219 | "version": "2.0.0", 220 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 221 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 222 | }, 223 | "http-errors": { 224 | "version": "2.0.0", 225 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 226 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 227 | "requires": { 228 | "depd": "2.0.0", 229 | "inherits": "2.0.4", 230 | "setprototypeof": "1.2.0", 231 | "statuses": "2.0.1", 232 | "toidentifier": "1.0.1" 233 | } 234 | }, 235 | "inherits": { 236 | "version": "2.0.4", 237 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 238 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 239 | }, 240 | "setprototypeof": { 241 | "version": "1.2.0", 242 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 243 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 244 | }, 245 | "statuses": { 246 | "version": "2.0.1", 247 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 248 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 249 | }, 250 | "toidentifier": { 251 | "version": "1.0.1", 252 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 253 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 254 | } 255 | } 256 | }, 257 | "express-http-proxy": { 258 | "version": "1.6.0", 259 | "resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-1.6.0.tgz", 260 | "integrity": "sha512-7Re6Lepg96NA2wiv7DC5csChAScn4K76/UgYnC71XiITCT1cgGTJUGK6GS0pIixudg3Fbx3Q6mmEW3mZv5tHFQ==", 261 | "requires": { 262 | "debug": "^3.0.1", 263 | "es6-promise": "^4.1.1", 264 | "raw-body": "^2.3.0" 265 | }, 266 | "dependencies": { 267 | "debug": { 268 | "version": "3.2.6", 269 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 270 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 271 | "requires": { 272 | "ms": "^2.1.1" 273 | } 274 | }, 275 | "ms": { 276 | "version": "2.1.2", 277 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 278 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 279 | } 280 | } 281 | }, 282 | "finalhandler": { 283 | "version": "1.2.0", 284 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 285 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 286 | "requires": { 287 | "debug": "2.6.9", 288 | "encodeurl": "~1.0.2", 289 | "escape-html": "~1.0.3", 290 | "on-finished": "2.4.1", 291 | "parseurl": "~1.3.3", 292 | "statuses": "2.0.1", 293 | "unpipe": "~1.0.0" 294 | }, 295 | "dependencies": { 296 | "statuses": { 297 | "version": "2.0.1", 298 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 299 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 300 | } 301 | } 302 | }, 303 | "forwarded": { 304 | "version": "0.2.0", 305 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 306 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 307 | }, 308 | "fresh": { 309 | "version": "0.5.2", 310 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 311 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 312 | }, 313 | "function-bind": { 314 | "version": "1.1.1", 315 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 316 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 317 | }, 318 | "get-intrinsic": { 319 | "version": "1.1.3", 320 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 321 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 322 | "requires": { 323 | "function-bind": "^1.1.1", 324 | "has": "^1.0.3", 325 | "has-symbols": "^1.0.3" 326 | } 327 | }, 328 | "has": { 329 | "version": "1.0.3", 330 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 331 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 332 | "requires": { 333 | "function-bind": "^1.1.1" 334 | } 335 | }, 336 | "has-symbols": { 337 | "version": "1.0.3", 338 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 339 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 340 | }, 341 | "http-errors": { 342 | "version": "1.7.2", 343 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 344 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 345 | "requires": { 346 | "depd": "~1.1.2", 347 | "inherits": "2.0.3", 348 | "setprototypeof": "1.1.1", 349 | "statuses": ">= 1.5.0 < 2", 350 | "toidentifier": "1.0.0" 351 | } 352 | }, 353 | "iconv-lite": { 354 | "version": "0.4.24", 355 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 356 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 357 | "requires": { 358 | "safer-buffer": ">= 2.1.2 < 3" 359 | } 360 | }, 361 | "inherits": { 362 | "version": "2.0.3", 363 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 364 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 365 | }, 366 | "ipaddr.js": { 367 | "version": "1.9.1", 368 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 369 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 370 | }, 371 | "media-typer": { 372 | "version": "0.3.0", 373 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 374 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 375 | }, 376 | "merge-descriptors": { 377 | "version": "1.0.1", 378 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 379 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 380 | }, 381 | "methods": { 382 | "version": "1.1.2", 383 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 384 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 385 | }, 386 | "mime": { 387 | "version": "1.6.0", 388 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 389 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 390 | }, 391 | "mime-db": { 392 | "version": "1.52.0", 393 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 394 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 395 | }, 396 | "mime-types": { 397 | "version": "2.1.35", 398 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 399 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 400 | "requires": { 401 | "mime-db": "1.52.0" 402 | } 403 | }, 404 | "ms": { 405 | "version": "2.0.0", 406 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 407 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 408 | }, 409 | "negotiator": { 410 | "version": "0.6.3", 411 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 412 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 413 | }, 414 | "object-inspect": { 415 | "version": "1.12.2", 416 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 417 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" 418 | }, 419 | "on-finished": { 420 | "version": "2.4.1", 421 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 422 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 423 | "requires": { 424 | "ee-first": "1.1.1" 425 | } 426 | }, 427 | "parseurl": { 428 | "version": "1.3.3", 429 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 430 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 431 | }, 432 | "path-to-regexp": { 433 | "version": "0.1.7", 434 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 435 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 436 | }, 437 | "proxy-addr": { 438 | "version": "2.0.7", 439 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 440 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 441 | "requires": { 442 | "forwarded": "0.2.0", 443 | "ipaddr.js": "1.9.1" 444 | } 445 | }, 446 | "qs": { 447 | "version": "6.11.0", 448 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 449 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 450 | "requires": { 451 | "side-channel": "^1.0.4" 452 | } 453 | }, 454 | "range-parser": { 455 | "version": "1.2.1", 456 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 457 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 458 | }, 459 | "raw-body": { 460 | "version": "2.4.0", 461 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 462 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 463 | "requires": { 464 | "bytes": "3.1.0", 465 | "http-errors": "1.7.2", 466 | "iconv-lite": "0.4.24", 467 | "unpipe": "1.0.0" 468 | } 469 | }, 470 | "safe-buffer": { 471 | "version": "5.2.1", 472 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 473 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 474 | }, 475 | "safer-buffer": { 476 | "version": "2.1.2", 477 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 478 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 479 | }, 480 | "send": { 481 | "version": "0.18.0", 482 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 483 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 484 | "requires": { 485 | "debug": "2.6.9", 486 | "depd": "2.0.0", 487 | "destroy": "1.2.0", 488 | "encodeurl": "~1.0.2", 489 | "escape-html": "~1.0.3", 490 | "etag": "~1.8.1", 491 | "fresh": "0.5.2", 492 | "http-errors": "2.0.0", 493 | "mime": "1.6.0", 494 | "ms": "2.1.3", 495 | "on-finished": "2.4.1", 496 | "range-parser": "~1.2.1", 497 | "statuses": "2.0.1" 498 | }, 499 | "dependencies": { 500 | "depd": { 501 | "version": "2.0.0", 502 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 503 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 504 | }, 505 | "http-errors": { 506 | "version": "2.0.0", 507 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 508 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 509 | "requires": { 510 | "depd": "2.0.0", 511 | "inherits": "2.0.4", 512 | "setprototypeof": "1.2.0", 513 | "statuses": "2.0.1", 514 | "toidentifier": "1.0.1" 515 | } 516 | }, 517 | "inherits": { 518 | "version": "2.0.4", 519 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 520 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 521 | }, 522 | "ms": { 523 | "version": "2.1.3", 524 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 525 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 526 | }, 527 | "setprototypeof": { 528 | "version": "1.2.0", 529 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 530 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 531 | }, 532 | "statuses": { 533 | "version": "2.0.1", 534 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 535 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 536 | }, 537 | "toidentifier": { 538 | "version": "1.0.1", 539 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 540 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 541 | } 542 | } 543 | }, 544 | "serve-static": { 545 | "version": "1.15.0", 546 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 547 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 548 | "requires": { 549 | "encodeurl": "~1.0.2", 550 | "escape-html": "~1.0.3", 551 | "parseurl": "~1.3.3", 552 | "send": "0.18.0" 553 | } 554 | }, 555 | "setprototypeof": { 556 | "version": "1.1.1", 557 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 558 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 559 | }, 560 | "side-channel": { 561 | "version": "1.0.4", 562 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 563 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 564 | "requires": { 565 | "call-bind": "^1.0.0", 566 | "get-intrinsic": "^1.0.2", 567 | "object-inspect": "^1.9.0" 568 | } 569 | }, 570 | "statuses": { 571 | "version": "1.5.0", 572 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 573 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 574 | }, 575 | "toidentifier": { 576 | "version": "1.0.0", 577 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 578 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 579 | }, 580 | "type-is": { 581 | "version": "1.6.18", 582 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 583 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 584 | "requires": { 585 | "media-typer": "0.3.0", 586 | "mime-types": "~2.1.24" 587 | } 588 | }, 589 | "unpipe": { 590 | "version": "1.0.0", 591 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 592 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 593 | }, 594 | "utils-merge": { 595 | "version": "1.0.1", 596 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 597 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 598 | }, 599 | "vary": { 600 | "version": "1.1.2", 601 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 602 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 603 | } 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-tag-manager-proxy", 3 | "version": "1.0.0", 4 | "description": "Proxying request to google analytics and tag manager", 5 | "module": "src/api.js", 6 | "scripts": { 7 | "test": "exit 0", 8 | "start": "node -r esm src/api.js", 9 | "mask": "node -r esm scripts.js mask", 10 | "unmask": "node -r esm scripts.js unmask" 11 | }, 12 | "keywords": [ 13 | "proxy" 14 | ], 15 | "author": "nikita.tk", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/ZitRos/save-analytics-from-content-blockers.git" 19 | }, 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/ZitRos/save-analytics-from-content-blockers/issues" 23 | }, 24 | "homepage": "https://github.com/ZitRos/save-analytics-from-content-blockers#readme", 25 | "dependencies": { 26 | "esm": "^3.2.7", 27 | "express": "^4.18.2", 28 | "express-http-proxy": "^1.5.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > ℹ️ This repository is no longer actively maintained, as focus has shifted to the SaaS version at [dataunlocker.com](https://www.dataunlocker.com). 2 | > However, contributions are still welcome and appreciated — we'll review them as time allows. 3 | 4 | # Google Tag Manager (Google Analytics) Proxy 5 | 6 | A proxy back end for Google Analytics / Google Tag Manager, which allows to avoid ad-blockers blocking client-side analytics tools. 7 | 8 | ``` 9 | docker pull zitros/analytics-saviour 10 | ``` 11 | 12 | **Note**: this repository is an open-sourced alternative (former PoC) of [**dataunlocker.com**](https://dataunlocker.com/). [DataUnlocker](https://dataunlocker.com/) is a fully-managed solution (SaaS) for fixing ad blockers' impact on the client-side analytics tools such as Google Analytics, Google Tag Manager, Segment, Facebook Pixel and any other client-side tools, also without code changes in your web application. [DataUnlocker](https://dataunlocker.com/) uses a very different (and a better) approach to what this repository offers. 13 | 14 | Originally introduced as an example in the article ["How to prevent your analytics data from being blocked by ad blockers"](https://www.freecodecamp.org/news/save-your-analytics-from-content-blockers-7ee08c6ec7ee/), this open-sourced application is now a complete stand-alone solution for Google Tag Manager and Google Analytics. 15 | 16 | ## How It Works 17 | 18 | Google Tag Manager (or plain Google Analytics) is a set of scripts used on the **front end** to track user actions (button clicks, page hits, device analytics, etc). Google's out-of-the-box solution works well, however, almost all ad-blocking software block Google tag manager / Google analytics by default. Hence, companies that are just on their start may loose a big 19 | portion of valuable information about their customers - how to they use the product? What do they like/dislike? Where do they stuck? And so on - an individual precision in analytics is crucial to understand the behavior of users. 20 | 21 | In order to solve ad-blocking issues, we have to introduce **a proxy which will forward front-end requests to Google domain through our own domain**. Also, we have to modify Google Tag Manager scripts "on-the-fly" to request our own domains instead of Google's ones, because all ad-blocking software block requests to domains (and some particular URLs!) which they have in their filters. Furthermore, some requests require additional data modifications which can't be done using standard proxying. 22 | 23 | The next diagram demonstrates the problem with Google Tag Manager / Google Analytics being blocked by ad blockers. 24 | 25 |
26 |
27 |
39 |
40 |