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();
};