Merge remote-tracking branch 'upstream/master'
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 }}
|
||||||
|
4
.github/workflows/ci.yml
vendored
@ -16,9 +16,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- node-version: 18.17
|
- node-version: 18.18
|
||||||
db-type: postgresql
|
db-type: postgresql
|
||||||
- node-version: 18.17
|
- node-version: 18.18
|
||||||
db-type: mysql
|
db-type: mysql
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -36,8 +36,8 @@ RUN addgroup --system --gid 1001 nodejs
|
|||||||
RUN adduser --system --uid 1001 nextjs
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apk add --no-cache curl \
|
&& apk add --no-cache curl openssl \
|
||||||
&& 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 .
|
||||||
|
114
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 18.18 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
|
||||||
|
57
db/clickhouse/migrations/03_session_data.sql
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
CREATE TABLE umami.event_data_new
|
||||||
|
(
|
||||||
|
website_id UUID,
|
||||||
|
session_id UUID,
|
||||||
|
event_id UUID,
|
||||||
|
url_path String,
|
||||||
|
event_name String,
|
||||||
|
data_key String,
|
||||||
|
string_value Nullable(String),
|
||||||
|
number_value Nullable(Decimal64(4)),
|
||||||
|
date_value Nullable(DateTime('UTC')),
|
||||||
|
data_type UInt32,
|
||||||
|
created_at DateTime('UTC'),
|
||||||
|
job_id Nullable(UUID)
|
||||||
|
)
|
||||||
|
engine = MergeTree
|
||||||
|
ORDER BY (website_id, event_id, data_key, created_at)
|
||||||
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
|
INSERT INTO umami.event_data_new
|
||||||
|
SELECT website_id,
|
||||||
|
session_id,
|
||||||
|
event_id,
|
||||||
|
url_path,
|
||||||
|
event_name,
|
||||||
|
event_key,
|
||||||
|
string_value,
|
||||||
|
number_value,
|
||||||
|
date_value,
|
||||||
|
data_type,
|
||||||
|
created_at,
|
||||||
|
NULL
|
||||||
|
FROM umami.event_data;
|
||||||
|
|
||||||
|
CREATE TABLE umami.session_data
|
||||||
|
(
|
||||||
|
website_id UUID,
|
||||||
|
session_id UUID,
|
||||||
|
data_key String,
|
||||||
|
string_value Nullable(String),
|
||||||
|
number_value Nullable(Decimal64(4)),
|
||||||
|
date_value Nullable(DateTime('UTC')),
|
||||||
|
data_type UInt32,
|
||||||
|
created_at DateTime('UTC'),
|
||||||
|
job_id Nullable(UUID)
|
||||||
|
)
|
||||||
|
engine = MergeTree
|
||||||
|
ORDER BY (website_id, session_id, data_key, created_at)
|
||||||
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
|
RENAME TABLE umami.event_data TO umami.event_data_old;
|
||||||
|
RENAME TABLE umami.event_data_new TO umami.event_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
DROP TABLE umami.event_data_old
|
||||||
|
*/
|
||||||
|
|
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,12 +26,15 @@ 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 UUID
|
job_id Nullable(UUID)
|
||||||
)
|
)
|
||||||
engine = MergeTree
|
ENGINE = MergeTree
|
||||||
ORDER BY (website_id, session_id, created_at)
|
PARTITION BY toYYYYMM(created_at)
|
||||||
SETTINGS index_granularity = 8192;
|
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;
|
||||||
|
|
||||||
CREATE TABLE umami.event_data
|
CREATE TABLE umami.event_data
|
||||||
(
|
(
|
||||||
@ -40,14 +43,156 @@ CREATE TABLE umami.event_data
|
|||||||
event_id UUID,
|
event_id UUID,
|
||||||
url_path String,
|
url_path String,
|
||||||
event_name String,
|
event_name String,
|
||||||
event_key String,
|
data_key String,
|
||||||
string_value Nullable(String),
|
string_value Nullable(String),
|
||||||
number_value Nullable(Decimal64(4)), --922337203685477.5625
|
number_value Nullable(Decimal64(4)),
|
||||||
date_value Nullable(DateTime('UTC')),
|
date_value Nullable(DateTime('UTC')),
|
||||||
data_type UInt32,
|
data_type UInt32,
|
||||||
created_at DateTime('UTC'),
|
created_at DateTime('UTC'),
|
||||||
job_id UUID
|
job_id Nullable(UUID)
|
||||||
)
|
)
|
||||||
engine = MergeTree
|
ENGINE = MergeTree
|
||||||
ORDER BY (website_id, event_id, event_key, created_at)
|
ORDER BY (website_id, event_id, data_key, created_at)
|
||||||
SETTINGS index_granularity = 8192;
|
SETTINGS index_granularity = 8192;
|
||||||
|
|
||||||
|
CREATE TABLE umami.session_data
|
||||||
|
(
|
||||||
|
website_id UUID,
|
||||||
|
session_id UUID,
|
||||||
|
data_key String,
|
||||||
|
string_value Nullable(String),
|
||||||
|
number_value Nullable(Decimal64(4)),
|
||||||
|
date_value Nullable(DateTime('UTC')),
|
||||||
|
data_type UInt32,
|
||||||
|
created_at DateTime('UTC'),
|
||||||
|
job_id Nullable(UUID)
|
||||||
|
)
|
||||||
|
ENGINE = ReplacingMergeTree
|
||||||
|
ORDER BY (website_id, session_id, data_key)
|
||||||
|
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
|
||||||
|
20
db/mysql/migrations/06_session_data/migration.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-- DropIndex
|
||||||
|
DROP INDEX `event_data_website_id_created_at_event_key_idx` ON `event_data`;
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX `event_data_website_id_website_event_id_created_at_idx` ON `event_data`;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `event_data` RENAME COLUMN `event_key` TO `data_key`;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `session_data` RENAME COLUMN `event_key` TO `data_key`;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `event_data_website_id_created_at_data_key_idx` ON `event_data`(`website_id`, `created_at`, `data_key`);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `session_data_session_id_created_at_idx` ON `session_data`(`session_id`, `created_at`);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX `session_data_website_id_created_at_data_key_idx` ON `session_data`(`website_id`, `created_at`, `data_key`);
|
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`);
|
@ -1,5 +1,6 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl-arm64-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
@ -19,10 +20,10 @@ model User {
|
|||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||||
|
|
||||||
websiteUser Website[] @relation("user")
|
websiteUser Website[] @relation("user")
|
||||||
websiteCreateUser Website[] @relation("createUser")
|
websiteCreateUser Website[] @relation("createUser")
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
report Report[]
|
report Report[]
|
||||||
|
|
||||||
@@map("user")
|
@@map("user")
|
||||||
}
|
}
|
||||||
@ -102,6 +103,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 +118,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")
|
||||||
@ -125,7 +128,7 @@ model EventData {
|
|||||||
id String @id() @map("event_data_id") @db.VarChar(36)
|
id String @id() @map("event_data_id") @db.VarChar(36)
|
||||||
websiteId String @map("website_id") @db.VarChar(36)
|
websiteId String @map("website_id") @db.VarChar(36)
|
||||||
websiteEventId String @map("website_event_id") @db.VarChar(36)
|
websiteEventId String @map("website_event_id") @db.VarChar(36)
|
||||||
eventKey String @map("event_key") @db.VarChar(500)
|
dataKey String @map("data_key") @db.VarChar(500)
|
||||||
stringValue String? @map("string_value") @db.VarChar(500)
|
stringValue String? @map("string_value") @db.VarChar(500)
|
||||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||||
dateValue DateTime? @map("date_value") @db.Timestamp(0)
|
dateValue DateTime? @map("date_value") @db.Timestamp(0)
|
||||||
@ -138,9 +141,8 @@ model EventData {
|
|||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@index([websiteId])
|
@@index([websiteId])
|
||||||
@@index([websiteEventId])
|
@@index([websiteEventId])
|
||||||
@@index([websiteId, websiteEventId, createdAt])
|
|
||||||
@@index([websiteId, createdAt])
|
@@index([websiteId, createdAt])
|
||||||
@@index([websiteId, createdAt, eventKey])
|
@@index([websiteId, createdAt, dataKey])
|
||||||
@@map("event_data")
|
@@map("event_data")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ model SessionData {
|
|||||||
id String @id() @map("session_data_id") @db.VarChar(36)
|
id String @id() @map("session_data_id") @db.VarChar(36)
|
||||||
websiteId String @map("website_id") @db.VarChar(36)
|
websiteId String @map("website_id") @db.VarChar(36)
|
||||||
sessionId String @map("session_id") @db.VarChar(36)
|
sessionId String @map("session_id") @db.VarChar(36)
|
||||||
eventKey String @map("event_key") @db.VarChar(500)
|
dataKey String @map("data_key") @db.VarChar(500)
|
||||||
stringValue String? @map("string_value") @db.VarChar(500)
|
stringValue String? @map("string_value") @db.VarChar(500)
|
||||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||||
dateValue DateTime? @map("date_value") @db.Timestamp(0)
|
dateValue DateTime? @map("date_value") @db.Timestamp(0)
|
||||||
@ -161,6 +163,8 @@ model SessionData {
|
|||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@index([websiteId])
|
@@index([websiteId])
|
||||||
@@index([sessionId])
|
@@index([sessionId])
|
||||||
|
@@index([sessionId, createdAt])
|
||||||
|
@@index([websiteId, createdAt, dataKey])
|
||||||
@@map("session_data")
|
@@map("session_data")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +177,8 @@ model Team {
|
|||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||||
|
|
||||||
website Website[]
|
website Website[]
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
|
|
||||||
@@index([accessCode])
|
@@index([accessCode])
|
||||||
@@map("team")
|
@@map("team")
|
||||||
|
18
db/postgresql/migrations/06_session_data/migration.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-- DropIndex
|
||||||
|
DROP INDEX IF EXISTS "event_data_website_id_created_at_event_key_idx";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "event_data" RENAME COLUMN "event_key" TO "data_key";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "session_data" DROP COLUMN "deleted_at";
|
||||||
|
ALTER TABLE "session_data" RENAME COLUMN "session_key" TO "data_key";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "event_data_website_id_created_at_data_key_idx" ON "event_data"("website_id", "created_at", "data_key");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "session_data_session_id_created_at_idx" ON "session_data"("session_id", "created_at");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "session_data_website_id_created_at_data_key_idx" ON "session_data"("website_id", "created_at", "data_key");
|
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");
|
@ -1,5 +1,6 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl-arm64-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
@ -19,8 +20,8 @@ model User {
|
|||||||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
||||||
|
|
||||||
websiteUser Website[] @relation("user")
|
websiteUser Website[] @relation("user")
|
||||||
websiteCreateUser Website[] @relation("createUser")
|
websiteCreateUser Website[] @relation("createUser")
|
||||||
teamUser TeamUser[]
|
teamUser TeamUser[]
|
||||||
report Report[]
|
report Report[]
|
||||||
|
|
||||||
@ -102,6 +103,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])
|
||||||
@ -116,6 +118,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")
|
||||||
@ -125,7 +128,7 @@ model EventData {
|
|||||||
id String @id() @map("event_data_id") @db.Uuid
|
id String @id() @map("event_data_id") @db.Uuid
|
||||||
websiteId String @map("website_id") @db.Uuid
|
websiteId String @map("website_id") @db.Uuid
|
||||||
websiteEventId String @map("website_event_id") @db.Uuid
|
websiteEventId String @map("website_event_id") @db.Uuid
|
||||||
eventKey String @map("event_key") @db.VarChar(500)
|
dataKey String @map("data_key") @db.VarChar(500)
|
||||||
stringValue String? @map("string_value") @db.VarChar(500)
|
stringValue String? @map("string_value") @db.VarChar(500)
|
||||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||||
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
||||||
@ -139,7 +142,7 @@ model EventData {
|
|||||||
@@index([websiteId])
|
@@index([websiteId])
|
||||||
@@index([websiteEventId])
|
@@index([websiteEventId])
|
||||||
@@index([websiteId, createdAt])
|
@@index([websiteId, createdAt])
|
||||||
@@index([websiteId, createdAt, eventKey])
|
@@index([websiteId, createdAt, dataKey])
|
||||||
@@map("event_data")
|
@@map("event_data")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,13 +150,12 @@ model SessionData {
|
|||||||
id String @id() @map("session_data_id") @db.Uuid
|
id String @id() @map("session_data_id") @db.Uuid
|
||||||
websiteId String @map("website_id") @db.Uuid
|
websiteId String @map("website_id") @db.Uuid
|
||||||
sessionId String @map("session_id") @db.Uuid
|
sessionId String @map("session_id") @db.Uuid
|
||||||
sessionKey String @map("session_key") @db.VarChar(500)
|
dataKey String @map("data_key") @db.VarChar(500)
|
||||||
stringValue String? @map("string_value") @db.VarChar(500)
|
stringValue String? @map("string_value") @db.VarChar(500)
|
||||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||||
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
||||||
dataType Int @map("data_type") @db.Integer
|
dataType Int @map("data_type") @db.Integer
|
||||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||||
deletedAt DateTime? @default(now()) @map("deleted_at") @db.Timestamptz(6)
|
|
||||||
|
|
||||||
website Website @relation(fields: [websiteId], references: [id])
|
website Website @relation(fields: [websiteId], references: [id])
|
||||||
session Session @relation(fields: [sessionId], references: [id])
|
session Session @relation(fields: [sessionId], references: [id])
|
||||||
@ -161,6 +163,8 @@ model SessionData {
|
|||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@index([websiteId])
|
@@index([websiteId])
|
||||||
@@index([sessionId])
|
@@index([sessionId])
|
||||||
|
@@index([sessionId, createdAt])
|
||||||
|
@@index([websiteId, createdAt, dataKey])
|
||||||
@@map("session_data")
|
@@map("session_data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
init: true
|
||||||
restart: always
|
restart: always
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
|
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
|
||||||
|
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",
|
||||||
|
36
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.11.0",
|
"version": "2.15.1",
|
||||||
"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.11.0",
|
"@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.24.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,20 +98,19 @@
|
|||||||
"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.4",
|
||||||
"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.11.0",
|
"prisma": "5.22.0",
|
||||||
"react": "^18.2.0",
|
"react": "^19.0.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": "^19.0.0",
|
||||||
"react-error-boundary": "^4.0.4",
|
"react-error-boundary": "^4.0.4",
|
||||||
"react-intl": "^6.5.5",
|
"react-intl": "^6.5.5",
|
||||||
"react-simple-maps": "^2.3.0",
|
"react-simple-maps": "^2.3.0",
|
||||||
@ -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",
|
||||||
@ -130,7 +134,7 @@
|
|||||||
"@svgr/rollup": "^8.1.0",
|
"@svgr/rollup": "^8.1.0",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@types/cypress": "^1.1.3",
|
"@types/cypress": "^1.1.3",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^20.9.0",
|
"@types/node": "^20.9.0",
|
||||||
"@types/react": "^18.2.41",
|
"@types/react": "^18.2.41",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
@ -175,7 +179,7 @@
|
|||||||
"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"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
||||||
}
|
}
|
||||||
|
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 |