diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..40d5f5bb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +docker-compose.yml +Dockerfile +.gitignore +.DS_Store +node_modules diff --git a/Dockerfile b/Dockerfile index a3e31e09..871b8a87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,10 @@ FROM node:12.18-alpine +ARG DATABASE_TYPE + +ENV DATABASE_URL "postgresql://umami:umami@db:5432/umami" \ + DATABASE_TYPE=$DATABASE_TYPE + COPY . /app WORKDIR /app diff --git a/components/metrics/ActiveUsers.js b/components/metrics/ActiveUsers.js index 4d1118fa..10c10155 100644 --- a/components/metrics/ActiveUsers.js +++ b/components/metrics/ActiveUsers.js @@ -38,7 +38,7 @@ export default function ActiveUsers({ websiteId, className }) { {props.x.interpolate(x => x.toFixed(0))} -
{`current vistor${count !== 1 ? 's' : ''}`}
+
{`current visitor${count !== 1 ? 's' : ''}`}
); diff --git a/docker-compose.yml b/docker-compose.yml index b09b81eb..7f1b42ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,25 +1,24 @@ -version: '3.8' +--- +version: '3' services: umami: build: . ports: - "3000:3000" environment: - DATABASE_URL: postgresql://umami:umami@postgres:5432/umami + DATABASE_URL: postgresql://umami:umami@db:5432/umami + DATABASE_TYPE: postgresql HASH_SALT: replace-me-with-a-random-string - postgres: + depends_on: + - db + db: image: postgres:alpine - ports: - - "5432:5432" environment: + POSTGRES_DB: umami POSTGRES_USER: umami POSTGRES_PASSWORD: umami volumes: - - type: bind - source: ./sql/schema.postgresql.sql - target: /docker-entrypoint-initdb.d/schema.postgresql.sql - - type: volume - source: postgres-data - target: /var/lib/postgresql/data + - ./sql/schema.postgresql.sql:/docker-entrypoint-initdb.d/schema.postgresql.sql:ro + - umami-db-data:/var/lib/postgresql/data volumes: - postgres-data: + umami-db-data: diff --git a/lib/web.js b/lib/web.js index b75a6ba7..3cf942ab 100644 --- a/lib/web.js +++ b/lib/web.js @@ -46,3 +46,28 @@ export const hook = (_this, method, callback) => { return orig.apply(_this, args); }; }; + +export const doNotTrack = () => { + if ( + window.doNotTrack || + navigator.doNotTrack || + navigator.msDoNotTrack || + 'msTrackingProtectionEnabled' in window.external + ) { + if ( + window.doNotTrack == '1' || + navigator.doNotTrack == 'yes' || + navigator.doNotTrack == '1' || + navigator.msDoNotTrack == '1' || + (window.external && + window.external.msTrackingProtectionEnabled && + window.external.msTrackingProtectionEnabled()) + ) { + return true; + } else { + return false; + } + } else { + return false; + } +}; diff --git a/pages/share/[...id].js b/pages/share/[...id].js index 15b911d4..21005a53 100644 --- a/pages/share/[...id].js +++ b/pages/share/[...id].js @@ -6,6 +6,7 @@ import NotFound from 'pages/404'; import { get } from 'lib/web'; export default function SharePage() { + const [loading, setLoading] = useState(true); const [websiteId, setWebsiteId] = useState(); const [notFound, setNotFound] = useState(false); const router = useRouter(); @@ -23,10 +24,16 @@ export default function SharePage() { useEffect(() => { if (id) { - loadData(); + loadData().finally(() => { + setLoading(false); + }); + } else { + setLoading(false); } }, [id]); + if (loading) return null; + if (!id || notFound) { return ; } diff --git a/public/umami.js b/public/umami.js index 64a68198..c46c7566 100644 --- a/public/umami.js +++ b/public/umami.js @@ -1 +1 @@ -!function(){"use strict";function e(e){var t=this.constructor;return this.then((function(n){return t.resolve(e()).then((function(){return n}))}),(function(n){return t.resolve(e()).then((function(){return t.reject(n)}))}))}var t=setTimeout;function n(e){return Boolean(e&&void 0!==e.length)}function r(){}function o(e){if(!(this instanceof o))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],f(e,this)}function i(e,t){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,o._immediateFn((function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null!==n){var r;try{r=n(e._value)}catch(e){return void a(t.promise,e)}u(t.promise,r)}else(1===e._state?u:a)(t.promise,e._value)}))):e._deferreds.push(t)}function u(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof o)return e._state=3,e._value=t,void c(e);if("function"==typeof n)return void f((r=n,i=t,function(){r.apply(i,arguments)}),e)}e._state=1,e._value=t,c(e)}catch(t){a(e,t)}var r,i}function a(e,t){e._state=2,e._value=t,c(e)}function c(e){2===e._state&&0===e._deferreds.length&&o._immediateFn((function(){e._handled||o._unhandledRejectionFn(e._value)}));for(var t=0,n=e._deferreds.length;t { const { @@ -13,7 +13,7 @@ import { post, hook } from '../lib/web'; const script = document.querySelector('script[data-website-id]'); - if (!script) return; + if (!script || doNotTrack()) return; const website = script.getAttribute('data-website-id'); const hostUrl = new URL(script.src).origin; @@ -53,10 +53,11 @@ import { post, hook } from '../lib/web'; /* Handle history */ - const handlePush = (state, title, url) => { + const handlePush = (state, title, navaigatedUrl) => { removeEvents(); currentRef = currentUrl; - currentUrl = url; + const url = new URL(navaigatedUrl); + currentUrl = `${url.pathname}${url.search}`; pageView(); };