mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-15 17:55:08 +01:00
commit
17aaa55aba
@ -4,7 +4,7 @@
|
|||||||
"es2020": true,
|
"es2020": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": ["eslint:recommended", "plugin:react/recommended", "prettier", "next"],
|
"extends": ["eslint:recommended", "plugin:prettier/recommended", "next"],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
"jsx": true
|
"jsx": true
|
||||||
@ -12,7 +12,6 @@
|
|||||||
"ecmaVersion": 11,
|
"ecmaVersion": 11,
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"plugins": ["react"],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"react/display-name": "off",
|
"react/display-name": "off",
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
|
30
.github/workflows/cd.yml
vendored
Normal file
30
.github/workflows/cd.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Create docker images
|
||||||
|
|
||||||
|
on: [create]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build, push, and deploy
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
db-type: [postgresql, mysql]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set env
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: mr-smithers-excellent/docker-build-push@v5
|
||||||
|
name: Build & push Docker image for ${{ matrix.db-type }}
|
||||||
|
with:
|
||||||
|
image: umami
|
||||||
|
tags: ${{ matrix.db-type }}-${{ env.RELEASE_VERSION }}, ${{ matrix.db-type }}-latest
|
||||||
|
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
|
||||||
|
registry: ghcr.io/${{ github.actor }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
42
.github/workflows/ci.yml
vendored
Normal file
42
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||||
|
|
||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
env:
|
||||||
|
DATABASE_TYPE: postgresql
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- node-version: 12.x
|
||||||
|
db-type: postgresql
|
||||||
|
- node-version: 12.x
|
||||||
|
db-type: mysql
|
||||||
|
- node-version: 14.x
|
||||||
|
db-type: postgresql
|
||||||
|
- node-version: 14.x
|
||||||
|
db-type: mysql
|
||||||
|
- node-version: 16.x
|
||||||
|
db-type: postgresql
|
||||||
|
- node-version: 16.x
|
||||||
|
db-type: mysql
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
env:
|
||||||
|
DATABASE_TYPE: ${{ matrix.db-type }}
|
||||||
|
- run: npm install --global yarn
|
||||||
|
- run: yarn install --frozen-lockfile
|
||||||
|
- run: yarn build
|
42
.github/workflows/main.yml
vendored
42
.github/workflows/main.yml
vendored
@ -1,42 +0,0 @@
|
|||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build, push, and deploy
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Checkout master
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Build PostgreSQL container image
|
|
||||||
run: |
|
|
||||||
docker build --build-arg DATABASE_TYPE=postgresql \
|
|
||||||
--tag ghcr.io/$GITHUB_ACTOR/umami:postgresql-$(echo $GITHUB_SHA | head -c7) \
|
|
||||||
--tag ghcr.io/$GITHUB_ACTOR/umami:postgresql-latest \
|
|
||||||
.
|
|
||||||
|
|
||||||
- name: Build MySQL container image
|
|
||||||
run: |
|
|
||||||
docker build --build-arg DATABASE_TYPE=mysql \
|
|
||||||
--tag ghcr.io/$GITHUB_ACTOR/umami:mysql-$(echo $GITHUB_SHA | head -c7) \
|
|
||||||
--tag ghcr.io/$GITHUB_ACTOR/umami:mysql-latest \
|
|
||||||
.
|
|
||||||
|
|
||||||
- name: Docker login
|
|
||||||
run: >-
|
|
||||||
echo "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
| docker login -u "${{ github.actor }}" --password-stdin ghcr.io
|
|
||||||
|
|
||||||
- name: Push image to GitHub
|
|
||||||
run: |
|
|
||||||
# Push each image individually, avoiding pushing to umami:latest
|
|
||||||
# as MySQL or PostgreSQL are required
|
|
||||||
docker push ghcr.io/$GITHUB_ACTOR/umami:postgresql-$(echo $GITHUB_SHA | head -c7)
|
|
||||||
docker push ghcr.io/$GITHUB_ACTOR/umami:postgresql-latest
|
|
||||||
docker push ghcr.io/$GITHUB_ACTOR/umami:mysql-$(echo $GITHUB_SHA | head -c7)
|
|
||||||
docker push ghcr.io/$GITHUB_ACTOR/umami:mysql-latest
|
|
@ -102,6 +102,13 @@ npm install
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To update the Docker image, simply pull the new images and rebuild:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up --force-recreate
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 428 389.11">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 428 389.11" width="20" height="20">
|
||||||
<circle cx="214.15" cy="181" r="171" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="20"/><path d="M413,134.11H15.29a15,15,0,0,0-15,15v15.3C.12,168,0,171.52,0,175.11c0,118.19,95.81,214,214,214,116.4,0,211.1-92.94,213.93-208.67,0-.44.07-.88.07-1.33v-30A15,15,0,0,0,413,134.11Z"/></svg>
|
<circle cx="214.15" cy="181" r="171" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="20"/><path d="M413,134.11H15.29a15,15,0,0,0-15,15v15.3C.12,168,0,171.52,0,175.11c0,118.19,95.81,214,214,214,116.4,0,211.1-92.94,213.93-208.67,0-.44.07-.88.07-1.33v-30A15,15,0,0,0,413,134.11Z"/></svg>
|
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 401 B |
@ -30,7 +30,7 @@ export default function MetricsTable({
|
|||||||
const {
|
const {
|
||||||
resolve,
|
resolve,
|
||||||
router,
|
router,
|
||||||
query: { url },
|
query: { url, referrer },
|
||||||
} = usePageQuery();
|
} = usePageQuery();
|
||||||
|
|
||||||
const { data, loading, error } = useFetch(
|
const { data, loading, error } = useFetch(
|
||||||
@ -41,12 +41,13 @@ export default function MetricsTable({
|
|||||||
start_at: +startDate,
|
start_at: +startDate,
|
||||||
end_at: +endDate,
|
end_at: +endDate,
|
||||||
url,
|
url,
|
||||||
|
referrer,
|
||||||
},
|
},
|
||||||
onDataLoad,
|
onDataLoad,
|
||||||
delay: DEFAULT_ANIMATION_DURATION,
|
delay: DEFAULT_ANIMATION_DURATION,
|
||||||
headers: { [TOKEN_HEADER]: shareToken?.token },
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
},
|
},
|
||||||
[modified],
|
[modified, url, referrer],
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
|
@ -19,7 +19,7 @@ export default function ReferrersTable({ websiteId, websiteDomain, showFilters,
|
|||||||
const [filter, setFilter] = useState(FILTER_COMBINED);
|
const [filter, setFilter] = useState(FILTER_COMBINED);
|
||||||
const {
|
const {
|
||||||
resolve,
|
resolve,
|
||||||
query: { ref: currentRef },
|
query: { referrer: currentRef },
|
||||||
} = usePageQuery();
|
} = usePageQuery();
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
@ -37,7 +37,7 @@ export default function ReferrersTable({ websiteId, websiteDomain, showFilters,
|
|||||||
const renderLink = ({ w: link, x: label }) => {
|
const renderLink = ({ w: link, x: label }) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
<Link href={resolve({ ref: label })} replace={true}>
|
<Link href={resolve({ referrer: label })} replace={true}>
|
||||||
<a
|
<a
|
||||||
className={classNames(styles.label, {
|
className={classNames(styles.label, {
|
||||||
[styles.inactive]: currentRef && label !== currentRef,
|
[styles.inactive]: currentRef && label !== currentRef,
|
||||||
|
@ -33,7 +33,7 @@ export default function WebsiteChart({
|
|||||||
const {
|
const {
|
||||||
router,
|
router,
|
||||||
resolve,
|
resolve,
|
||||||
query: { url, ref },
|
query: { url, referrer },
|
||||||
} = usePageQuery();
|
} = usePageQuery();
|
||||||
const { get } = useApi();
|
const { get } = useApi();
|
||||||
|
|
||||||
@ -46,12 +46,12 @@ export default function WebsiteChart({
|
|||||||
unit,
|
unit,
|
||||||
tz: timezone,
|
tz: timezone,
|
||||||
url,
|
url,
|
||||||
ref,
|
referrer,
|
||||||
},
|
},
|
||||||
onDataLoad,
|
onDataLoad,
|
||||||
headers: { [TOKEN_HEADER]: shareToken?.token },
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
},
|
},
|
||||||
[modified, url, ref],
|
[modified, url, referrer],
|
||||||
);
|
);
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
@ -88,7 +88,7 @@ export default function WebsiteChart({
|
|||||||
stickyClassName={styles.sticky}
|
stickyClassName={styles.sticky}
|
||||||
enabled={stickyHeader}
|
enabled={stickyHeader}
|
||||||
>
|
>
|
||||||
<FilterTags params={{ url, ref }} onClick={handleCloseFilter} />
|
<FilterTags params={{ url, referrer }} onClick={handleCloseFilter} />
|
||||||
<div className="col-12 col-lg-9">
|
<div className="col-12 col-lg-9">
|
||||||
<MetricsBar websiteId={websiteId} />
|
<MetricsBar websiteId={websiteId} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,17 +37,18 @@ export default function AccountSettings() {
|
|||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Buttons = row =>
|
const Buttons = row => (
|
||||||
row.username !== 'admin' ? (
|
<ButtonLayout align="right">
|
||||||
<ButtonLayout align="right">
|
<Button icon={<Pen />} size="small" onClick={() => setEditAccount(row)}>
|
||||||
<Button icon={<Pen />} size="small" onClick={() => setEditAccount(row)}>
|
<FormattedMessage id="label.edit" defaultMessage="Edit" />
|
||||||
<FormattedMessage id="label.edit" defaultMessage="Edit" />
|
</Button>
|
||||||
</Button>
|
{!row.is_admin && (
|
||||||
<Button icon={<Trash />} size="small" onClick={() => setDeleteAccount(row)}>
|
<Button icon={<Trash />} size="small" onClick={() => setDeleteAccount(row)}>
|
||||||
<FormattedMessage id="label.delete" defaultMessage="Delete" />
|
<FormattedMessage id="label.delete" defaultMessage="Delete" />
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonLayout>
|
)}
|
||||||
) : null;
|
</ButtonLayout>
|
||||||
|
);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
import enUS from 'public/country/en-US.json';
|
import enUS from 'public/intl/country/en-US.json';
|
||||||
|
|
||||||
const countryNames = {
|
const countryNames = {
|
||||||
'en-US': enUS,
|
'en-US': enUS,
|
||||||
@ -12,7 +12,7 @@ export default function useCountryNames(locale) {
|
|||||||
const { basePath } = useRouter();
|
const { basePath } = useRouter();
|
||||||
|
|
||||||
async function loadData(locale) {
|
async function loadData(locale) {
|
||||||
const { ok, data } = await get(`${basePath}/country/${locale}.json`);
|
const { ok, data } = await get(`${basePath}/intl/country/${locale}.json`);
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
countryNames[locale] = data;
|
countryNames[locale] = data;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
import enUS from 'public/language/en-US.json';
|
import enUS from 'public/intl/language/en-US.json';
|
||||||
|
|
||||||
const languageNames = {
|
const languageNames = {
|
||||||
'en-US': enUS,
|
'en-US': enUS,
|
||||||
@ -12,7 +12,7 @@ export default function useLanguageNames(locale) {
|
|||||||
const { basePath } = useRouter();
|
const { basePath } = useRouter();
|
||||||
|
|
||||||
async function loadData(locale) {
|
async function loadData(locale) {
|
||||||
const { ok, data } = await get(`${basePath}/language/${locale}.json`);
|
const { ok, data } = await get(`${basePath}/intl/language/${locale}.json`);
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
languageNames[locale] = data;
|
languageNames[locale] = data;
|
||||||
|
@ -5,7 +5,7 @@ import { LOCALE_CONFIG } from 'lib/constants';
|
|||||||
import { getDateLocale, getTextDirection } from 'lib/lang';
|
import { getDateLocale, getTextDirection } from 'lib/lang';
|
||||||
import useStore, { setLocale } from 'store/app';
|
import useStore, { setLocale } from 'store/app';
|
||||||
import useForceUpdate from 'hooks/useForceUpdate';
|
import useForceUpdate from 'hooks/useForceUpdate';
|
||||||
import enUS from 'public/messages/en-US.json';
|
import enUS from 'public/intl/messages/en-US.json';
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
'en-US': enUS,
|
'en-US': enUS,
|
||||||
@ -21,7 +21,7 @@ export default function useLocale() {
|
|||||||
const dateLocale = getDateLocale(locale);
|
const dateLocale = getDateLocale(locale);
|
||||||
|
|
||||||
async function loadMessages(locale) {
|
async function loadMessages(locale) {
|
||||||
const { ok, data } = await get(`${basePath}/messages/${locale}.json`);
|
const { ok, data } = await get(`${basePath}/intl/messages/${locale}.json`);
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
messages[locale] = data;
|
messages[locale] = data;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"label.enable-share-url": "Activer le partage d'URL",
|
"label.enable-share-url": "Activer le partage d'URL",
|
||||||
"label.invalid": "Invalide",
|
"label.invalid": "Invalide",
|
||||||
"label.invalid-domain": "Domaine invalide",
|
"label.invalid-domain": "Domaine invalide",
|
||||||
"label.language": "Language",
|
"label.language": "Langage",
|
||||||
"label.last-days": "{x} derniers jours",
|
"label.last-days": "{x} derniers jours",
|
||||||
"label.last-hours": "{x} dernières heures",
|
"label.last-hours": "{x} dernières heures",
|
||||||
"label.logged-in-as": "Connecté en tant que {username}",
|
"label.logged-in-as": "Connecté en tant que {username}",
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"label.settings": "Paramètres",
|
"label.settings": "Paramètres",
|
||||||
"label.share-url": "Partager l'URL",
|
"label.share-url": "Partager l'URL",
|
||||||
"label.single-day": "Journée",
|
"label.single-day": "Journée",
|
||||||
"label.theme": "Theme",
|
"label.theme": "Thème",
|
||||||
"label.this-month": "Ce mois ci",
|
"label.this-month": "Ce mois ci",
|
||||||
"label.this-week": "Cette semaine",
|
"label.this-week": "Cette semaine",
|
||||||
"label.this-year": "Cette année",
|
"label.this-year": "Cette année",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"label.administrator": "Administrator",
|
"label.administrator": "Administrator",
|
||||||
"label.all": "Wszystkie",
|
"label.all": "Wszystkie",
|
||||||
"label.all-events": "Wszystkie wydarzenia",
|
"label.all-events": "Wszystkie wydarzenia",
|
||||||
"label.all-time": "All time",
|
"label.all-time": "Cały czas",
|
||||||
"label.all-websites": "Wszystkie witryny",
|
"label.all-websites": "Wszystkie witryny",
|
||||||
"label.back": "Powrót",
|
"label.back": "Powrót",
|
||||||
"label.cancel": "Anuluj",
|
"label.cancel": "Anuluj",
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"label.copy-to-clipboard": "Skopiuj do schowka",
|
"label.copy-to-clipboard": "Skopiuj do schowka",
|
||||||
"label.current-password": "Aktualne hasło",
|
"label.current-password": "Aktualne hasło",
|
||||||
"label.custom-range": "Zakres niestandardowy",
|
"label.custom-range": "Zakres niestandardowy",
|
||||||
"label.dashboard": "Dashboard",
|
"label.dashboard": "Panel",
|
||||||
"label.date-range": "Zakres dat",
|
"label.date-range": "Zakres dat",
|
||||||
"label.default-date-range": "Domyślny zakres dat",
|
"label.default-date-range": "Domyślny zakres dat",
|
||||||
"label.delete": "Usuń",
|
"label.delete": "Usuń",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"label.enable-share-url": "Włącz udostępnianie adresu URL",
|
"label.enable-share-url": "Włącz udostępnianie adresu URL",
|
||||||
"label.invalid": "Nieprawidłowy",
|
"label.invalid": "Nieprawidłowy",
|
||||||
"label.invalid-domain": "Nieprawidłowa witryna",
|
"label.invalid-domain": "Nieprawidłowa witryna",
|
||||||
"label.language": "Language",
|
"label.language": "Język",
|
||||||
"label.last-days": "Ostatnie {x} dni",
|
"label.last-days": "Ostatnie {x} dni",
|
||||||
"label.last-hours": "Ostatnie {x} godzin",
|
"label.last-hours": "Ostatnie {x} godzin",
|
||||||
"label.logged-in-as": "Zalogowano jako {username}",
|
"label.logged-in-as": "Zalogowano jako {username}",
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"label.more": "Więcej",
|
"label.more": "Więcej",
|
||||||
"label.name": "Nazwa",
|
"label.name": "Nazwa",
|
||||||
"label.new-password": "Nowe hasło",
|
"label.new-password": "Nowe hasło",
|
||||||
"label.owner": "Owner",
|
"label.owner": "Właściciel",
|
||||||
"label.password": "Hasło",
|
"label.password": "Hasło",
|
||||||
"label.passwords-dont-match": "Hasła się nie zgadzają",
|
"label.passwords-dont-match": "Hasła się nie zgadzają",
|
||||||
"label.profile": "Profil",
|
"label.profile": "Profil",
|
||||||
@ -46,12 +46,12 @@
|
|||||||
"label.refresh": "Odśwież",
|
"label.refresh": "Odśwież",
|
||||||
"label.required": "Wymagany",
|
"label.required": "Wymagany",
|
||||||
"label.reset": "Zresetuj",
|
"label.reset": "Zresetuj",
|
||||||
"label.reset-website": "Reset statistics",
|
"label.reset-website": "Zresetuj statystyki",
|
||||||
"label.save": "Zapisz",
|
"label.save": "Zapisz",
|
||||||
"label.settings": "Ustawienia",
|
"label.settings": "Ustawienia",
|
||||||
"label.share-url": "Udostępnij adres URL",
|
"label.share-url": "Udostępnij adres URL",
|
||||||
"label.single-day": "W tym dniu",
|
"label.single-day": "W tym dniu",
|
||||||
"label.theme": "Theme",
|
"label.theme": "Motyw",
|
||||||
"label.this-month": "W tym miesiącu",
|
"label.this-month": "W tym miesiącu",
|
||||||
"label.this-week": "W tym tygodniu",
|
"label.this-week": "W tym tygodniu",
|
||||||
"label.this-year": "W tym roku",
|
"label.this-year": "W tym roku",
|
||||||
@ -64,7 +64,7 @@
|
|||||||
"label.websites": "Witryny",
|
"label.websites": "Witryny",
|
||||||
"message.active-users": "{x} aktualnie {x, plural, one {odwiedzający} other {odwiedzających}}",
|
"message.active-users": "{x} aktualnie {x, plural, one {odwiedzający} other {odwiedzających}}",
|
||||||
"message.confirm-delete": "Czy na pewno chcesz usunąć {target}?",
|
"message.confirm-delete": "Czy na pewno chcesz usunąć {target}?",
|
||||||
"message.confirm-reset": "Are your sure you want to reset {target}'s statistics?",
|
"message.confirm-reset": "Czy na pewno chcesz zresetować statystyki {target}?",
|
||||||
"message.copied": "Skopiowano!",
|
"message.copied": "Skopiowano!",
|
||||||
"message.delete-warning": "Wszystkie powiązane dane również zostaną usunięte.",
|
"message.delete-warning": "Wszystkie powiązane dane również zostaną usunięte.",
|
||||||
"message.failure": "Coś poszło nie tak.",
|
"message.failure": "Coś poszło nie tak.",
|
||||||
@ -78,10 +78,10 @@
|
|||||||
"message.no-websites-configured": "Nie masz skonfigurowanych żadnych witryn internetowych.",
|
"message.no-websites-configured": "Nie masz skonfigurowanych żadnych witryn internetowych.",
|
||||||
"message.page-not-found": "Strona nie znaleziona.",
|
"message.page-not-found": "Strona nie znaleziona.",
|
||||||
"message.powered-by": "Obsługiwane przez {name}",
|
"message.powered-by": "Obsługiwane przez {name}",
|
||||||
"message.reset-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.",
|
"message.reset-warning": "Wszystkie statystyki tej witryny zostaną usunięte, ale kod śledzenia pozostanie nienaruszony.",
|
||||||
"message.save-success": "Zapisano pomyślnie.",
|
"message.save-success": "Zapisano pomyślnie.",
|
||||||
"message.share-url": "To jest publicznie udostępniany adres URL dla {target}.",
|
"message.share-url": "To jest publicznie udostępniany adres URL dla {target}.",
|
||||||
"message.toggle-charts": "Toggle charts",
|
"message.toggle-charts": "Przełącz wykresy",
|
||||||
"message.track-stats": "Aby śledzić statystyki dla {target}, umieść poniższy kod w sekcji {head} swojej witryny.",
|
"message.track-stats": "Aby śledzić statystyki dla {target}, umieść poniższy kod w sekcji {head} swojej witryny.",
|
||||||
"message.type-delete": "Wpisz {delete} w polu poniżej, aby potwierdzić.",
|
"message.type-delete": "Wpisz {delete} w polu poniżej, aby potwierdzić.",
|
||||||
"message.type-reset": "Wpisz {reset} w polu poniżej, aby potwierdzić.",
|
"message.type-reset": "Wpisz {reset} w polu poniżej, aby potwierdzić.",
|
||||||
@ -99,7 +99,7 @@
|
|||||||
"metrics.filter.combined": "Połączone",
|
"metrics.filter.combined": "Połączone",
|
||||||
"metrics.filter.domain-only": "Tylko domena",
|
"metrics.filter.domain-only": "Tylko domena",
|
||||||
"metrics.filter.raw": "Surowe dane",
|
"metrics.filter.raw": "Surowe dane",
|
||||||
"metrics.languages": "Languages",
|
"metrics.languages": "Języki",
|
||||||
"metrics.operating-systems": "System operacyjny",
|
"metrics.operating-systems": "System operacyjny",
|
||||||
"metrics.page-views": "Wyświetlenia strony",
|
"metrics.page-views": "Wyświetlenia strony",
|
||||||
"metrics.pages": "Strony",
|
"metrics.pages": "Strony",
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
"label.add-website": "添加网站",
|
"label.add-website": "添加网站",
|
||||||
"label.administrator": "管理员",
|
"label.administrator": "管理员",
|
||||||
"label.all": "所有",
|
"label.all": "所有",
|
||||||
"label.all-events": "All events",
|
"label.all-events": "所有事件",
|
||||||
"label.all-time": "All time",
|
"label.all-time": "所有时间段",
|
||||||
"label.all-websites": "全部网站",
|
"label.all-websites": "全部网站",
|
||||||
"label.back": "返回",
|
"label.back": "返回",
|
||||||
"label.cancel": "取消",
|
"label.cancel": "取消",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"label.enable-share-url": "启用共享链接",
|
"label.enable-share-url": "启用共享链接",
|
||||||
"label.invalid": "输入无效",
|
"label.invalid": "输入无效",
|
||||||
"label.invalid-domain": "无效域名",
|
"label.invalid-domain": "无效域名",
|
||||||
"label.language": "Language",
|
"label.language": "语言",
|
||||||
"label.last-days": "最近 {x} 天",
|
"label.last-days": "最近 {x} 天",
|
||||||
"label.last-hours": "最近 {x} 小时",
|
"label.last-hours": "最近 {x} 小时",
|
||||||
"label.logged-in-as": "登录名: {username}",
|
"label.logged-in-as": "登录名: {username}",
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"label.more": "更多",
|
"label.more": "更多",
|
||||||
"label.name": "名字",
|
"label.name": "名字",
|
||||||
"label.new-password": "新密码",
|
"label.new-password": "新密码",
|
||||||
"label.owner": "Owner",
|
"label.owner": "所有者",
|
||||||
"label.password": "密码",
|
"label.password": "密码",
|
||||||
"label.passwords-dont-match": "密码不一致",
|
"label.passwords-dont-match": "密码不一致",
|
||||||
"label.profile": "个人资料",
|
"label.profile": "个人资料",
|
||||||
@ -46,12 +46,12 @@
|
|||||||
"label.refresh": "刷新",
|
"label.refresh": "刷新",
|
||||||
"label.required": "必填",
|
"label.required": "必填",
|
||||||
"label.reset": "重置",
|
"label.reset": "重置",
|
||||||
"label.reset-website": "Reset statistics",
|
"label.reset-website": "重置统计数据",
|
||||||
"label.save": "保存",
|
"label.save": "保存",
|
||||||
"label.settings": "设置",
|
"label.settings": "设置",
|
||||||
"label.share-url": "共享链接",
|
"label.share-url": "共享链接",
|
||||||
"label.single-day": "单日",
|
"label.single-day": "单日",
|
||||||
"label.theme": "Theme",
|
"label.theme": "主题",
|
||||||
"label.this-month": "本月",
|
"label.this-month": "本月",
|
||||||
"label.this-week": "本周",
|
"label.this-week": "本周",
|
||||||
"label.this-year": "今年",
|
"label.this-year": "今年",
|
||||||
@ -64,7 +64,7 @@
|
|||||||
"label.websites": "网站",
|
"label.websites": "网站",
|
||||||
"message.active-users": "当前在线 {x} 人",
|
"message.active-users": "当前在线 {x} 人",
|
||||||
"message.confirm-delete": "你确定要删除 {target} 吗?",
|
"message.confirm-delete": "你确定要删除 {target} 吗?",
|
||||||
"message.confirm-reset": "Are your sure you want to reset {target}'s statistics?",
|
"message.confirm-reset": "您确定要重置 {target} 的数据吗?",
|
||||||
"message.copied": "复制成功!",
|
"message.copied": "复制成功!",
|
||||||
"message.delete-warning": "所有相关数据将会被删除。",
|
"message.delete-warning": "所有相关数据将会被删除。",
|
||||||
"message.failure": "出现错误。",
|
"message.failure": "出现错误。",
|
||||||
@ -78,10 +78,10 @@
|
|||||||
"message.no-websites-configured": "你还没有设置任何网站。",
|
"message.no-websites-configured": "你还没有设置任何网站。",
|
||||||
"message.page-not-found": "网页未找到。",
|
"message.page-not-found": "网页未找到。",
|
||||||
"message.powered-by": "由 {name} 提供支持",
|
"message.powered-by": "由 {name} 提供支持",
|
||||||
"message.reset-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.",
|
"message.reset-warning": "本网站的所有统计数据将被删除,但您的跟踪代码将保持不变。",
|
||||||
"message.save-success": "保存成功。",
|
"message.save-success": "保存成功。",
|
||||||
"message.share-url": "这是 {target} 的共享链接。",
|
"message.share-url": "这是 {target} 的共享链接。",
|
||||||
"message.toggle-charts": "Toggle charts",
|
"message.toggle-charts": "切换图表",
|
||||||
"message.track-stats": "把以下代码放到你的网站的 {head} 部分来收集 {target} 的数据。",
|
"message.track-stats": "把以下代码放到你的网站的 {head} 部分来收集 {target} 的数据。",
|
||||||
"message.type-delete": "在下方输入框输入 {delete} 以确认删除。",
|
"message.type-delete": "在下方输入框输入 {delete} 以确认删除。",
|
||||||
"message.type-reset": "在下方输入框输入 {reset} 以确认删除。",
|
"message.type-reset": "在下方输入框输入 {reset} 以确认删除。",
|
||||||
@ -99,7 +99,7 @@
|
|||||||
"metrics.filter.combined": "总和",
|
"metrics.filter.combined": "总和",
|
||||||
"metrics.filter.domain-only": "只看域名",
|
"metrics.filter.domain-only": "只看域名",
|
||||||
"metrics.filter.raw": "原始",
|
"metrics.filter.raw": "原始",
|
||||||
"metrics.languages": "Languages",
|
"metrics.languages": "语言",
|
||||||
"metrics.operating-systems": "操作系统",
|
"metrics.operating-systems": "操作系统",
|
||||||
"metrics.page-views": "页面浏览量",
|
"metrics.page-views": "页面浏览量",
|
||||||
"metrics.pages": "网页",
|
"metrics.pages": "网页",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BROWSERS } from './constants';
|
import { BROWSERS } from './constants';
|
||||||
import { removeTrailingSlash, removeWWW } from './url';
|
import { removeTrailingSlash, removeWWW, getDomainName } from './url';
|
||||||
|
|
||||||
export const urlFilter = (data, { raw }) => {
|
export const urlFilter = (data, { raw }) => {
|
||||||
const isValidUrl = url => {
|
const isValidUrl = url => {
|
||||||
@ -46,11 +46,14 @@ export const urlFilter = (data, { raw }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const refFilter = (data, { domain, domainOnly, raw }) => {
|
export const refFilter = (data, { domain, domainOnly, raw }) => {
|
||||||
const regex = new RegExp(`http[s]?://([a-z0-9-]+\\.)*${domain}`);
|
const domainName = getDomainName(domain);
|
||||||
|
const regex = new RegExp(`http[s]?://([a-z0-9-]+\\.)*${domainName}`);
|
||||||
const links = {};
|
const links = {};
|
||||||
|
|
||||||
const isValidRef = ref => {
|
const isValidRef = referrer => {
|
||||||
return ref !== '' && ref !== null && !ref.startsWith('/') && !ref.startsWith('#');
|
return (
|
||||||
|
referrer !== '' && referrer !== null && !referrer.startsWith('/') && !referrer.startsWith('#')
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanUrl = url => {
|
const cleanUrl = url => {
|
||||||
@ -71,8 +74,8 @@ export const refFilter = (data, { domain, domainOnly, raw }) => {
|
|||||||
|
|
||||||
if (protocol.startsWith('http')) {
|
if (protocol.startsWith('http')) {
|
||||||
const path = removeTrailingSlash(pathname);
|
const path = removeTrailingSlash(pathname);
|
||||||
const ref = searchParams.get('ref');
|
const referrer = searchParams.get('referrer');
|
||||||
const query = ref ? `?ref=${ref}` : '';
|
const query = referrer ? `?referrer=${referrer}` : '';
|
||||||
|
|
||||||
return removeTrailingSlash(`${removeWWW(hostname)}${path}`) + query;
|
return removeTrailingSlash(`${removeWWW(hostname)}${path}`) + query;
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,16 @@ export async function saveEvent(website_id, session_id, url, event_type, event_v
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getAccounts() {
|
export async function getAccounts() {
|
||||||
return runQuery(prisma.account.findMany());
|
return runQuery(
|
||||||
|
prisma.account.findMany({
|
||||||
|
orderBy: [
|
||||||
|
{ is_admin: 'desc' },
|
||||||
|
{
|
||||||
|
username: 'asc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAccountById(user_id) {
|
export async function getAccountById(user_id) {
|
||||||
@ -335,7 +344,7 @@ export async function getEvents(websites, start_at) {
|
|||||||
|
|
||||||
export function getWebsiteStats(website_id, start_at, end_at, filters = {}) {
|
export function getWebsiteStats(website_id, start_at, end_at, filters = {}) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
const { url, ref } = filters;
|
const { url, referrer } = filters;
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
let refFilter = '';
|
let refFilter = '';
|
||||||
|
|
||||||
@ -344,9 +353,9 @@ export function getWebsiteStats(website_id, start_at, end_at, filters = {}) {
|
|||||||
params.push(decodeURIComponent(url));
|
params.push(decodeURIComponent(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref) {
|
if (referrer) {
|
||||||
refFilter = `and referrer like $${params.length + 1}`;
|
refFilter = `and referrer like $${params.length + 1}`;
|
||||||
params.push(`%${decodeURIComponent(ref)}%`);
|
params.push(`%${decodeURIComponent(referrer)}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
@ -382,7 +391,7 @@ export function getPageviewStats(
|
|||||||
filters = {},
|
filters = {},
|
||||||
) {
|
) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
const { url, ref } = filters;
|
const { url, referrer } = filters;
|
||||||
|
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
let refFilter = '';
|
let refFilter = '';
|
||||||
@ -392,9 +401,9 @@ export function getPageviewStats(
|
|||||||
params.push(decodeURIComponent(url));
|
params.push(decodeURIComponent(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref) {
|
if (referrer) {
|
||||||
refFilter = `and referrer like $${params.length + 1}`;
|
refFilter = `and referrer like $${params.length + 1}`;
|
||||||
params.push(`%${decodeURIComponent(ref)}%`);
|
params.push(`%${decodeURIComponent(referrer)}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
@ -444,10 +453,11 @@ export function getSessionMetrics(website_id, start_at, end_at, field, filters =
|
|||||||
|
|
||||||
export function getPageviewMetrics(website_id, start_at, end_at, field, table, filters = {}) {
|
export function getPageviewMetrics(website_id, start_at, end_at, field, table, filters = {}) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
const { domain, url } = filters;
|
const { domain, url, referrer } = filters;
|
||||||
|
|
||||||
let domainFilter = '';
|
let domainFilter = '';
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
|
let refFilter = '';
|
||||||
|
|
||||||
if (domain) {
|
if (domain) {
|
||||||
domainFilter = `and referrer not like $${params.length + 1} and referrer not like '/%'`;
|
domainFilter = `and referrer not like $${params.length + 1} and referrer not like '/%'`;
|
||||||
@ -459,6 +469,11 @@ export function getPageviewMetrics(website_id, start_at, end_at, field, table, f
|
|||||||
params.push(decodeURIComponent(url));
|
params.push(decodeURIComponent(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (referrer) {
|
||||||
|
refFilter = `and referrer like $${params.length + 1}`;
|
||||||
|
params.push(`%${decodeURIComponent(referrer)}%`);
|
||||||
|
}
|
||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
`
|
`
|
||||||
select ${field} x, count(*) y
|
select ${field} x, count(*) y
|
||||||
@ -467,6 +482,7 @@ export function getPageviewMetrics(website_id, start_at, end_at, field, table, f
|
|||||||
and created_at between $2 and $3
|
and created_at between $2 and $3
|
||||||
${domainFilter}
|
${domainFilter}
|
||||||
${urlFilter}
|
${urlFilter}
|
||||||
|
${refFilter}
|
||||||
group by 1
|
group by 1
|
||||||
order by 2 desc
|
order by 2 desc
|
||||||
`,
|
`,
|
||||||
|
@ -6,7 +6,9 @@ export function json(res, data = {}) {
|
|||||||
return res.status(200).json(data);
|
return res.status(200).json(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function send(res, data) {
|
export function send(res, data, type = 'text/plain') {
|
||||||
|
res.setHeader('Content-Type', type);
|
||||||
|
|
||||||
return res.status(200).send(data);
|
return res.status(200).send(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ export async function getSession(req) {
|
|||||||
throw new Error('Invalid request');
|
throw new Error('Invalid request');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { website: website_uuid, hostname, screen, language, cache } = payload;
|
const { website: website_uuid, hostname, screen, language } = payload;
|
||||||
|
const cache = req.headers['x-umami-cache'];
|
||||||
|
|
||||||
if (cache) {
|
if (cache) {
|
||||||
const result = await parseToken(cache);
|
const result = await parseToken(cache);
|
||||||
|
35
package.json
35
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "1.28.0",
|
"version": "1.29.0",
|
||||||
"description": "A simple, fast, website analytics alternative to Google Analytics.",
|
"description": "A simple, fast, website analytics alternative to Google Analytics.",
|
||||||
"author": "Mike Cao <mike@mikecao.com>",
|
"author": "Mike Cao <mike@mikecao.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -12,7 +12,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "npm-run-all build-tracker build-geo build-db build-app",
|
"build": "npm-run-all build-tracker build-geo build-db build-app",
|
||||||
"start": "next start",
|
"start": "npm-run-all init start-app",
|
||||||
|
"start-app": "next start",
|
||||||
"start-env": "node -r dotenv/config scripts/start-env.js",
|
"start-env": "node -r dotenv/config scripts/start-env.js",
|
||||||
"build-app": "next build",
|
"build-app": "next build",
|
||||||
"build-tracker": "rollup -c rollup.tracker.config.js",
|
"build-tracker": "rollup -c rollup.tracker.config.js",
|
||||||
@ -25,19 +26,20 @@
|
|||||||
"build-mysql-client": "dotenv prisma generate -- --schema=./prisma/schema.mysql.prisma",
|
"build-mysql-client": "dotenv prisma generate -- --schema=./prisma/schema.mysql.prisma",
|
||||||
"build-postgresql-schema": "dotenv prisma db pull -- --schema=./prisma/schema.postgresql.prisma",
|
"build-postgresql-schema": "dotenv prisma db pull -- --schema=./prisma/schema.postgresql.prisma",
|
||||||
"build-postgresql-client": "dotenv prisma generate -- --schema=./prisma/schema.postgresql.prisma",
|
"build-postgresql-client": "dotenv prisma generate -- --schema=./prisma/schema.postgresql.prisma",
|
||||||
"postbuild": "node scripts/postbuild.js",
|
|
||||||
"copy-db-schema": "node scripts/copy-db-schema.js",
|
"copy-db-schema": "node scripts/copy-db-schema.js",
|
||||||
"generate-lang": "npm-run-all extract-lang merge-lang",
|
"generate-lang": "npm-run-all extract-lang merge-lang",
|
||||||
"extract-lang": "formatjs extract \"{pages,components}/**/*.js\" --out-file build/messages.json",
|
"extract-lang": "formatjs extract \"{pages,components}/**/*.js\" --out-file build/messages.json",
|
||||||
"merge-lang": "node scripts/merge-lang.js",
|
"merge-lang": "node scripts/merge-lang.js",
|
||||||
"format-lang": "node scripts/format-lang.js",
|
"format-lang": "node scripts/format-lang.js",
|
||||||
"compile-lang": "formatjs compile-folder --ast build public/messages",
|
"compile-lang": "formatjs compile-folder --ast build public/intl/messages",
|
||||||
"check-lang": "node scripts/check-lang.js",
|
"check-lang": "node scripts/check-lang.js",
|
||||||
"download-country-names": "node scripts/download-country-names.js",
|
"download-country-names": "node scripts/download-country-names.js",
|
||||||
"download-language-names": "node scripts/download-language-names.js",
|
"download-language-names": "node scripts/download-language-names.js",
|
||||||
"change-password": "node scripts/change-password.js",
|
"change-password": "node scripts/change-password.js",
|
||||||
"lint": "next lint --quiet",
|
"lint": "next lint --quiet",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install",
|
||||||
|
"postbuild": "node scripts/postbuild.js",
|
||||||
|
"init": "node scripts/prestart.js"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"**/*.js": [
|
"**/*.js": [
|
||||||
@ -54,8 +56,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "4.5.5",
|
"@fontsource/inter": "4.5.5",
|
||||||
"@prisma/client": "3.11.0",
|
"@prisma/client": "3.11.1",
|
||||||
"async-retry": "^1.3.3",
|
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
@ -70,19 +71,16 @@
|
|||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
"immer": "^9.0.12",
|
"immer": "^9.0.12",
|
||||||
"ipaddr.js": "^2.0.1",
|
"ipaddr.js": "^2.0.1",
|
||||||
"is-ci": "^3.0.1",
|
|
||||||
"is-docker": "^3.0.0",
|
|
||||||
"is-localhost-ip": "^1.4.0",
|
"is-localhost-ip": "^1.4.0",
|
||||||
"isbot": "^3.4.5",
|
"isbot": "^3.4.5",
|
||||||
"jose": "2.0.5",
|
"jose": "2.0.5",
|
||||||
"maxmind": "^4.3.6",
|
"maxmind": "^4.3.6",
|
||||||
"moment-timezone": "^0.5.33",
|
"moment-timezone": "^0.5.33",
|
||||||
"next": "12.1.0",
|
"next": "12.1.4",
|
||||||
"node-fetch": "^3.2.3",
|
"node-fetch": "^3.2.3",
|
||||||
"prompts": "2.4.2",
|
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.2",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.0.0",
|
||||||
"react-intl": "^5.24.7",
|
"react-intl": "^5.24.7",
|
||||||
"react-simple-maps": "^2.3.0",
|
"react-simple-maps": "^2.3.0",
|
||||||
"react-spring": "^9.4.4",
|
"react-spring": "^9.4.4",
|
||||||
@ -100,17 +98,18 @@
|
|||||||
"@formatjs/cli": "^4.2.29",
|
"@formatjs/cli": "^4.2.29",
|
||||||
"@rollup/plugin-buble": "^0.21.3",
|
"@rollup/plugin-buble": "^0.21.3",
|
||||||
"@svgr/webpack": "^6.2.1",
|
"@svgr/webpack": "^6.2.1",
|
||||||
|
"async-retry": "^1.3.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"del": "^6.0.0",
|
"del": "^6.0.0",
|
||||||
"dotenv-cli": "^4.0.0",
|
"dotenv-cli": "^4.0.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-next": "^12.0.1",
|
"eslint-config-next": "^12.0.1",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-react": "^7.29.4",
|
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
|
||||||
"extract-react-intl-messages": "^4.1.1",
|
"extract-react-intl-messages": "^4.1.1",
|
||||||
"husky": "^7.0.0",
|
"husky": "^7.0.0",
|
||||||
|
"is-ci": "^3.0.1",
|
||||||
|
"is-docker": "^3.0.0",
|
||||||
"lint-staged": "^11.0.0",
|
"lint-staged": "^11.0.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.12",
|
"postcss": "^8.4.12",
|
||||||
@ -119,8 +118,8 @@
|
|||||||
"postcss-preset-env": "^7.4.2",
|
"postcss-preset-env": "^7.4.2",
|
||||||
"postcss-rtlcss": "^3.5.3",
|
"postcss-rtlcss": "^3.5.3",
|
||||||
"prettier": "^2.6.0",
|
"prettier": "^2.6.0",
|
||||||
"prettier-eslint": "^13.0.0",
|
"prisma": "3.11.1",
|
||||||
"prisma": "3.11.0",
|
"prompts": "2.4.2",
|
||||||
"rollup": "^2.70.1",
|
"rollup": "^2.70.1",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"stylelint": "^14.5.3",
|
"stylelint": "^14.5.3",
|
||||||
|
@ -23,10 +23,7 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
// Only admin can change these fields
|
// Only admin can change these fields
|
||||||
if (current_user_is_admin) {
|
if (current_user_is_admin) {
|
||||||
// Cannot change username of admin
|
data.username = username;
|
||||||
if (username !== 'admin') {
|
|
||||||
data.username = username;
|
|
||||||
}
|
|
||||||
data.is_admin = is_admin;
|
data.is_admin = is_admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +34,7 @@ export default async (req, res) => {
|
|||||||
return badRequest(res, 'Account already exists');
|
return badRequest(res, 'Account already exists');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log('------------------\n', data);
|
||||||
const updated = await updateAccount(user_id, data);
|
const updated = await updateAccount(user_id, data);
|
||||||
|
|
||||||
return ok(res, updated);
|
return ok(res, updated);
|
||||||
|
@ -30,7 +30,7 @@ export default async (req, res) => {
|
|||||||
return unauthorized(res);
|
return unauthorized(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, type, start_at, end_at, url } = req.query;
|
const { id, type, start_at, end_at, url, referrer } = req.query;
|
||||||
|
|
||||||
const websiteId = +id;
|
const websiteId = +id;
|
||||||
const startDate = new Date(+start_at);
|
const startDate = new Date(+start_at);
|
||||||
@ -75,6 +75,7 @@ export default async (req, res) => {
|
|||||||
{
|
{
|
||||||
domain,
|
domain,
|
||||||
url: type !== 'url' && url,
|
url: type !== 'url' && url,
|
||||||
|
referrer,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export default async (req, res) => {
|
|||||||
return unauthorized(res);
|
return unauthorized(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, start_at, end_at, unit, tz, url, ref } = req.query;
|
const { id, start_at, end_at, unit, tz, url, referrer } = req.query;
|
||||||
|
|
||||||
const websiteId = +id;
|
const websiteId = +id;
|
||||||
const startDate = new Date(+start_at);
|
const startDate = new Date(+start_at);
|
||||||
@ -22,10 +22,10 @@ export default async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [pageviews, sessions] = await Promise.all([
|
const [pageviews, sessions] = await Promise.all([
|
||||||
getPageviewStats(websiteId, startDate, endDate, tz, unit, '*', { url, ref }),
|
getPageviewStats(websiteId, startDate, endDate, tz, unit, '*', { url, referrer }),
|
||||||
getPageviewStats(websiteId, startDate, endDate, tz, unit, 'distinct session_id', {
|
getPageviewStats(websiteId, startDate, endDate, tz, unit, 'distinct session_id', {
|
||||||
url,
|
url,
|
||||||
ref,
|
referrer,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ export default async (req, res) => {
|
|||||||
return unauthorized(res);
|
return unauthorized(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, start_at, end_at, url, ref } = req.query;
|
const { id, start_at, end_at, url, referrer } = req.query;
|
||||||
|
|
||||||
const websiteId = +id;
|
const websiteId = +id;
|
||||||
const startDate = new Date(+start_at);
|
const startDate = new Date(+start_at);
|
||||||
@ -18,8 +18,11 @@ export default async (req, res) => {
|
|||||||
const prevStartDate = new Date(+start_at - distance);
|
const prevStartDate = new Date(+start_at - distance);
|
||||||
const prevEndDate = new Date(+end_at - distance);
|
const prevEndDate = new Date(+end_at - distance);
|
||||||
|
|
||||||
const metrics = await getWebsiteStats(websiteId, startDate, endDate, { url, ref });
|
const metrics = await getWebsiteStats(websiteId, startDate, endDate, { url, referrer });
|
||||||
const prevPeriod = await getWebsiteStats(websiteId, prevStartDate, prevEndDate, { url, ref });
|
const prevPeriod = await getWebsiteStats(websiteId, prevStartDate, prevEndDate, {
|
||||||
|
url,
|
||||||
|
referrer,
|
||||||
|
});
|
||||||
|
|
||||||
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
|
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
|
||||||
obj[key] = {
|
obj[key] = {
|
||||||
|
@ -1 +0,0 @@
|
|||||||
../seed.js
|
|
@ -1 +0,0 @@
|
|||||||
../seed.js
|
|
@ -8,13 +8,12 @@ const hashPassword = password => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const password = hashPassword(process.env.ADMIN_PASSWORD || 'umami');
|
|
||||||
await prisma.account.upsert({
|
await prisma.account.upsert({
|
||||||
where: { username: 'admin' },
|
where: { username: 'admin' },
|
||||||
update: {},
|
update: {},
|
||||||
create: {
|
create: {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: password,
|
password: hashPassword(process.env.ADMIN_PASSWORD || 'umami'),
|
||||||
is_admin: true,
|
is_admin: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user