mirror of
https://github.com/umami-software/umami.git
synced 2026-02-18 19:45:35 +01:00
upgrade
This commit is contained in:
parent
bbf91f31db
commit
6a8b804aff
27 changed files with 16152 additions and 2893 deletions
|
|
@ -13,13 +13,6 @@
|
|||
"ecmaVersion": 11,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"moduleDirectory": ["node_modules", "src/"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"eslint:recommended",
|
||||
|
|
|
|||
29
.github/workflows/cd-cloud.yml
vendored
Normal file
29
.github/workflows/cd-cloud.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: Create docker images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- analytics
|
||||
- cloud
|
||||
|
||||
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
19
.github/workflows/cd-manual.yml
vendored
|
|
@ -20,11 +20,26 @@ jobs:
|
|||
steps:
|
||||
- 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
|
||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
||||
with:
|
||||
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 }}
|
||||
registry: ghcr.io
|
||||
multiPlatform: true
|
||||
|
|
@ -36,7 +51,7 @@ jobs:
|
|||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
||||
with:
|
||||
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 }}
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
|
|
|
|||
14
.github/workflows/cd.yml
vendored
14
.github/workflows/cd.yml
vendored
|
|
@ -17,14 +17,21 @@ jobs:
|
|||
|
||||
- name: Set env
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $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
|
||||
name: Build & push Docker image to ghcr.io for ${{ matrix.db-type }}
|
||||
with:
|
||||
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 }}
|
||||
registry: ghcr.io
|
||||
multiPlatform: true
|
||||
|
|
@ -32,12 +39,11 @@ jobs:
|
|||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
||||
name: Build & push Docker image to docker.io for ${{ matrix.db-type }}
|
||||
with:
|
||||
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 }}
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
|
|
|
|||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -16,9 +16,9 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- node-version: 18.17
|
||||
- node-version: 18.18
|
||||
db-type: postgresql
|
||||
- node-version: 18.17
|
||||
- node-version: 18.18
|
||||
db-type: mysql
|
||||
|
||||
steps:
|
||||
|
|
|
|||
11
Dockerfile
11
Dockerfile
|
|
@ -1,5 +1,5 @@
|
|||
# Install dependencies only when needed
|
||||
FROM node:18-alpine AS deps
|
||||
FROM node:22-alpine AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
|
@ -9,7 +9,7 @@ RUN yarn config set network-timeout 300000
|
|||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM node:18-alpine AS builder
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
|
@ -26,18 +26,21 @@ ENV NEXT_TELEMETRY_DISABLED 1
|
|||
RUN yarn build-docker
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM node:18-alpine AS runner
|
||||
FROM node:22-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ARG NODE_OPTIONS
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
ENV NODE_OPTIONS $NODE_OPTIONS
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache curl \
|
||||
&& yarn add npm-run-all dotenv prisma semver
|
||||
&& yarn add npm-run-all dotenv semver prisma@6.1.0
|
||||
|
||||
# You only need to copy next.config.js if you are NOT using the default configuration
|
||||
COPY --from=builder /app/next.config.js .
|
||||
|
|
|
|||
116
README.md
116
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
|
||||
|
||||
- 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 server with Node.js version 18.18 or newer
|
||||
- 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
|
||||
|
||||
```
|
||||
```bash
|
||||
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
|
||||
cd umami
|
||||
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
|
||||
```
|
||||
|
||||
The connection url is in the following format:
|
||||
The connection URL format:
|
||||
|
||||
```
|
||||
```bash
|
||||
postgresql://username:mypassword@localhost:5432/mydb
|
||||
|
||||
mysql://username:mypassword@localhost:3306/mydb
|
||||
```
|
||||
|
||||
### Build the application
|
||||
### Build the Application
|
||||
|
||||
```bash
|
||||
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
|
||||
yarn start
|
||||
```
|
||||
|
||||
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.
|
||||
*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.*
|
||||
|
||||
## 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
|
||||
docker compose up -d
|
||||
|
|
@ -72,16 +96,18 @@ docker compose up -d
|
|||
Alternatively, to pull just the Umami Docker image with PostgreSQL support:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/umami-software/umami:postgresql-latest
|
||||
docker pull docker.umami.is/umami-software/umami:postgresql-latest
|
||||
```
|
||||
|
||||
Or with MySQL support:
|
||||
|
||||
```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:
|
||||
|
||||
|
|
@ -95,9 +121,39 @@ To update the Docker image, simply pull the new images and rebuild:
|
|||
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose up --force-recreate
|
||||
docker compose up --force-recreate -d
|
||||
```
|
||||
|
||||
## 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
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
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
|
||||
event_type UInt32,
|
||||
event_name String,
|
||||
tag String,
|
||||
created_at DateTime('UTC'),
|
||||
job_id UUID
|
||||
job_id Nullable(UUID)
|
||||
)
|
||||
engine = MergeTree
|
||||
ORDER BY (website_id, session_id, created_at)
|
||||
SETTINGS index_granularity = 8192;
|
||||
ENGINE = MergeTree
|
||||
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;
|
||||
|
||||
CREATE TABLE umami.event_data
|
||||
(
|
||||
|
|
@ -40,14 +43,156 @@ CREATE TABLE umami.event_data
|
|||
event_id UUID,
|
||||
url_path String,
|
||||
event_name String,
|
||||
event_key String,
|
||||
data_key String,
|
||||
string_value Nullable(String),
|
||||
number_value Nullable(Decimal64(4)), --922337203685477.5625
|
||||
number_value Nullable(Decimal64(4)),
|
||||
date_value Nullable(DateTime('UTC')),
|
||||
data_type UInt32,
|
||||
created_at DateTime('UTC'),
|
||||
job_id UUID
|
||||
job_id Nullable(UUID)
|
||||
)
|
||||
engine = MergeTree
|
||||
ORDER BY (website_id, event_id, event_key, created_at)
|
||||
SETTINGS index_granularity = 8192;
|
||||
ENGINE = MergeTree
|
||||
ORDER BY (website_id, event_id, data_key, created_at)
|
||||
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_username_key`(`username`),
|
||||
PRIMARY KEY (`user_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `session` (
|
||||
|
|
@ -33,7 +33,7 @@ CREATE TABLE `session` (
|
|||
INDEX `session_created_at_idx`(`created_at`),
|
||||
INDEX `session_website_id_idx`(`website_id`),
|
||||
PRIMARY KEY (`session_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `website` (
|
||||
|
|
@ -53,7 +53,7 @@ CREATE TABLE `website` (
|
|||
INDEX `website_created_at_idx`(`created_at`),
|
||||
INDEX `website_share_id_idx`(`share_id`),
|
||||
PRIMARY KEY (`website_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
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_session_id_created_at_idx`(`website_id`, `session_id`, `created_at`),
|
||||
PRIMARY KEY (`event_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
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_id_website_event_id_created_at_idx`(`website_id`, `website_event_id`, `created_at`),
|
||||
PRIMARY KEY (`event_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `team` (
|
||||
|
|
@ -109,7 +109,7 @@ CREATE TABLE `team` (
|
|||
UNIQUE INDEX `team_access_code_key`(`access_code`),
|
||||
INDEX `team_access_code_idx`(`access_code`),
|
||||
PRIMARY KEY (`team_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `team_user` (
|
||||
|
|
@ -124,7 +124,7 @@ CREATE TABLE `team_user` (
|
|||
INDEX `team_user_team_id_idx`(`team_id`),
|
||||
INDEX `team_user_user_id_idx`(`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
|
||||
CREATE TABLE `team_website` (
|
||||
|
|
@ -137,7 +137,7 @@ CREATE TABLE `team_website` (
|
|||
INDEX `team_website_team_id_idx`(`team_id`),
|
||||
INDEX `team_website_website_id_idx`(`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
|
||||
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_session_id_idx`(`session_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
|
||||
CREATE TABLE `report` (
|
||||
|
|
@ -41,7 +41,7 @@ CREATE TABLE `report` (
|
|||
INDEX `report_type_idx`(`type`),
|
||||
INDEX `report_name_idx`(`name`),
|
||||
PRIMARY KEY (`report_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- EventData migration
|
||||
UPDATE event_data
|
||||
|
|
|
|||
20
db/mysql/migrations/06_session_data/migration.sql
Normal file
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
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 {
|
||||
provider = "prisma-client-js"
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["native"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
|
@ -19,10 +20,10 @@ model User {
|
|||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||
|
||||
websiteUser Website[] @relation("user")
|
||||
websiteCreateUser Website[] @relation("createUser")
|
||||
teamUser TeamUser[]
|
||||
report Report[]
|
||||
websiteUser Website[] @relation("user")
|
||||
websiteCreateUser Website[] @relation("createUser")
|
||||
teamUser TeamUser[]
|
||||
report Report[]
|
||||
|
||||
@@map("user")
|
||||
}
|
||||
|
|
@ -102,6 +103,7 @@ model WebsiteEvent {
|
|||
pageTitle String? @map("page_title") @db.VarChar(500)
|
||||
eventType Int @default(1) @map("event_type") @db.UnsignedInt
|
||||
eventName String? @map("event_name") @db.VarChar(50)
|
||||
tag String? @db.VarChar(50)
|
||||
|
||||
eventData EventData[]
|
||||
session Session @relation(fields: [sessionId], references: [id])
|
||||
|
|
@ -116,6 +118,7 @@ model WebsiteEvent {
|
|||
@@index([websiteId, createdAt, referrerDomain])
|
||||
@@index([websiteId, createdAt, pageTitle])
|
||||
@@index([websiteId, createdAt, eventName])
|
||||
@@index([websiteId, createdAt, tag])
|
||||
@@index([websiteId, sessionId, createdAt])
|
||||
@@index([websiteId, visitId, createdAt])
|
||||
@@map("website_event")
|
||||
|
|
@ -125,7 +128,7 @@ model EventData {
|
|||
id String @id() @map("event_data_id") @db.VarChar(36)
|
||||
websiteId String @map("website_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)
|
||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||
dateValue DateTime? @map("date_value") @db.Timestamp(0)
|
||||
|
|
@ -138,9 +141,8 @@ model EventData {
|
|||
@@index([createdAt])
|
||||
@@index([websiteId])
|
||||
@@index([websiteEventId])
|
||||
@@index([websiteId, websiteEventId, createdAt])
|
||||
@@index([websiteId, createdAt])
|
||||
@@index([websiteId, createdAt, eventKey])
|
||||
@@index([websiteId, createdAt, dataKey])
|
||||
@@map("event_data")
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +150,7 @@ model SessionData {
|
|||
id String @id() @map("session_data_id") @db.VarChar(36)
|
||||
websiteId String @map("website_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)
|
||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||
dateValue DateTime? @map("date_value") @db.Timestamp(0)
|
||||
|
|
@ -161,6 +163,8 @@ model SessionData {
|
|||
@@index([createdAt])
|
||||
@@index([websiteId])
|
||||
@@index([sessionId])
|
||||
@@index([sessionId, createdAt])
|
||||
@@index([websiteId, createdAt, dataKey])
|
||||
@@map("session_data")
|
||||
}
|
||||
|
||||
|
|
@ -173,8 +177,8 @@ model Team {
|
|||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
|
||||
deletedAt DateTime? @map("deleted_at") @db.Timestamp(0)
|
||||
|
||||
website Website[]
|
||||
teamUser TeamUser[]
|
||||
website Website[]
|
||||
teamUser TeamUser[]
|
||||
|
||||
@@index([accessCode])
|
||||
@@map("team")
|
||||
|
|
|
|||
18
db/postgresql/migrations/06_session_data/migration.sql
Normal file
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
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 {
|
||||
provider = "prisma-client-js"
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["native"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
|
@ -19,8 +20,8 @@ model User {
|
|||
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
|
||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
||||
|
||||
websiteUser Website[] @relation("user")
|
||||
websiteCreateUser Website[] @relation("createUser")
|
||||
websiteUser Website[] @relation("user")
|
||||
websiteCreateUser Website[] @relation("createUser")
|
||||
teamUser TeamUser[]
|
||||
report Report[]
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ model WebsiteEvent {
|
|||
pageTitle String? @map("page_title") @db.VarChar(500)
|
||||
eventType Int @default(1) @map("event_type") @db.Integer
|
||||
eventName String? @map("event_name") @db.VarChar(50)
|
||||
tag String? @db.VarChar(50)
|
||||
|
||||
eventData EventData[]
|
||||
session Session @relation(fields: [sessionId], references: [id])
|
||||
|
|
@ -116,6 +118,7 @@ model WebsiteEvent {
|
|||
@@index([websiteId, createdAt, referrerDomain])
|
||||
@@index([websiteId, createdAt, pageTitle])
|
||||
@@index([websiteId, createdAt, eventName])
|
||||
@@index([websiteId, createdAt, tag])
|
||||
@@index([websiteId, sessionId, createdAt])
|
||||
@@index([websiteId, visitId, createdAt])
|
||||
@@map("website_event")
|
||||
|
|
@ -125,7 +128,7 @@ model EventData {
|
|||
id String @id() @map("event_data_id") @db.Uuid
|
||||
websiteId String @map("website_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)
|
||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
||||
|
|
@ -139,7 +142,7 @@ model EventData {
|
|||
@@index([websiteId])
|
||||
@@index([websiteEventId])
|
||||
@@index([websiteId, createdAt])
|
||||
@@index([websiteId, createdAt, eventKey])
|
||||
@@index([websiteId, createdAt, dataKey])
|
||||
@@map("event_data")
|
||||
}
|
||||
|
||||
|
|
@ -147,13 +150,12 @@ model SessionData {
|
|||
id String @id() @map("session_data_id") @db.Uuid
|
||||
websiteId String @map("website_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)
|
||||
numberValue Decimal? @map("number_value") @db.Decimal(19, 4)
|
||||
dateValue DateTime? @map("date_value") @db.Timestamptz(6)
|
||||
dataType Int @map("data_type") @db.Integer
|
||||
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])
|
||||
session Session @relation(fields: [sessionId], references: [id])
|
||||
|
|
@ -161,6 +163,8 @@ model SessionData {
|
|||
@@index([createdAt])
|
||||
@@index([websiteId])
|
||||
@@index([sessionId])
|
||||
@@index([sessionId, createdAt])
|
||||
@@index([websiteId, createdAt, dataKey])
|
||||
@@map("session_data")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
---
|
||||
version: '3'
|
||||
services:
|
||||
umami:
|
||||
image: ghcr.io/umami-software/umami:postgresql-latest
|
||||
|
|
@ -12,6 +11,7 @@ services:
|
|||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
init: true
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
|
||||
|
|
|
|||
3
next-env.d.ts
vendored
3
next-env.d.ts
vendored
|
|
@ -1,6 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
153
next.config.js
153
next.config.js
|
|
@ -1,29 +1,31 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
require('dotenv').config();
|
||||
const path = require('path');
|
||||
const pkg = require('./package.json');
|
||||
|
||||
const basePath = process.env.BASE_PATH || '';
|
||||
const forceSSL = process.env.FORCE_SSL || '';
|
||||
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT || '';
|
||||
const defaultLocale = process.env.DEFAULT_LOCALE || '';
|
||||
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME || '';
|
||||
const cloudMode = process.env.CLOUD_MODE || '';
|
||||
const cloudUrl = process.env.CLOUD_URL || '';
|
||||
const disableLogin = process.env.DISABLE_LOGIN || '';
|
||||
const disableUI = process.env.DISABLE_UI || '';
|
||||
const hostURL = process.env.HOST_URL || '';
|
||||
const privateMode = process.env.PRIVATE_MODE || '';
|
||||
const TRACKER_SCRIPT = '/script.js';
|
||||
|
||||
const basePath = process.env.BASE_PATH;
|
||||
const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT;
|
||||
const cloudMode = process.env.CLOUD_MODE;
|
||||
const cloudUrl = process.env.CLOUD_URL;
|
||||
const corsMaxAge = process.env.CORS_MAX_AGE;
|
||||
const defaultLocale = process.env.DEFAULT_LOCALE;
|
||||
const disableLogin = process.env.DISABLE_LOGIN;
|
||||
const disableUI = process.env.DISABLE_UI;
|
||||
const forceSSL = process.env.FORCE_SSL;
|
||||
const privateMode = process.env.PRIVATE_MODE;
|
||||
const trackerScriptName = process.env.TRACKER_SCRIPT_NAME;
|
||||
const trackerScriptURL = process.env.TRACKER_SCRIPT_URL;
|
||||
|
||||
const contentSecurityPolicy = [
|
||||
`default-src 'self'`,
|
||||
`img-src *`,
|
||||
`img-src * data:`,
|
||||
`script-src 'self' 'unsafe-eval' '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`,
|
||||
];
|
||||
|
||||
const headers = [
|
||||
const defaultHeaders = [
|
||||
{
|
||||
key: 'X-DNS-Prefetch-Control',
|
||||
value: 'on',
|
||||
|
|
@ -38,14 +40,52 @@ const headers = [
|
|||
];
|
||||
|
||||
if (forceSSL) {
|
||||
headers.push({
|
||||
defaultHeaders.push({
|
||||
key: 'Strict-Transport-Security',
|
||||
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: '/api/:path*',
|
||||
headers: [
|
||||
{ key: 'Access-Control-Allow-Origin', value: '*' },
|
||||
{ key: 'Access-Control-Allow-Headers', value: '*' },
|
||||
{ key: 'Access-Control-Allow-Methods', value: 'GET, DELETE, POST, PUT' },
|
||||
{ key: 'Access-Control-Max-Age', value: corsMaxAge || '86400' },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: defaultHeaders,
|
||||
},
|
||||
{
|
||||
source: TRACKER_SCRIPT,
|
||||
headers: trackerHeaders,
|
||||
},
|
||||
];
|
||||
|
||||
const rewrites = [];
|
||||
|
||||
if (trackerScriptURL) {
|
||||
rewrites.push({
|
||||
source: TRACKER_SCRIPT,
|
||||
destination: trackerScriptURL,
|
||||
});
|
||||
}
|
||||
|
||||
if (collectApiEndpoint) {
|
||||
rewrites.push({
|
||||
source: collectApiEndpoint,
|
||||
|
|
@ -53,19 +93,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 = [
|
||||
{
|
||||
source: '/settings',
|
||||
|
|
@ -84,6 +111,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) {
|
||||
redirects.push({
|
||||
source: '/settings/:path*',
|
||||
|
|
@ -118,7 +166,6 @@ const config = {
|
|||
defaultLocale,
|
||||
disableLogin,
|
||||
disableUI,
|
||||
hostURL,
|
||||
privateMode,
|
||||
},
|
||||
basePath,
|
||||
|
|
@ -129,36 +176,26 @@ const config = {
|
|||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
experimental: {
|
||||
turbo: {
|
||||
rules: {
|
||||
'*.svg': {
|
||||
loaders: ['@svgr/webpack'],
|
||||
as: '*.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
webpack(config) {
|
||||
const fileLoaderRule = config.module.rules.find(rule => rule.test?.test?.('.svg'));
|
||||
|
||||
config.module.rules.push(
|
||||
{
|
||||
...fileLoaderRule,
|
||||
test: /\.svg$/i,
|
||||
resourceQuery: /url/,
|
||||
},
|
||||
{
|
||||
test: /\.svg$/i,
|
||||
issuer: fileLoaderRule.issuer,
|
||||
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] },
|
||||
use: ['@svgr/webpack'],
|
||||
},
|
||||
);
|
||||
|
||||
fileLoaderRule.exclude = /\.svg$/i;
|
||||
|
||||
config.resolve.alias['public'] = path.resolve('./public');
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/,
|
||||
issuer: /\.(js|ts)x?$/,
|
||||
use: ['@svgr/webpack'],
|
||||
});
|
||||
return config;
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
headers,
|
||||
},
|
||||
];
|
||||
return headers;
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
|
|
@ -167,6 +204,10 @@ const config = {
|
|||
source: '/telemetry.js',
|
||||
destination: '/api/scripts/telemetry',
|
||||
},
|
||||
{
|
||||
source: '/teams/:teamId/:path((?!settings).*)*',
|
||||
destination: '/:path*',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"@tanstack/react-query": "^4.33.0",
|
||||
"classnames": "^2.3.1",
|
||||
"colord": "^2.9.2",
|
||||
"date-fns-tz": "^1.1.4",
|
||||
"immer": "^9.0.12",
|
||||
"moment-timezone": "^0.5.35",
|
||||
"next": "^13.4.0",
|
||||
|
|
|
|||
57
package.json
57
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "umami",
|
||||
"version": "2.11.0",
|
||||
"version": "2.16.1",
|
||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||
"author": "Umami Software, Inc. <hello@umami.is>",
|
||||
"license": "MIT",
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
"url": "https://github.com/umami-software/umami.git"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3000",
|
||||
"dev": "next dev -p 3000 --turbo",
|
||||
"build": "npm-run-all check-env build-db check-db build-tracker build-geo build-app",
|
||||
"start": "next start",
|
||||
"build-docker": "npm-run-all build-db build-tracker build-geo build-app",
|
||||
|
|
@ -64,14 +64,19 @@
|
|||
".next/cache"
|
||||
],
|
||||
"dependencies": {
|
||||
"@clickhouse/client": "^0.2.2",
|
||||
"@clickhouse/client": "^1.10.1",
|
||||
"@date-fns/utc": "^1.2.0",
|
||||
"@dicebear/collection": "^9.2.1",
|
||||
"@dicebear/core": "^9.2.1",
|
||||
"@fontsource/inter": "^4.5.15",
|
||||
"@prisma/client": "5.11.0",
|
||||
"@prisma/extension-read-replicas": "^0.3.0",
|
||||
"@hello-pangea/dnd": "^17.0.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"@prisma/extension-read-replicas": "^0.4.0",
|
||||
"@react-spring/web": "^9.7.3",
|
||||
"@tanstack/react-query": "^5.28.6",
|
||||
"@umami/prisma-client": "^0.14.0",
|
||||
"@umami/redis-client": "^0.18.0",
|
||||
"@umami/redis-client": "^0.26.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"chalk": "^4.1.1",
|
||||
"chart.js": "^4.4.2",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
|
|
@ -81,7 +86,6 @@
|
|||
"cross-spawn": "^7.0.3",
|
||||
"date-fns": "^2.23.0",
|
||||
"date-fns-tz": "^1.1.4",
|
||||
"dateformat": "^5.0.3",
|
||||
"debug": "^4.3.4",
|
||||
"del": "^6.0.0",
|
||||
"detect-browser": "^5.2.0",
|
||||
|
|
@ -93,20 +97,19 @@
|
|||
"is-ci": "^3.0.1",
|
||||
"is-docker": "^3.0.0",
|
||||
"is-localhost-ip": "^1.4.0",
|
||||
"isbot": "^5.1.1",
|
||||
"isbot": "^5.1.16",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"kafkajs": "^2.1.0",
|
||||
"maxmind": "^4.3.6",
|
||||
"maxmind": "^4.3.24",
|
||||
"md5": "^2.3.0",
|
||||
"moment-timezone": "^0.5.35",
|
||||
"next": "14.1.4",
|
||||
"next-basics": "^0.39.0",
|
||||
"next": "15.0.4",
|
||||
"node-fetch": "^3.2.8",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prisma": "5.11.0",
|
||||
"react": "^18.2.0",
|
||||
"react-basics": "^0.123.0",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"prisma": "6.1.0",
|
||||
"pure-rand": "^6.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-basics": "^0.126.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-error-boundary": "^4.0.4",
|
||||
"react-intl": "^6.5.5",
|
||||
"react-simple-maps": "^2.3.0",
|
||||
|
|
@ -114,14 +117,15 @@
|
|||
"react-window": "^1.8.6",
|
||||
"request-ip": "^3.3.0",
|
||||
"semver": "^7.5.4",
|
||||
"serialize-error": "^12.0.0",
|
||||
"thenby": "^1.3.4",
|
||||
"uuid": "^9.0.0",
|
||||
"yup": "^0.32.11",
|
||||
"zustand": "^4.3.8"
|
||||
"zod": "^3.24.1",
|
||||
"zustand": "^4.5.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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-commonjs": "^25.0.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
|
|
@ -130,16 +134,17 @@
|
|||
"@svgr/rollup": "^8.1.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/cypress": "^1.1.3",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/react": "^18.2.41",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"@types/react-intl": "^3.0.0",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||
"@typescript-eslint/parser": "^6.7.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^13.6.6",
|
||||
"esbuild": "^0.17.17",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-next": "^14.0.4",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
|
|
@ -175,6 +180,6 @@
|
|||
"tar": "^6.1.2",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.4.3"
|
||||
"typescript": "^5.5.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -19,13 +19,8 @@ const customResolver = resolve({
|
|||
|
||||
const aliasConfig = {
|
||||
entries: [
|
||||
{ find: /^app/, replacement: path.resolve('./src/app') },
|
||||
{ find: /^components/, replacement: path.resolve('./src/components') },
|
||||
{ find: /^hooks/, replacement: path.resolve('./src/hooks') },
|
||||
{ find: /^lib/, replacement: path.resolve('./src/lib') },
|
||||
{ find: /^store/, replacement: path.resolve('./src/store') },
|
||||
{ find: /^@/, replacement: path.resolve('./src/') },
|
||||
{ find: /^public/, replacement: path.resolve('./public') },
|
||||
{ find: /^assets/, replacement: path.resolve('./src/assets') },
|
||||
],
|
||||
customResolver,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2021",
|
||||
"target": "es2022",
|
||||
"outDir": "./build",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
|
|
@ -21,16 +21,10 @@
|
|||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": false,
|
||||
"baseUrl": "./src",
|
||||
"types": ["jest"],
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"paths": {
|
||||
"react": ["./node_modules/@types/react"],
|
||||
"assets/*": ["./assets/*"],
|
||||
"components/*": ["./components/*"],
|
||||
"lib/*": ["./lib/*"],
|
||||
"pages/*": ["./pages/*"],
|
||||
"queries/*": ["./queries/*"],
|
||||
"store/*": ["./store/*"],
|
||||
"styles/*": ["./styles/*"]
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue