Merge branch 'dev' into search-formatted-metrics
28
.github/workflows/cd-cloud.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Create docker images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- analytics
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build, push, and deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Generate random hash
|
||||||
|
id: random_hash
|
||||||
|
run: echo "hash=$(openssl rand -hex 4)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
|
name: Build & push Docker image to docker.io
|
||||||
|
with:
|
||||||
|
image: umamisoftware/umami
|
||||||
|
tags: cloud-${{ steps.random_hash.outputs.hash }}, cloud-latest
|
||||||
|
buildArgs: DATABASE_TYPE=postgresql
|
||||||
|
registry: docker.io
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
19
.github/workflows/cd-manual.yml
vendored
@ -20,11 +20,26 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Extract version parts from input
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
echo "version=$(echo ${{ github.event.inputs.version }})" >> $GITHUB_ENV
|
||||||
|
echo "major=$(echo ${{ github.event.inputs.version }} | cut -d. -f1)" >> $GITHUB_ENV
|
||||||
|
echo "minor=$(echo ${{ github.event.inputs.version }} | cut -d. -f2)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate tags
|
||||||
|
id: generate_tags
|
||||||
|
run: |
|
||||||
|
echo "tag_major=$(echo ${{ matrix.db-type }}-${{ env.major }})" >> $GITHUB_ENV
|
||||||
|
echo "tag_minor=$(echo ${{ matrix.db-type }}-${{ env.major }}.${{ env.minor }})" >> $GITHUB_ENV
|
||||||
|
echo "tag_patch=$(echo ${{ matrix.db-type }}-${{ env.version }})" >> $GITHUB_ENV
|
||||||
|
echo "tag_latest=$(echo ${{ matrix.db-type }}-latest)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umami
|
image: umami
|
||||||
tags: ${{ matrix.db-type }}-${{ inputs.version }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
multiPlatform: true
|
multiPlatform: true
|
||||||
@ -36,7 +51,7 @@ jobs:
|
|||||||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umamisoftware/umami
|
image: umamisoftware/umami
|
||||||
tags: ${{ matrix.db-type }}-${{ inputs.version }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
14
.github/workflows/cd.yml
vendored
@ -17,14 +17,21 @@ jobs:
|
|||||||
|
|
||||||
- name: Set env
|
- name: Set env
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
|
||||||
echo "NOW=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
echo "NOW=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Generate tags
|
||||||
|
id: generate_tags
|
||||||
|
run: |
|
||||||
|
echo "tag_patch=$(echo ${{ matrix.db-type }})-${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
echo "tag_minor=$(echo ${{ matrix.db-type }})-$(echo ${GITHUB_REF#refs/tags/} | cut -d. -f1,2)" >> $GITHUB_ENV
|
||||||
|
echo "tag_major=$(echo ${{ matrix.db-type }})-$(echo ${GITHUB_REF#refs/tags/} | cut -d. -f1)" >> $GITHUB_ENV
|
||||||
|
echo "tag_latest=$(echo ${{ matrix.db-type }})-latest" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umami
|
image: umami
|
||||||
tags: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
multiPlatform: true
|
multiPlatform: true
|
||||||
@ -32,12 +39,11 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
||||||
with:
|
with:
|
||||||
image: umamisoftware/umami
|
image: umamisoftware/umami
|
||||||
tags: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
tags: ${{ env.tag_major }}, ${{ env.tag_minor }}, ${{ env.tag_patch }}, ${{ env.tag_latest }}
|
||||||
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
@ -37,7 +37,7 @@ RUN adduser --system --uid 1001 nextjs
|
|||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apk add --no-cache curl \
|
&& apk add --no-cache curl \
|
||||||
&& yarn add npm-run-all dotenv prisma semver
|
&& yarn add npm-run-all dotenv semver prisma@5.17.0
|
||||||
|
|
||||||
# You only need to copy next.config.js if you are NOT using the default configuration
|
# You only need to copy next.config.js if you are NOT using the default configuration
|
||||||
COPY --from=builder /app/next.config.js .
|
COPY --from=builder /app/next.config.js .
|
||||||
|
112
README.md
@ -1,69 +1,93 @@
|
|||||||
# umami
|
<p align="center">
|
||||||
|
<img src="https://content.umami.is/website/images/umami-logo.png" alt="Umami Logo" width="100">
|
||||||
|
</p>
|
||||||
|
|
||||||
Umami is a simple, fast, privacy-focused alternative to Google Analytics.
|
<h1 align="center">Umami</h1>
|
||||||
|
|
||||||
## Getting started
|
<p align="center">
|
||||||
|
<i>Umami is a simple, fast, privacy-focused alternative to Google Analytics.</i>
|
||||||
|
</p>
|
||||||
|
|
||||||
A detailed getting started guide can be found at [https://umami.is/docs/](https://umami.is/docs/)
|
<p align="center">
|
||||||
|
<a href="https://github.com/umami-software/umami/releases">
|
||||||
|
<img src="https://img.shields.io/github/release/umami-software/umami.svg" alt="GitHub Release" />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/umami-software/umami/blob/master/LICENSE">
|
||||||
|
<img src="https://img.shields.io/github/license/umami-software/umami.svg" alt="MIT License" />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/umami-software/umami/actions">
|
||||||
|
<img src="https://img.shields.io/github/actions/workflow/status/umami-software/umami/ci.yml" alt="Build Status" />
|
||||||
|
</a>
|
||||||
|
<a href="https://analytics.umami.is/share/LGazGOecbDtaIwDr/umami.is" style="text-decoration: none;">
|
||||||
|
<img src="https://img.shields.io/badge/Try%20Demo%20Now-Click%20Here-brightgreen" alt="Umami Demo" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Installing from source
|
---
|
||||||
|
|
||||||
|
## 🚀 Getting Started
|
||||||
|
|
||||||
|
A detailed getting started guide can be found at [umami.is/docs](https://umami.is/docs/).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠 Installing from Source
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- A server with Node.js version 16.13 or newer
|
- A server with Node.js version 16.13 or newer
|
||||||
- A database. Umami supports [MySQL](https://www.mysql.com/) and [Postgresql](https://www.postgresql.org/) databases.
|
- A database. Umami supports [MariaDB](https://www.mariadb.org/) (minimum v10.5), [MySQL](https://www.mysql.com/) (minimum v8.0) and [PostgreSQL](https://www.postgresql.org/) (minimum v12.14) databases.
|
||||||
|
|
||||||
### Install Yarn
|
### Install Yarn
|
||||||
|
|
||||||
```
|
```bash
|
||||||
npm install -g yarn
|
npm install -g yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get the source code and install packages
|
### Get the Source Code and Install Packages
|
||||||
|
|
||||||
```
|
```bash
|
||||||
git clone https://github.com/umami-software/umami.git
|
git clone https://github.com/umami-software/umami.git
|
||||||
cd umami
|
cd umami
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configure umami
|
### Configure Umami
|
||||||
|
|
||||||
Create an `.env` file with the following
|
Create an `.env` file with the following:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
DATABASE_URL=connection-url
|
DATABASE_URL=connection-url
|
||||||
```
|
```
|
||||||
|
|
||||||
The connection url is in the following format:
|
The connection URL format:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
postgresql://username:mypassword@localhost:5432/mydb
|
postgresql://username:mypassword@localhost:5432/mydb
|
||||||
|
|
||||||
mysql://username:mypassword@localhost:3306/mydb
|
mysql://username:mypassword@localhost:3306/mydb
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build the application
|
### Build the Application
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn build
|
yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
The build step will also create tables in your database if you are installing for the first time. It will also create a login user with username **admin** and password **umami**.
|
*The build step will create tables in your database if you are installing for the first time. It will also create a login user with username **admin** and password **umami**.*
|
||||||
|
|
||||||
### Start the application
|
### Start the Application
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
By default this will launch the application on `http://localhost:3000`. You will need to either
|
*By default, this will launch the application on `http://localhost:3000`. You will need to either [proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) requests from your web server or change the [port](https://nextjs.org/docs/api-reference/cli#production) to serve the application directly.*
|
||||||
[proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) requests from your web server
|
|
||||||
or change the [port](https://nextjs.org/docs/api-reference/cli#production) to serve the application directly.
|
|
||||||
|
|
||||||
## Installing with Docker
|
---
|
||||||
|
|
||||||
To build the umami container and start up a Postgres database, run:
|
## 🐳 Installing with Docker
|
||||||
|
|
||||||
|
To build the Umami container and start up a Postgres database, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@ -72,16 +96,18 @@ docker compose up -d
|
|||||||
Alternatively, to pull just the Umami Docker image with PostgreSQL support:
|
Alternatively, to pull just the Umami Docker image with PostgreSQL support:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull ghcr.io/umami-software/umami:postgresql-latest
|
docker pull docker.umami.is/umami-software/umami:postgresql-latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Or with MySQL support:
|
Or with MySQL support:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull ghcr.io/umami-software/umami:mysql-latest
|
docker pull docker.umami.is/umami-software/umami:mysql-latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## Getting updates
|
---
|
||||||
|
|
||||||
|
## 🔄 Getting Updates
|
||||||
|
|
||||||
To get the latest features, simply do a pull, install any new dependencies, and rebuild:
|
To get the latest features, simply do a pull, install any new dependencies, and rebuild:
|
||||||
|
|
||||||
@ -98,6 +124,36 @@ docker compose pull
|
|||||||
docker compose up --force-recreate
|
docker compose up --force-recreate
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
---
|
||||||
|
|
||||||
MIT
|
## 🛟 Support
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/umami-software/umami">
|
||||||
|
<img src="https://img.shields.io/badge/GitHub--blue?style=social&logo=github" alt="GitHub" />
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/umami_software">
|
||||||
|
<img src="https://img.shields.io/badge/Twitter--blue?style=social&logo=twitter" alt="Twitter" />
|
||||||
|
</a>
|
||||||
|
<a href="https://linkedin.com/company/umami-software">
|
||||||
|
<img src="https://img.shields.io/badge/LinkedIn--blue?style=social&logo=linkedin" alt="LinkedIn" />
|
||||||
|
</a>
|
||||||
|
<a href="https://umami.is/discord">
|
||||||
|
<img src="https://img.shields.io/badge/Discord--blue?style=social&logo=discord" alt="Discord" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[release-shield]: https://img.shields.io/github/release/umami-software/umami.svg
|
||||||
|
[releases-url]: https://github.com/umami-software/umami/releases
|
||||||
|
[license-shield]: https://img.shields.io/github/license/umami-software/umami.svg
|
||||||
|
[license-url]: https://github.com/umami-software/umami/blob/master/LICENSE
|
||||||
|
[build-shield]: https://img.shields.io/github/actions/workflow/status/umami-software/umami/ci.yml
|
||||||
|
[build-url]: https://github.com/umami-software/umami/actions
|
||||||
|
[github-shield]: https://img.shields.io/badge/GitHub--blue?style=social&logo=github
|
||||||
|
[github-url]: https://github.com/umami-software/umami
|
||||||
|
[twitter-shield]: https://img.shields.io/badge/Twitter--blue?style=social&logo=twitter
|
||||||
|
[twitter-url]: https://twitter.com/umami_software
|
||||||
|
[linkedin-shield]: https://img.shields.io/badge/LinkedIn--blue?style=social&logo=linkedin
|
||||||
|
[linkedin-url]: https://linkedin.com/company/umami-software
|
||||||
|
[discord-shield]: https://img.shields.io/badge/Discord--blue?style=social&logo=discord
|
||||||
|
[discord-url]: https://discord.com/invite/4dz4zcXYrQ
|
||||||
|
77
db/clickhouse/migrations/04_add_tag.sql
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
-- add tag column
|
||||||
|
ALTER TABLE umami.website_event ADD COLUMN "tag" String AFTER "event_name";
|
||||||
|
ALTER TABLE umami.website_event_stats_hourly ADD COLUMN "tag" SimpleAggregateFunction(groupArrayArray, Array(String)) AFTER "max_time";
|
||||||
|
|
||||||
|
-- update materialized view
|
||||||
|
DROP TABLE umami.website_event_stats_hourly_mv;
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv
|
||||||
|
TO umami.website_event_stats_hourly
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
entry_url,
|
||||||
|
exit_url,
|
||||||
|
url_paths as url_path,
|
||||||
|
url_query,
|
||||||
|
referrer_domain,
|
||||||
|
page_title,
|
||||||
|
event_type,
|
||||||
|
event_name,
|
||||||
|
views,
|
||||||
|
min_time,
|
||||||
|
max_time,
|
||||||
|
tag,
|
||||||
|
timestamp as created_at
|
||||||
|
FROM (SELECT
|
||||||
|
website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
argMinState(url_path, created_at) entry_url,
|
||||||
|
argMaxState(url_path, created_at) exit_url,
|
||||||
|
arrayFilter(x -> x != '', groupArray(url_path)) as url_paths,
|
||||||
|
arrayFilter(x -> x != '', groupArray(url_query)) url_query,
|
||||||
|
arrayFilter(x -> x != '', groupArray(referrer_domain)) referrer_domain,
|
||||||
|
arrayFilter(x -> x != '', groupArray(page_title)) page_title,
|
||||||
|
event_type,
|
||||||
|
if(event_type = 2, groupArray(event_name), []) event_name,
|
||||||
|
sumIf(1, event_type = 1) views,
|
||||||
|
min(created_at) min_time,
|
||||||
|
max(created_at) max_time,
|
||||||
|
arrayFilter(x -> x != '', groupArray(tag)) tag,
|
||||||
|
toStartOfHour(created_at) timestamp
|
||||||
|
FROM umami.website_event
|
||||||
|
GROUP BY website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
event_type,
|
||||||
|
timestamp);
|
@ -26,11 +26,14 @@ CREATE TABLE umami.website_event
|
|||||||
--events
|
--events
|
||||||
event_type UInt32,
|
event_type UInt32,
|
||||||
event_name String,
|
event_name String,
|
||||||
|
tag String,
|
||||||
created_at DateTime('UTC'),
|
created_at DateTime('UTC'),
|
||||||
job_id Nullable(UUID)
|
job_id Nullable(UUID)
|
||||||
)
|
)
|
||||||
engine = MergeTree
|
ENGINE = MergeTree
|
||||||
ORDER BY (website_id, session_id, created_at)
|
PARTITION BY toYYYYMM(created_at)
|
||||||
|
ORDER BY (toStartOfHour(created_at), website_id, session_id, visit_id, created_at)
|
||||||
|
PRIMARY KEY (toStartOfHour(created_at), website_id, session_id, visit_id)
|
||||||
SETTINGS index_granularity = 8192;
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
CREATE TABLE umami.event_data
|
CREATE TABLE umami.event_data
|
||||||
@ -48,7 +51,7 @@ CREATE TABLE umami.event_data
|
|||||||
created_at DateTime('UTC'),
|
created_at DateTime('UTC'),
|
||||||
job_id Nullable(UUID)
|
job_id Nullable(UUID)
|
||||||
)
|
)
|
||||||
engine = MergeTree
|
ENGINE = MergeTree
|
||||||
ORDER BY (website_id, event_id, data_key, created_at)
|
ORDER BY (website_id, event_id, data_key, created_at)
|
||||||
SETTINGS index_granularity = 8192;
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
@ -64,6 +67,132 @@ CREATE TABLE umami.session_data
|
|||||||
created_at DateTime('UTC'),
|
created_at DateTime('UTC'),
|
||||||
job_id Nullable(UUID)
|
job_id Nullable(UUID)
|
||||||
)
|
)
|
||||||
engine = MergeTree
|
ENGINE = ReplacingMergeTree
|
||||||
ORDER BY (website_id, session_id, data_key, created_at)
|
ORDER BY (website_id, session_id, data_key)
|
||||||
SETTINGS index_granularity = 8192;
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
|
-- stats hourly
|
||||||
|
CREATE TABLE umami.website_event_stats_hourly
|
||||||
|
(
|
||||||
|
website_id UUID,
|
||||||
|
session_id UUID,
|
||||||
|
visit_id UUID,
|
||||||
|
hostname LowCardinality(String),
|
||||||
|
browser LowCardinality(String),
|
||||||
|
os LowCardinality(String),
|
||||||
|
device LowCardinality(String),
|
||||||
|
screen LowCardinality(String),
|
||||||
|
language LowCardinality(String),
|
||||||
|
country LowCardinality(String),
|
||||||
|
subdivision1 LowCardinality(String),
|
||||||
|
city String,
|
||||||
|
entry_url AggregateFunction(argMin, String, DateTime('UTC')),
|
||||||
|
exit_url AggregateFunction(argMax, String, DateTime('UTC')),
|
||||||
|
url_path SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
|
url_query SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
|
referrer_domain SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
|
page_title SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
|
event_type UInt32,
|
||||||
|
event_name SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
|
views SimpleAggregateFunction(sum, UInt64),
|
||||||
|
min_time SimpleAggregateFunction(min, DateTime('UTC')),
|
||||||
|
max_time SimpleAggregateFunction(max, DateTime('UTC')),
|
||||||
|
tag SimpleAggregateFunction(groupArrayArray, Array(String)),
|
||||||
|
created_at Datetime('UTC')
|
||||||
|
)
|
||||||
|
ENGINE = AggregatingMergeTree
|
||||||
|
PARTITION BY toYYYYMM(created_at)
|
||||||
|
ORDER BY (
|
||||||
|
website_id,
|
||||||
|
event_type,
|
||||||
|
toStartOfHour(created_at),
|
||||||
|
cityHash64(visit_id),
|
||||||
|
visit_id
|
||||||
|
)
|
||||||
|
SAMPLE BY cityHash64(visit_id);
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv
|
||||||
|
TO umami.website_event_stats_hourly
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
entry_url,
|
||||||
|
exit_url,
|
||||||
|
url_paths as url_path,
|
||||||
|
url_query,
|
||||||
|
referrer_domain,
|
||||||
|
page_title,
|
||||||
|
event_type,
|
||||||
|
event_name,
|
||||||
|
views,
|
||||||
|
min_time,
|
||||||
|
max_time,
|
||||||
|
tag,
|
||||||
|
timestamp as created_at
|
||||||
|
FROM (SELECT
|
||||||
|
website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
argMinState(url_path, created_at) entry_url,
|
||||||
|
argMaxState(url_path, created_at) exit_url,
|
||||||
|
arrayFilter(x -> x != '', groupArray(url_path)) as url_paths,
|
||||||
|
arrayFilter(x -> x != '', groupArray(url_query)) url_query,
|
||||||
|
arrayFilter(x -> x != '', groupArray(referrer_domain)) referrer_domain,
|
||||||
|
arrayFilter(x -> x != '', groupArray(page_title)) page_title,
|
||||||
|
event_type,
|
||||||
|
if(event_type = 2, groupArray(event_name), []) event_name,
|
||||||
|
sumIf(1, event_type = 1) views,
|
||||||
|
min(created_at) min_time,
|
||||||
|
max(created_at) max_time,
|
||||||
|
arrayFilter(x -> x != '', groupArray(tag)) tag,
|
||||||
|
toStartOfHour(created_at) timestamp
|
||||||
|
FROM umami.website_event
|
||||||
|
GROUP BY website_id,
|
||||||
|
session_id,
|
||||||
|
visit_id,
|
||||||
|
hostname,
|
||||||
|
browser,
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
screen,
|
||||||
|
language,
|
||||||
|
country,
|
||||||
|
subdivision1,
|
||||||
|
city,
|
||||||
|
event_type,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
-- projections
|
||||||
|
ALTER TABLE umami.website_event
|
||||||
|
ADD PROJECTION website_event_url_path_projection (
|
||||||
|
SELECT * ORDER BY toStartOfDay(created_at), website_id, url_path, created_at
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE umami.website_event MATERIALIZE PROJECTION website_event_url_path_projection;
|
||||||
|
|
||||||
|
ALTER TABLE umami.website_event
|
||||||
|
ADD PROJECTION website_event_referrer_domain_projection (
|
||||||
|
SELECT * ORDER BY toStartOfDay(created_at), website_id, referrer_domain, created_at
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE umami.website_event MATERIALIZE PROJECTION website_event_referrer_domain_projection;
|
||||||
|
@ -11,7 +11,7 @@ CREATE TABLE `user` (
|
|||||||
UNIQUE INDEX `user_user_id_key`(`user_id`),
|
UNIQUE INDEX `user_user_id_key`(`user_id`),
|
||||||
UNIQUE INDEX `user_username_key`(`username`),
|
UNIQUE INDEX `user_username_key`(`username`),
|
||||||
PRIMARY KEY (`user_id`)
|
PRIMARY KEY (`user_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `session` (
|
CREATE TABLE `session` (
|
||||||
@ -33,7 +33,7 @@ CREATE TABLE `session` (
|
|||||||
INDEX `session_created_at_idx`(`created_at`),
|
INDEX `session_created_at_idx`(`created_at`),
|
||||||
INDEX `session_website_id_idx`(`website_id`),
|
INDEX `session_website_id_idx`(`website_id`),
|
||||||
PRIMARY KEY (`session_id`)
|
PRIMARY KEY (`session_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `website` (
|
CREATE TABLE `website` (
|
||||||
@ -53,7 +53,7 @@ CREATE TABLE `website` (
|
|||||||
INDEX `website_created_at_idx`(`created_at`),
|
INDEX `website_created_at_idx`(`created_at`),
|
||||||
INDEX `website_share_id_idx`(`share_id`),
|
INDEX `website_share_id_idx`(`share_id`),
|
||||||
PRIMARY KEY (`website_id`)
|
PRIMARY KEY (`website_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `website_event` (
|
CREATE TABLE `website_event` (
|
||||||
@ -76,7 +76,7 @@ CREATE TABLE `website_event` (
|
|||||||
INDEX `website_event_website_id_created_at_idx`(`website_id`, `created_at`),
|
INDEX `website_event_website_id_created_at_idx`(`website_id`, `created_at`),
|
||||||
INDEX `website_event_website_id_session_id_created_at_idx`(`website_id`, `session_id`, `created_at`),
|
INDEX `website_event_website_id_session_id_created_at_idx`(`website_id`, `session_id`, `created_at`),
|
||||||
PRIMARY KEY (`event_id`)
|
PRIMARY KEY (`event_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `event_data` (
|
CREATE TABLE `event_data` (
|
||||||
@ -95,7 +95,7 @@ CREATE TABLE `event_data` (
|
|||||||
INDEX `event_data_website_event_id_idx`(`website_event_id`),
|
INDEX `event_data_website_event_id_idx`(`website_event_id`),
|
||||||
INDEX `event_data_website_id_website_event_id_created_at_idx`(`website_id`, `website_event_id`, `created_at`),
|
INDEX `event_data_website_id_website_event_id_created_at_idx`(`website_id`, `website_event_id`, `created_at`),
|
||||||
PRIMARY KEY (`event_id`)
|
PRIMARY KEY (`event_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `team` (
|
CREATE TABLE `team` (
|
||||||
@ -109,7 +109,7 @@ CREATE TABLE `team` (
|
|||||||
UNIQUE INDEX `team_access_code_key`(`access_code`),
|
UNIQUE INDEX `team_access_code_key`(`access_code`),
|
||||||
INDEX `team_access_code_idx`(`access_code`),
|
INDEX `team_access_code_idx`(`access_code`),
|
||||||
PRIMARY KEY (`team_id`)
|
PRIMARY KEY (`team_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `team_user` (
|
CREATE TABLE `team_user` (
|
||||||
@ -124,7 +124,7 @@ CREATE TABLE `team_user` (
|
|||||||
INDEX `team_user_team_id_idx`(`team_id`),
|
INDEX `team_user_team_id_idx`(`team_id`),
|
||||||
INDEX `team_user_user_id_idx`(`user_id`),
|
INDEX `team_user_user_id_idx`(`user_id`),
|
||||||
PRIMARY KEY (`team_user_id`)
|
PRIMARY KEY (`team_user_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `team_website` (
|
CREATE TABLE `team_website` (
|
||||||
@ -137,7 +137,7 @@ CREATE TABLE `team_website` (
|
|||||||
INDEX `team_website_team_id_idx`(`team_id`),
|
INDEX `team_website_team_id_idx`(`team_id`),
|
||||||
INDEX `team_website_website_id_idx`(`website_id`),
|
INDEX `team_website_website_id_idx`(`website_id`),
|
||||||
PRIMARY KEY (`team_website_id`)
|
PRIMARY KEY (`team_website_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- AddSystemUser
|
-- AddSystemUser
|
||||||
INSERT INTO user (user_id, username, role, password) VALUES ('41e2b680-648e-4b09-bcd7-3e2b10c06264' , 'admin', 'admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa');
|
INSERT INTO user (user_id, username, role, password) VALUES ('41e2b680-648e-4b09-bcd7-3e2b10c06264' , 'admin', 'admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa');
|
@ -21,7 +21,7 @@ CREATE TABLE `session_data` (
|
|||||||
INDEX `session_data_website_id_idx`(`website_id`),
|
INDEX `session_data_website_id_idx`(`website_id`),
|
||||||
INDEX `session_data_session_id_idx`(`session_id`),
|
INDEX `session_data_session_id_idx`(`session_id`),
|
||||||
PRIMARY KEY (`session_data_id`)
|
PRIMARY KEY (`session_data_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE `report` (
|
CREATE TABLE `report` (
|
||||||
@ -41,7 +41,7 @@ CREATE TABLE `report` (
|
|||||||
INDEX `report_type_idx`(`type`),
|
INDEX `report_type_idx`(`type`),
|
||||||
INDEX `report_name_idx`(`name`),
|
INDEX `report_name_idx`(`name`),
|
||||||
PRIMARY KEY (`report_id`)
|
PRIMARY KEY (`report_id`)
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
-- EventData migration
|
-- EventData migration
|
||||||
UPDATE event_data
|
UPDATE event_data
|
||||||
|
5
db/mysql/migrations/07_add_tag/migration.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `website_event` ADD COLUMN `tag` VARCHAR(50) NULL;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `website_event_website_id_created_at_tag_idx` ON `website_event`(`website_id`, `created_at`, `tag`);
|
@ -102,6 +102,7 @@ model WebsiteEvent {
|
|||||||
pageTitle String? @map("page_title") @db.VarChar(500)
|
pageTitle String? @map("page_title") @db.VarChar(500)
|
||||||
eventType Int @default(1) @map("event_type") @db.UnsignedInt
|
eventType Int @default(1) @map("event_type") @db.UnsignedInt
|
||||||
eventName String? @map("event_name") @db.VarChar(50)
|
eventName String? @map("event_name") @db.VarChar(50)
|
||||||
|
tag String? @db.VarChar(50)
|
||||||
|
|
||||||
eventData EventData[]
|
eventData EventData[]
|
||||||
session Session @relation(fields: [sessionId], references: [id])
|
session Session @relation(fields: [sessionId], references: [id])
|
||||||
@ -116,6 +117,7 @@ model WebsiteEvent {
|
|||||||
@@index([websiteId, createdAt, referrerDomain])
|
@@index([websiteId, createdAt, referrerDomain])
|
||||||
@@index([websiteId, createdAt, pageTitle])
|
@@index([websiteId, createdAt, pageTitle])
|
||||||
@@index([websiteId, createdAt, eventName])
|
@@index([websiteId, createdAt, eventName])
|
||||||
|
@@index([websiteId, createdAt, tag])
|
||||||
@@index([websiteId, sessionId, createdAt])
|
@@index([websiteId, sessionId, createdAt])
|
||||||
@@index([websiteId, visitId, createdAt])
|
@@index([websiteId, visitId, createdAt])
|
||||||
@@map("website_event")
|
@@map("website_event")
|
||||||
|
5
db/postgresql/migrations/07_add_tag/migration.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "website_event" ADD COLUMN "tag" VARCHAR(50);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "website_event_website_id_created_at_tag_idx" ON "website_event"("website_id", "created_at", "tag");
|
@ -102,6 +102,7 @@ model WebsiteEvent {
|
|||||||
pageTitle String? @map("page_title") @db.VarChar(500)
|
pageTitle String? @map("page_title") @db.VarChar(500)
|
||||||
eventType Int @default(1) @map("event_type") @db.Integer
|
eventType Int @default(1) @map("event_type") @db.Integer
|
||||||
eventName String? @map("event_name") @db.VarChar(50)
|
eventName String? @map("event_name") @db.VarChar(50)
|
||||||
|
tag String? @db.VarChar(50)
|
||||||
|
|
||||||
eventData EventData[]
|
eventData EventData[]
|
||||||
session Session @relation(fields: [sessionId], references: [id])
|
session Session @relation(fields: [sessionId], references: [id])
|
||||||
@ -111,11 +112,13 @@ model WebsiteEvent {
|
|||||||
@@index([visitId])
|
@@index([visitId])
|
||||||
@@index([websiteId])
|
@@index([websiteId])
|
||||||
@@index([websiteId, createdAt])
|
@@index([websiteId, createdAt])
|
||||||
|
|
||||||
@@index([websiteId, createdAt, urlPath])
|
@@index([websiteId, createdAt, urlPath])
|
||||||
@@index([websiteId, createdAt, urlQuery])
|
@@index([websiteId, createdAt, urlQuery])
|
||||||
@@index([websiteId, createdAt, referrerDomain])
|
@@index([websiteId, createdAt, referrerDomain])
|
||||||
@@index([websiteId, createdAt, pageTitle])
|
@@index([websiteId, createdAt, pageTitle])
|
||||||
@@index([websiteId, createdAt, eventName])
|
@@index([websiteId, createdAt, eventName])
|
||||||
|
@@index([websiteId, createdAt, tag])
|
||||||
@@index([websiteId, sessionId, createdAt])
|
@@index([websiteId, sessionId, createdAt])
|
||||||
@@index([websiteId, visitId, createdAt])
|
@@index([websiteId, visitId, createdAt])
|
||||||
@@map("website_event")
|
@@map("website_event")
|
||||||
|
2
next-env.d.ts
vendored
@ -3,4 +3,4 @@
|
|||||||
/// <reference types="next/navigation-types/compat/navigation" />
|
/// <reference types="next/navigation-types/compat/navigation" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||||
|
107
next.config.js
@ -3,29 +3,31 @@ require('dotenv').config();
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const pkg = require('./package.json');
|
const pkg = require('./package.json');
|
||||||
|
|
||||||
const basePath = process.env.BASE_PATH || '';
|
const TRACKER_SCRIPT = '/script.js';
|
||||||
const forceSSL = process.env.FORCE_SSL || '';
|
|
||||||
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT || '';
|
const basePath = process.env.BASE_PATH;
|
||||||
const defaultLocale = process.env.DEFAULT_LOCALE || '';
|
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT;
|
||||||
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME || '';
|
const cloudMode = process.env.CLOUD_MODE;
|
||||||
const cloudMode = process.env.CLOUD_MODE || '';
|
const cloudUrl = process.env.CLOUD_URL;
|
||||||
const cloudUrl = process.env.CLOUD_URL || '';
|
const defaultLocale = process.env.DEFAULT_LOCALE;
|
||||||
const frameAncestors = process.env.ALLOWED_FRAME_URLS || '';
|
const disableLogin = process.env.DISABLE_LOGIN;
|
||||||
const disableLogin = process.env.DISABLE_LOGIN || '';
|
const disableUI = process.env.DISABLE_UI;
|
||||||
const disableUI = process.env.DISABLE_UI || '';
|
const forceSSL = process.env.FORCE_SSL;
|
||||||
const hostURL = process.env.HOST_URL || '';
|
const frameAncestors = process.env.ALLOWED_FRAME_URLS;
|
||||||
const privateMode = process.env.PRIVATE_MODE || '';
|
const privateMode = process.env.PRIVATE_MODE;
|
||||||
|
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME;
|
||||||
|
const trackerScriptURL = process.env.TRACKER_SCRIPT_URL;
|
||||||
|
|
||||||
const contentSecurityPolicy = [
|
const contentSecurityPolicy = [
|
||||||
`default-src 'self'`,
|
`default-src 'self'`,
|
||||||
`img-src *`,
|
`img-src * data:`,
|
||||||
`script-src 'self' 'unsafe-eval' 'unsafe-inline'`,
|
`script-src 'self' 'unsafe-eval' 'unsafe-inline'`,
|
||||||
`style-src 'self' 'unsafe-inline'`,
|
`style-src 'self' 'unsafe-inline'`,
|
||||||
`connect-src 'self' api.umami.is cloud.umami.is`,
|
`connect-src 'self' api.umami.is cloud.umami.is`,
|
||||||
`frame-ancestors 'self' ${frameAncestors}`,
|
`frame-ancestors 'self' ${frameAncestors}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
const headers = [
|
const defaultHeaders = [
|
||||||
{
|
{
|
||||||
key: 'X-DNS-Prefetch-Control',
|
key: 'X-DNS-Prefetch-Control',
|
||||||
value: 'on',
|
value: 'on',
|
||||||
@ -40,14 +42,43 @@ const headers = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (forceSSL) {
|
if (forceSSL) {
|
||||||
headers.push({
|
defaultHeaders.push({
|
||||||
key: 'Strict-Transport-Security',
|
key: 'Strict-Transport-Security',
|
||||||
value: 'max-age=63072000; includeSubDomains; preload',
|
value: 'max-age=63072000; includeSubDomains; preload',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const trackerHeaders = [
|
||||||
|
{
|
||||||
|
key: 'Access-Control-Allow-Origin',
|
||||||
|
value: '*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Cache-Control',
|
||||||
|
value: 'public, max-age=86400, must-revalidate',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
{
|
||||||
|
source: '/:path*',
|
||||||
|
headers: defaultHeaders,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: TRACKER_SCRIPT,
|
||||||
|
headers: trackerHeaders,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const rewrites = [];
|
const rewrites = [];
|
||||||
|
|
||||||
|
if (trackerScriptURL) {
|
||||||
|
rewrites.push({
|
||||||
|
source: TRACKER_SCRIPT,
|
||||||
|
destination: trackerScriptURL,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (collectApiEndpoint) {
|
if (collectApiEndpoint) {
|
||||||
rewrites.push({
|
rewrites.push({
|
||||||
source: collectApiEndpoint,
|
source: collectApiEndpoint,
|
||||||
@ -55,19 +86,6 @@ if (collectApiEndpoint) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackerScriptName) {
|
|
||||||
const names = trackerScriptName?.split(',').map(name => name.trim());
|
|
||||||
|
|
||||||
if (names) {
|
|
||||||
names.forEach(name => {
|
|
||||||
rewrites.push({
|
|
||||||
source: `/${name.replace(/^\/+/, '')}`,
|
|
||||||
destination: '/script.js',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const redirects = [
|
const redirects = [
|
||||||
{
|
{
|
||||||
source: '/settings',
|
source: '/settings',
|
||||||
@ -86,6 +104,27 @@ const redirects = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Adding rewrites + headers for all alternative tracker script names.
|
||||||
|
if (trackerScriptName) {
|
||||||
|
const names = trackerScriptName?.split(',').map(name => name.trim());
|
||||||
|
|
||||||
|
if (names) {
|
||||||
|
names.forEach(name => {
|
||||||
|
const normalizedSource = `/${name.replace(/^\/+/, '')}`;
|
||||||
|
|
||||||
|
rewrites.push({
|
||||||
|
source: normalizedSource,
|
||||||
|
destination: TRACKER_SCRIPT,
|
||||||
|
});
|
||||||
|
|
||||||
|
headers.push({
|
||||||
|
source: normalizedSource,
|
||||||
|
headers: trackerHeaders,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cloudMode && cloudUrl) {
|
if (cloudMode && cloudUrl) {
|
||||||
redirects.push({
|
redirects.push({
|
||||||
source: '/settings/:path*',
|
source: '/settings/:path*',
|
||||||
@ -120,7 +159,6 @@ const config = {
|
|||||||
defaultLocale,
|
defaultLocale,
|
||||||
disableLogin,
|
disableLogin,
|
||||||
disableUI,
|
disableUI,
|
||||||
hostURL,
|
|
||||||
privateMode,
|
privateMode,
|
||||||
},
|
},
|
||||||
basePath,
|
basePath,
|
||||||
@ -155,12 +193,7 @@ const config = {
|
|||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
return headers;
|
||||||
{
|
|
||||||
source: '/:path*',
|
|
||||||
headers,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
@ -169,6 +202,10 @@ const config = {
|
|||||||
source: '/telemetry.js',
|
source: '/telemetry.js',
|
||||||
destination: '/api/scripts/telemetry',
|
destination: '/api/scripts/telemetry',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: '/teams/:teamId/:path((?!settings).*)*',
|
||||||
|
destination: '/:path*',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
async redirects() {
|
async redirects() {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"@tanstack/react-query": "^4.33.0",
|
"@tanstack/react-query": "^4.33.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"colord": "^2.9.2",
|
"colord": "^2.9.2",
|
||||||
|
"date-fns-tz": "^1.1.4",
|
||||||
"immer": "^9.0.12",
|
"immer": "^9.0.12",
|
||||||
"moment-timezone": "^0.5.35",
|
"moment-timezone": "^0.5.35",
|
||||||
"next": "^13.4.0",
|
"next": "^13.4.0",
|
||||||
|
30
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.12.0",
|
"version": "2.15.0",
|
||||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||||
"author": "Umami Software, Inc. <hello@umami.is>",
|
"author": "Umami Software, Inc. <hello@umami.is>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -63,15 +63,21 @@
|
|||||||
"cacheDirectories": [
|
"cacheDirectories": [
|
||||||
".next/cache"
|
".next/cache"
|
||||||
],
|
],
|
||||||
|
"resolutions": {
|
||||||
|
"jackspeak": "2.1.1"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clickhouse/client": "^0.2.2",
|
"@clickhouse/client": "^1.4.1",
|
||||||
|
"@date-fns/utc": "^1.2.0",
|
||||||
|
"@dicebear/collection": "^9.2.1",
|
||||||
|
"@dicebear/core": "^9.2.1",
|
||||||
"@fontsource/inter": "^4.5.15",
|
"@fontsource/inter": "^4.5.15",
|
||||||
"@prisma/client": "5.12.1",
|
"@prisma/client": "5.22.0",
|
||||||
"@prisma/extension-read-replicas": "^0.3.0",
|
"@prisma/extension-read-replicas": "^0.3.0",
|
||||||
"@react-spring/web": "^9.7.3",
|
"@react-spring/web": "^9.7.3",
|
||||||
"@tanstack/react-query": "^5.28.6",
|
"@tanstack/react-query": "^5.28.6",
|
||||||
"@umami/prisma-client": "^0.14.0",
|
"@umami/prisma-client": "^0.14.0",
|
||||||
"@umami/redis-client": "^0.18.0",
|
"@umami/redis-client": "^0.21.0",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"chart.js": "^4.4.2",
|
"chart.js": "^4.4.2",
|
||||||
"chartjs-adapter-date-fns": "^3.0.0",
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
@ -81,7 +87,6 @@
|
|||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.3",
|
||||||
"date-fns": "^2.23.0",
|
"date-fns": "^2.23.0",
|
||||||
"date-fns-tz": "^1.1.4",
|
"date-fns-tz": "^1.1.4",
|
||||||
"dateformat": "^5.0.3",
|
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"del": "^6.0.0",
|
"del": "^6.0.0",
|
||||||
"detect-browser": "^5.2.0",
|
"detect-browser": "^5.2.0",
|
||||||
@ -93,18 +98,17 @@
|
|||||||
"is-ci": "^3.0.1",
|
"is-ci": "^3.0.1",
|
||||||
"is-docker": "^3.0.0",
|
"is-docker": "^3.0.0",
|
||||||
"is-localhost-ip": "^1.4.0",
|
"is-localhost-ip": "^1.4.0",
|
||||||
"isbot": "^5.1.1",
|
"isbot": "^5.1.16",
|
||||||
"kafkajs": "^2.1.0",
|
"kafkajs": "^2.1.0",
|
||||||
"maxmind": "^4.3.6",
|
"maxmind": "^4.3.6",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"moment-timezone": "^0.5.35",
|
"next": "15.0.3",
|
||||||
"next": "14.1.4",
|
|
||||||
"next-basics": "^0.39.0",
|
"next-basics": "^0.39.0",
|
||||||
"node-fetch": "^3.2.8",
|
"node-fetch": "^3.2.8",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prisma": "5.12.1",
|
"prisma": "5.22.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-basics": "^0.123.0",
|
"react-basics": "^0.125.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.4",
|
"react-error-boundary": "^4.0.4",
|
||||||
@ -117,11 +121,11 @@
|
|||||||
"thenby": "^1.3.4",
|
"thenby": "^1.3.4",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"yup": "^0.32.11",
|
"yup": "^0.32.11",
|
||||||
"zustand": "^4.3.8"
|
"zustand": "^4.5.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@formatjs/cli": "^4.2.29",
|
"@formatjs/cli": "^4.2.29",
|
||||||
"@netlify/plugin-nextjs": "^4.41.3",
|
"@netlify/plugin-nextjs": "^5.8.1",
|
||||||
"@rollup/plugin-alias": "^5.0.0",
|
"@rollup/plugin-alias": "^5.0.0",
|
||||||
"@rollup/plugin-commonjs": "^25.0.4",
|
"@rollup/plugin-commonjs": "^25.0.4",
|
||||||
"@rollup/plugin-json": "^6.0.0",
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
@ -175,6 +179,6 @@
|
|||||||
"tar": "^6.1.2",
|
"tar": "^6.1.2",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.4.3"
|
"typescript": "^5.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 806 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 819 B After Width: | Height: | Size: 819 B |
Before Width: | Height: | Size: 680 B After Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 819 B After Width: | Height: | Size: 819 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 811 B |
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 811 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 835 B After Width: | Height: | Size: 835 B |
Before Width: | Height: | Size: 835 B After Width: | Height: | Size: 835 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 794 B After Width: | Height: | Size: 794 B |
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 632 B |
Before Width: | Height: | Size: 829 B After Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 535 B After Width: | Height: | Size: 535 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 106 B After Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 794 B After Width: | Height: | Size: 794 B |
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 122 B After Width: | Height: | Size: 122 B |
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 281 B |
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 245 B |
Before Width: | Height: | Size: 183 B After Width: | Height: | Size: 183 B |
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 110 B |
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 186 B After Width: | Height: | Size: 186 B |
Before Width: | Height: | Size: 141 B After Width: | Height: | Size: 141 B |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 237 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 139 B After Width: | Height: | Size: 139 B |
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 163 B |
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 150 B |
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 175 B |
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 129 B |
Before Width: | Height: | Size: 105 B After Width: | Height: | Size: 105 B |
Before Width: | Height: | Size: 140 B After Width: | Height: | Size: 140 B |
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 97 B |
Before Width: | Height: | Size: 153 B After Width: | Height: | Size: 153 B |
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 107 B |
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 353 B |
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 260 B After Width: | Height: | Size: 260 B |
Before Width: | Height: | Size: 147 B After Width: | Height: | Size: 147 B |
Before Width: | Height: | Size: 316 B After Width: | Height: | Size: 316 B |
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 150 B |
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 114 B |
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 253 B |
Before Width: | Height: | Size: 171 B After Width: | Height: | Size: 171 B |
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 233 B |
Before Width: | Height: | Size: 206 B After Width: | Height: | Size: 206 B |
Before Width: | Height: | Size: 164 B After Width: | Height: | Size: 164 B |
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 129 B |
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 114 B |
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 144 B After Width: | Height: | Size: 144 B |
Before Width: | Height: | Size: 135 B After Width: | Height: | Size: 135 B |
Before Width: | Height: | Size: 144 B After Width: | Height: | Size: 144 B |
Before Width: | Height: | Size: 112 B After Width: | Height: | Size: 112 B |
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 163 B |
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |