1
0
mirror of https://github.com/kremalicious/umami.git synced 2025-02-14 21:10:34 +01:00

Merge pull request from mikecao/dev

v1.33
This commit is contained in:
Mike Cao 2022-06-27 02:20:22 -07:00 committed by GitHub
commit 4246d4fa50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1337 additions and 326 deletions

36
.github/workflows/cd-manual.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Create docker images
on:
workflow_dispatch:
inputs:
version:
type: string
description: Version
required: true
add-latest:
type: boolean
description: Add latest tag
required: false
jobs:
build:
name: Build, push, and deploy
runs-on: ubuntu-latest
strategy:
matrix:
db-type: [postgresql, mysql]
steps:
- uses: actions/checkout@v2
- uses: mr-smithers-excellent/docker-build-push@v5
name: Build & push Docker image for ${{ matrix.db-type }}
with:
image: umami
tags: ${{ matrix.db-type }}-${{ inputs.version }}
addLatest: ${{ inputs.add-latest }}
buildArgs: DATABASE_TYPE=${{ matrix.db-type }}
registry: ghcr.io/${{ github.actor }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,16 +1,12 @@
name: Create docker images
on:
workflow_dispatch:
inputs:
version:
type: string
description: Version
required: true
on: [create]
jobs:
build:
name: Build, push, and deploy
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: ubuntu-latest
strategy:
@ -20,11 +16,14 @@ jobs:
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 }}-${{ inputs.version }}
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 }}

2
.gitignore vendored
View File

@ -11,7 +11,7 @@
# next.js
/.next/
/out/
/prisma/schema.prisma
/prisma/
# production
/build

View File

@ -8,18 +8,20 @@ RUN yarn install --frozen-lockfile
# Rebuild the source code only when needed
FROM node:16-alpine AS builder
ARG BASE_PATH
ARG DATABASE_TYPE
ENV BASE_PATH=$BASE_PATH
ENV DATABASE_URL "postgresql://umami:umami@db:5432/umami"
ENV DATABASE_TYPE=$DATABASE_TYPE
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
ARG DATABASE_URL
ARG DATABASE_TYPE
ARG BASE_PATH
ARG DISABLE_LOGIN
ENV DATABASE_URL $DATABASE_URL
ENV DATABASE_TYPE $DATABASE_TYPE
ENV BASE_PATH $BASE_PATH
ENV DISABLE_LOGIN $DISABLE_LOGIN
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
@ -29,16 +31,19 @@ FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
RUN yarn global add prisma
# You only need to copy next.config.js if you are NOT using the default configuration
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/prisma/schema.prisma ./prisma/schema.prisma
COPY --from=builder /app/prisma/migrations ./prisma/migrations
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
@ -51,4 +56,4 @@ EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
CMD ["yarn", "start-docker"]

View File

@ -16,10 +16,10 @@ See [Running on Railway](https://umami.is/docs/running-on-railway) to get starte
### Requirements
- A server with Node.js 12 or newer
- A database (MySQL or Postgresql)
- A server with Node.js version 12 or newer
- A database. Umami supports [MySQL](https://www.mysql.com/) and [Postgresql](https://www.postgresql.org/) databases.
### Install Yarn (if needed)
### Install Yarn
```
npm install -g yarn
@ -33,32 +33,12 @@ cd umami
yarn install
```
### Create database tables
Umami supports [MySQL](https://www.mysql.com/) and [Postgresql](https://www.postgresql.org/).
Create a database for your Umami installation and install the tables with the included scripts.
For MySQL:
```
mysql -u username -p databasename < sql/schema.mysql.sql
```
For Postgresql:
```
psql -h hostname -U username -d databasename -f sql/schema.postgresql.sql
```
This will also create a login account with username **admin** and password **umami**.
### Configure umami
Create an `.env` file with the following
```
DATABASE_URL=(connection url)
HASH_SALT=(any random string)
```
The connection url is in the following format:
@ -68,7 +48,7 @@ postgresql://username:mypassword@localhost:5432/mydb
mysql://username:mypassword@localhost:3306/mydb
```
The `HASH_SALT` is used to generate unique values for your installation.
This will also create a login account with username **admin** and password **umami**.
### Build the application
@ -76,13 +56,19 @@ The `HASH_SALT` is used to generate unique values for your installation.
yarn build
```
### Create database tables
```bash
yarn update-db
```
### Start the application
```bash
yarn start
```
By default this will launch the application on `http://localhost:3000`. You will need to either
By default this will launch the application on `http://localhost:3000`. You will need to either
[proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) requests from your web server
or change the [port](https://nextjs.org/docs/api-reference/cli#production) to serve the application directly.
@ -112,6 +98,7 @@ To get the latest features, simply do a pull, install any new dependencies, and
git pull
yarn install
yarn build
yarn update-db
```
To update the Docker image, simply pull the new images and rebuild:

View File

@ -1,26 +1,16 @@
{
"name": "Umami",
"description": "Umami is a simple, fast, website analytics alternative to Google Analytics.",
"keywords": [
"analytics",
"charts",
"statistics",
"web-analytics"
],
"website": "https://umami.is",
"repository": "https://github.com/mikecao/umami",
"addons": [
"heroku-postgresql"
],
"env": {
"HASH_SALT": {
"description": "Used to generate unique values for your installation",
"required": true,
"generator": "secret"
}
},
"scripts": {
"postdeploy": "psql $DATABASE_URL -f sql/schema.postgresql.sql"
},
"success_url": "/"
"name": "Umami",
"description": "Umami is a simple, fast, website analytics alternative to Google Analytics.",
"keywords": ["analytics", "charts", "statistics", "web-analytics"],
"website": "https://umami.is",
"repository": "https://github.com/mikecao/umami",
"addons": ["heroku-postgresql"],
"env": {
"HASH_SALT": {
"description": "Used to generate unique values for your installation",
"required": true,
"generator": "secret"
}
},
"success_url": "/"
}

View File

@ -2,6 +2,7 @@ import React from 'react';
import Link from 'next/link';
import classNames from 'classnames';
import usePageQuery from 'hooks/usePageQuery';
import { safeDecodeURI } from 'lib/url';
import Icon from './Icon';
import External from 'assets/arrow-up-right-from-square.svg';
import styles from './FilterLink.module.css';
@ -20,7 +21,7 @@ export default function FilterLink({ id, value, label, externalUrl }) {
[styles.active]: active && selected,
})}
>
{label || value}
{safeDecodeURI(label || value)}
</a>
</Link>
{externalUrl && (

View File

@ -1,27 +1,38 @@
import React from 'react';
import { useState, useEffect, useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import useVersion from 'hooks/useVersion';
import styles from './UpdateNotice.module.css';
import ButtonLayout from '../layout/ButtonLayout';
import ButtonLayout from 'components/layout/ButtonLayout';
import useStore, { checkVersion } from 'store/version';
import { setItem } from 'lib/web';
import { VERSION_CHECK, VERSION_URL } from 'lib/constants';
import Button from './Button';
import useForceUpdate from '../../hooks/useForceUpdate';
import styles from './UpdateNotice.module.css';
export default function UpdateNotice() {
const forceUpdate = useForceUpdate();
const { hasUpdate, checked, latest, updateCheck } = useVersion(true);
const { latest, checked, hasUpdate } = useStore();
const [dismissed, setDismissed] = useState(false);
const updateCheck = useCallback(() => {
setItem(VERSION_CHECK, { version: latest, time: Date.now() });
}, [latest]);
function handleViewClick() {
location.href = 'https://github.com/mikecao/umami/releases';
updateCheck();
forceUpdate();
setDismissed(true);
location.href = VERSION_URL;
}
function handleDismissClick() {
updateCheck();
forceUpdate();
setDismissed(true);
}
if (!hasUpdate || checked) {
useEffect(() => {
if (!checked) {
checkVersion();
}
}, []);
if (!hasUpdate || dismissed) {
return null;
}

View File

@ -3,11 +3,11 @@ import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import Link from 'components/common/Link';
import styles from './Footer.module.css';
import useVersion from 'hooks/useVersion';
import useStore from 'store/version';
import { HOMEPAGE_URL, VERSION_URL } from 'lib/constants';
export default function Footer() {
const { current } = useVersion();
const { current } = useStore();
return (
<footer className={classNames(styles.footer, 'row')}>

View File

@ -19,7 +19,7 @@ export default function Header() {
return (
<>
{user?.is_admin && <UpdateNotice />}
{user?.is_admin && !process.env.updatesDisabled && <UpdateNotice />}
<header className={classNames(styles.header, 'row')}>
<div className={styles.title}>
<Icon icon={<Logo />} size="large" className={styles.logo} />

View File

@ -1,14 +1,21 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { percentFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
import { useIntl, defineMessages } from 'react-intl';
import FilterLink from 'components/common/FilterLink';
import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale';
const messages = defineMessages({
unknown: { id: 'label.unknown', defaultMessage: 'Unknown' },
countries: { id: 'metrics.countries', defaultMessage: 'Countries' },
visitors: { id: 'metrics.visitors', defaultMessage: 'Visitors' },
});
export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
const { formatMessage } = useIntl();
function renderLink({ x: code }) {
return (
@ -16,9 +23,7 @@ export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
<FilterLink
id="country"
value={code}
label={
countryNames[code] ?? <FormattedMessage id="label.unknown" defaultMessage="Unknown" />
}
label={countryNames[code] ?? formatMessage(messages.unknown)}
/>
</div>
);
@ -27,9 +32,9 @@ export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
return (
<MetricsTable
{...props}
title={<FormattedMessage id="metrics.countries" defaultMessage="Countries" />}
title={formatMessage(messages.countries)}
type="country"
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
metric={formatMessage(messages.visitors)}
websiteId={websiteId}
onDataLoad={data => onDataLoad?.(percentFilter(data))}
renderLabel={renderLink}

View File

@ -97,3 +97,6 @@ ALTER TABLE `session` ADD FOREIGN KEY (`website_id`) REFERENCES `website`(`websi
-- AddForeignKey
ALTER TABLE `website` ADD FOREIGN KEY (`user_id`) REFERENCES `account`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- CreateAdminUser
INSERT INTO account (username, password, is_admin) values ('admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa', true);

View File

@ -127,3 +127,6 @@ ALTER TABLE "session" ADD FOREIGN KEY ("website_id") REFERENCES "website"("websi
-- AddForeignKey
ALTER TABLE "website" ADD FOREIGN KEY ("user_id") REFERENCES "account"("user_id") ON DELETE CASCADE ON UPDATE CASCADE;
-- CreateAdminUser
INSERT INTO account (username, password, is_admin) values ('admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa', true);

View File

@ -1,21 +0,0 @@
import { useEffect, useCallback } from 'react';
import useStore, { checkVersion } from 'store/version';
import { VERSION_CHECK } from 'lib/constants';
import { getItem, setItem } from 'lib/web';
export default function useVersion(check) {
const versions = useStore();
const checked = versions.latest === getItem(VERSION_CHECK)?.version;
const updateCheck = useCallback(() => {
setItem(VERSION_CHECK, { version: versions.latest, time: Date.now() });
}, [versions]);
useEffect(() => {
if (check && !versions.latest) {
checkVersion();
}
}, [versions, check]);
return { ...versions, checked, updateCheck };
}

View File

@ -5,7 +5,7 @@
"label.administrator": "مدیر",
"label.all": "همه",
"label.all-events": "همه‌ی رویدادها",
"label.all-time": "All time",
"label.all-time": "همه زمان",
"label.all-websites": "همه‌ی وب‌سایت‌ها",
"label.back": "برگشت",
"label.cancel": "انصراف",
@ -28,16 +28,16 @@
"label.enable-share-url": "فعال کردن اشتراک گذاری URL",
"label.invalid": "نامعتبر",
"label.invalid-domain": "دامنه‌ی نامعتبر",
"label.language": "Language",
"label.last-days": "لیست {x} روز",
"label.last-hours": "لیست {x} ساعت",
"label.language": "زبان",
"label.last-days": "لیست {x} روز گذشته",
"label.last-hours": "لیست {x} ساعت گذشته",
"label.logged-in-as": "وارد شده به عنوان {username}",
"label.login": "ورود",
"label.logout": "خروج",
"label.more": "بیشتر",
"label.name": "نام",
"label.new-password": "رمز جدید",
"label.owner": "Owner",
"label.owner": "ایجاد شده توسط",
"label.password": "رمز",
"label.passwords-dont-match": "رمزها یکسان نیستند",
"label.profile": "پروفایل",
@ -46,12 +46,12 @@
"label.refresh": "به‌روزرسانی",
"label.required": "ضروری",
"label.reset": "بازنشانی",
"label.reset-website": "Reset statistics",
"label.reset-website": "بازنشانی آمار",
"label.save": "ذخیره",
"label.settings": "تنظیمات",
"label.share-url": "به اشتراک گذاری URL",
"label.single-day": "یک روز",
"label.theme": "Theme",
"label.theme": "تم",
"label.this-month": "این ماه",
"label.this-week": "این هفته",
"label.this-year": "امسال",
@ -64,7 +64,7 @@
"label.websites": "وب‌سایت‌ها",
"message.active-users": "{x} هم اکنون {x, plural, one {یک} other {از میان}}",
"message.confirm-delete": "آیا مطمئن هستید می‌خواهید {target} را حذف کنید?",
"message.confirm-reset": "Are your sure you want to reset {target}'s statistics?",
"message.confirm-reset": "آیا از بازنشانی آمار {target} مطمئن هستید?",
"message.copied": "کپی شد!",
"message.delete-warning": "همه‌ی داده‌های مرتبط هم حذف خواهد شد.",
"message.failure": "مشکلی پیش آمده است.",
@ -78,7 +78,7 @@
"message.no-websites-configured": "شما هیچ وب‌سایتی را پیکربندی نکرده‌اید.",
"message.page-not-found": "صفحه یافت نشد.",
"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": "تمامی آمارهای این وب‌سایت حذف خواهد شد اما tracking code بدون تغییر باقی می‌ماند.",
"message.save-success": "با موفقیت ذخیره شد.",
"message.share-url": "این URL به اشتراک گذاشته شده عمومی برای {target} است.",
"message.toggle-charts": "Toggle charts",
@ -99,7 +99,7 @@
"metrics.filter.combined": "ترکیب شده",
"metrics.filter.domain-only": "فقط دامنه",
"metrics.filter.raw": "خام",
"metrics.languages": "Languages",
"metrics.languages": "زبان‌ها",
"metrics.operating-systems": "سیستم‌عامل‌ها",
"metrics.page-views": "بازدید صفحه",
"metrics.pages": "صفحه‌ها",

View File

@ -5,7 +5,7 @@
"label.administrator": "Administrateur",
"label.all": "Tout",
"label.all-events": "Tous les événements",
"label.all-time": "Toutes périodes",
"label.all-time": "Toutes les données",
"label.all-websites": "Tous les sites web",
"label.back": "Retour",
"label.cancel": "Annuler",
@ -13,10 +13,10 @@
"label.confirm-password": "Confirmation du mot de passe",
"label.copy-to-clipboard": "Copier dans le presse papier",
"label.current-password": "Mot de passe actuel",
"label.custom-range": "Intervalle personnalisé",
"label.custom-range": "Période personnalisée",
"label.dashboard": "Tableau de bord",
"label.date-range": "Intervalle",
"label.default-date-range": "Intervalle par défaut",
"label.date-range": "Période",
"label.default-date-range": "Période par défaut",
"label.delete": "Supprimer",
"label.delete-account": "Supprimer le compte",
"label.delete-website": "Supprimer le site",
@ -25,7 +25,7 @@
"label.edit": "Modifier",
"label.edit-account": "Modifier le compte",
"label.edit-website": "Modifier le site",
"label.enable-share-url": "Activer le partage d'URL",
"label.enable-share-url": "Activer l'URL de partage",
"label.invalid": "Invalide",
"label.invalid-domain": "Domaine invalide",
"label.language": "Langage",
@ -68,10 +68,10 @@
"message.copied": "Copié !",
"message.delete-warning": "Toutes les données associées seront également supprimées.",
"message.failure": "Un problème est survenu.",
"message.get-share-url": "Obtenez l'URL de partage",
"message.get-tracking-code": "Obtenez le code de suivi",
"message.get-share-url": "Obtenir l'URL de partage",
"message.get-tracking-code": "Obtenir le code de suivi",
"message.go-to-settings": "Aller aux paramètres",
"message.incorrect-username-password": "nom d'utilisateurs/mot de passe incorrect.",
"message.incorrect-username-password": "Nom d'utilisateur/Mot de passe incorrect.",
"message.log.visitor": "Visiteur de {country} utilisant {browser} sur {os} {device}",
"message.new-version-available": "Une nouvelle version de umami {version} est disponible !",
"message.no-data-available": "Pas de données disponibles.",
@ -81,7 +81,7 @@
"message.reset-warning": "Toutes les statistiques pour ce site seront supprimés, mais votre code de suivi restera intact.",
"message.save-success": "Enregistré avec succès.",
"message.share-url": "Ceci est l'URL partagée pour {target}.",
"message.toggle-charts": "Changer les graphiques",
"message.toggle-charts": "Afficher/Masquer les graphiques",
"message.track-stats": "Pour suivre les statistiques de {target}, placez le code suivant dans la section {head} de votre site Web.",
"message.type-delete": "Tapez {delete} dans la case ci-dessous pour confirmer.",
"message.type-reset": "Tapez {reset} dans la case ci-dessous pour confirmer.",
@ -98,12 +98,12 @@
"metrics.events": "Événements",
"metrics.filter.combined": "Combiné",
"metrics.filter.domain-only": "Domaine uniquement",
"metrics.filter.raw": "Brute",
"metrics.filter.raw": "Brut",
"metrics.languages": "Langages",
"metrics.operating-systems": "Systèmes d'exploitation",
"metrics.page-views": "Pages vues",
"metrics.pages": "Pages",
"metrics.referrers": "URL Référentes",
"metrics.referrers": "Sources",
"metrics.unique-visitors": "Visiteurs uniques",
"metrics.views": "Vues",
"metrics.visitors": "Visiteurs"

110
lang/ga-ES.json Normal file
View File

@ -0,0 +1,110 @@
{
"label.accounts": "Contas",
"label.add-account": "Engadir conta",
"label.add-website": "Engadir sitio web",
"label.administrator": "Administradora",
"label.all": "Todo",
"label.all-events": "Tódolos eventos",
"label.all-time": "Sempre",
"label.all-websites": "Tódolos sitios web",
"label.back": "Atrás",
"label.cancel": "Cancelar",
"label.change-password": "Mudar contrasinal",
"label.confirm-password": "Confirmar contrasinal",
"label.copy-to-clipboard": "Copiar ao portapapeis",
"label.current-password": "Contrasinal actual",
"label.custom-range": "Rango personalizado",
"label.dashboard": "Taboleiro",
"label.date-range": "Rango temporal",
"label.default-date-range": "Rango temporal por defecto",
"label.delete": "Eliminar",
"label.delete-account": "Eliminar conta",
"label.delete-website": "Eliminar sitio web",
"label.dismiss": "Desbotar",
"label.domain": "Dominio",
"label.edit": "Editar",
"label.edit-account": "Editar conta",
"label.edit-website": "Editar sitio web",
"label.enable-share-url": "Activar URL de compartición",
"label.invalid": "Non válido",
"label.invalid-domain": "Dominio non válido",
"label.language": "Idioma",
"label.last-days": "Últimos {x} días",
"label.last-hours": "Últimas {x} horas",
"label.logged-in-as": "Sesión de {username}",
"label.login": "Acceder",
"label.logout": "Pechar sesión",
"label.more": "Máis",
"label.name": "Nome",
"label.new-password": "Novo contrasinal",
"label.owner": "Dona",
"label.password": "Contrasinal",
"label.passwords-dont-match": "Non concordan os contrasinais",
"label.profile": "Perfil",
"label.realtime": "Agora mesmo",
"label.realtime-logs": "Rexistro neste intre",
"label.refresh": "Actualizar",
"label.required": "Requerido",
"label.reset": "Restablecer",
"label.reset-website": "Restablecer estatísticas",
"label.save": "Gardar",
"label.settings": "Axustes",
"label.share-url": "Compartir URL",
"label.single-day": "Un só día",
"label.theme": "Decorado",
"label.this-month": "Este mes",
"label.this-week": "Esta semana",
"label.this-year": "Este ano",
"label.timezone": "Zona horaria",
"label.today": "Hoxe",
"label.tracking-code": "Código de seguimento",
"label.unknown": "Descoñecido",
"label.username": "Identificador",
"label.view-details": "Ver detalles",
"label.websites": "Sitios web",
"message.active-users": "{x} actual {x, plural, one {visitante} other {visitantes}}",
"message.confirm-delete": "Tes a certeza de querer eliminar {target}?",
"message.confirm-reset": "Tes a certeza de querer restablecer as estatísticas de {target}?",
"message.copied": "Copiado!",
"message.delete-warning": "Tamén serán borrados tódolos datos asociados.",
"message.failure": "Houbo un fallo.",
"message.get-share-url": "Obter URL de compartición",
"message.get-tracking-code": "Obter código de seguimento",
"message.go-to-settings": "Ir aos axustes",
"message.incorrect-username-password": "Credenciais incorrectas.",
"message.log.visitor": "Visitante desde {country} usando {browser} en {os} {device}",
"message.new-version-available": "A nova versión {version} de umami está dispoñible!",
"message.no-data-available": "Sen datos dispoñibles.",
"message.no-websites-configured": "Non tes sitios web configurados.",
"message.page-not-found": "Páxina non atopada.",
"message.powered-by": "Funciona grazas a {name}",
"message.reset-warning": "Vanse eliminar tódalas estatísticas deste sitio web, pero o código de seguimento permanecerá sen cambios.",
"message.save-success": "Gardouse correctamente.",
"message.share-url": "Este é o URL da compartición pública de {target}.",
"message.toggle-charts": "Activación das gráficas",
"message.track-stats": "Para crear estatísticas de {target}, pon este código na sección {head} do teu sitio web.",
"message.type-delete": "Escribe {delete} na caixa inferior para confirmar.",
"message.type-reset": "Escribe {reset} na caixa inferior para confirmar.",
"metrics.actions": "Accións",
"metrics.average-visit-time": "Tempo medio de visita",
"metrics.bounce-rate": "Proporción de rebote",
"metrics.browsers": "Navegadores",
"metrics.countries": "Países",
"metrics.device.desktop": "Escritorio",
"metrics.device.laptop": "Portátil",
"metrics.device.mobile": "Móbil",
"metrics.device.tablet": "Tableta",
"metrics.devices": "Dispositivos",
"metrics.events": "Eventos",
"metrics.filter.combined": "Combinado",
"metrics.filter.domain-only": "Só dominio",
"metrics.filter.raw": "Raw",
"metrics.languages": "Idiomas",
"metrics.operating-systems": "Sistemas operativos",
"metrics.page-views": "Vistas de páxinas",
"metrics.pages": "Páxinas",
"metrics.referrers": "Orixes",
"metrics.unique-visitors": "Visitas únicas",
"metrics.views": "Visualizacións",
"metrics.visitors": "Visitantes"
}

View File

@ -5,7 +5,7 @@
"label.administrator": "Админ",
"label.all": "Бүх",
"label.all-events": "Бүх үйл явдал",
"label.all-time": "All time",
"label.all-time": "Бүх цаг үеийн",
"label.all-websites": "Бүх вебүүд",
"label.back": "Буцах",
"label.cancel": "Цуцлах",
@ -28,7 +28,7 @@
"label.enable-share-url": "Хуваалцах холбоос идэвхжүүлэх",
"label.invalid": "Буруу",
"label.invalid-domain": "Буруу домэйн",
"label.language": "Language",
"label.language": "Хэл",
"label.last-days": "Сүүлийн {x} хоног",
"label.last-hours": "Сүүлийн {x} цаг",
"label.logged-in-as": "{username}-р нэвтэрсэн",
@ -37,7 +37,7 @@
"label.more": "Цааш",
"label.name": "Нэр",
"label.new-password": "Шинэ нууц үг",
"label.owner": "Owner",
"label.owner": "Эзэмшигч",
"label.password": "Нууц үг",
"label.passwords-dont-match": "Нууц үг тохирохгүй байна",
"label.profile": "Бүртгэл",
@ -46,12 +46,12 @@
"label.refresh": "Сэргээх",
"label.required": "Шаардлагатай",
"label.reset": "Хуучин хэвд нь оруулах",
"label.reset-website": "Reset statistics",
"label.reset-website": "Тоон үзүүлэлтийг дахин эхлүүлэх",
"label.save": "Хадгалах",
"label.settings": "Тохиргоо",
"label.share-url": "Хуваалцах холбоос",
"label.single-day": "Нэг өдөр",
"label.theme": "Theme",
"label.theme": "Загвар",
"label.this-month": "Энэ сар",
"label.this-week": "Энэ долоо хоног",
"label.this-year": "Энэ жил",
@ -78,10 +78,10 @@
"message.no-websites-configured": "Та ямар нэгэн веб тохируулаагүй байна.",
"message.page-not-found": "Хуудас олдсонгүй.",
"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.share-url": "{target}-г нийтэд хуваалцах холбоос.",
"message.toggle-charts": "Toggle charts",
"message.toggle-charts": "Графикийг харуулах/нуух",
"message.track-stats": "{target} вебийн статистикийг бүртгэхийн тулд доорх кодыг вебийнхээ {head} хэсэгт байрлуулна уу.",
"message.type-delete": "Доорх хэсэгт {delete} гэж бичиж баталгаажуулна уу.",
"message.type-reset": "Доорх хэсэгт {reset} гэж бичиж баталгаажуулна уу.",
@ -99,7 +99,7 @@
"metrics.filter.combined": "Нэгтгэсэн",
"metrics.filter.domain-only": "Зөвхөн домэйн",
"metrics.filter.raw": "Түүхий",
"metrics.languages": "Languages",
"metrics.languages": "Хэл",
"metrics.operating-systems": "Үйлдлийн систем",
"metrics.page-views": "Хуудас үзсэн",
"metrics.pages": "Хуудас",

View File

@ -4,8 +4,8 @@
"label.add-website": "Adicionar site",
"label.administrator": "Administrador",
"label.all": "Todos",
"label.all-events": "All events",
"label.all-time": "All time",
"label.all-events": "Todos os eventos",
"label.all-time": "Todo o período",
"label.all-websites": "Todos os sites",
"label.back": "Voltar",
"label.cancel": "Cancelar",
@ -28,7 +28,7 @@
"label.enable-share-url": "Ativar link de compartilhamento",
"label.invalid": "Inválido",
"label.invalid-domain": "Domínio inválido",
"label.language": "Language",
"label.language": "Idioma",
"label.last-days": "Últimos {x} dias",
"label.last-hours": "Últimas {x} horas",
"label.logged-in-as": "Sessão iniciada como {username}",
@ -37,7 +37,7 @@
"label.more": "Mais",
"label.name": "Nome",
"label.new-password": "Nova senha",
"label.owner": "Owner",
"label.owner": "Proprietário",
"label.password": "Senha",
"label.passwords-dont-match": "As senhas não correspondem",
"label.profile": "Perfil",
@ -46,12 +46,12 @@
"label.refresh": "Atualizar",
"label.required": "Obrigatório",
"label.reset": "Redefinir",
"label.reset-website": "Reset statistics",
"label.reset-website": "Redefinir estatísticas",
"label.save": "Salvar",
"label.settings": "Configurações",
"label.share-url": "Link de compartilhamento",
"label.single-day": "Dia específico",
"label.theme": "Theme",
"label.theme": "Tema",
"label.this-month": "Este mês",
"label.this-week": "Esta semana",
"label.this-year": "Este ano",
@ -64,7 +64,7 @@
"label.websites": "Sites",
"message.active-users": "{x} {x, plural, one {visitante} other {visitantes}} neste momento",
"message.confirm-delete": "Deseja realmente remover {target}?",
"message.confirm-reset": "Are your sure you want to reset {target}'s statistics?",
"message.confirm-reset": "Você tem certeza que deseja redefinir as estatísticas de {target}?",
"message.copied": "Copiado!",
"message.delete-warning": "Todos os dados associados também serão eliminados.",
"message.failure": "Ocorreu um erro.",
@ -78,10 +78,10 @@
"message.no-websites-configured": "Nenhum site foi configurado ainda.",
"message.page-not-found": "Página não encontrada.",
"message.powered-by": "Distribuído por {name}",
"message.reset-warning": "All statistics for this website will be deleted, but your tracking code will remain intact.",
"message.reset-warning": "Todas as estatísticas deste site serão removidas, mas seu código de rastreamento permanecerá intacto.",
"message.save-success": "Salvo com sucesso.",
"message.share-url": "Este é o link público de compartilhamento para {target}.",
"message.toggle-charts": "Toggle charts",
"message.toggle-charts": "Mostrar/Esconder gráficos",
"message.track-stats": "Para gerar estatística para {target}, coloque o seguinte código no {head} do html do seu site.",
"message.type-delete": "Escreva {delete} abaixo para continuar.",
"message.type-reset": "Escreva {reset} abaixo para continuar.",
@ -99,7 +99,7 @@
"metrics.filter.combined": "Combinado",
"metrics.filter.domain-only": "Apenas domínio",
"metrics.filter.raw": "Dados brutos",
"metrics.languages": "Languages",
"metrics.languages": "Idiomas",
"metrics.operating-systems": "Sistemas operacionais",
"metrics.page-views": "Visualizações de página",
"metrics.pages": "Páginas",

View File

@ -2,11 +2,11 @@
"label.accounts": "Tài khoản",
"label.add-account": "Thêm tài khoản",
"label.add-website": "Thêm website",
"label.administrator": "Quản Trị",
"label.administrator": "Quản trị",
"label.all": "Tất cả",
"label.all-events": "Tất cả events",
"label.all-time": "All time",
"label.all-websites": "Tất cả websites",
"label.all-events": "Tất cả sự kiện",
"label.all-time": "Toàn thời gian",
"label.all-websites": "Tất cả website",
"label.back": "Quay về",
"label.cancel": "Huỷ bỏ",
"label.change-password": "Đổi mật khẩu",
@ -16,7 +16,7 @@
"label.custom-range": "Phạm vi ngày tuỳ chọn",
"label.dashboard": "Bảng điều khiển",
"label.date-range": "Phạm vi ngày",
"label.default-date-range": "Phạm vi ngày mặc định",
"label.default-date-range": "Khoảng thời gian mặc định",
"label.delete": "Xoá",
"label.delete-account": "Xoá tài khoản",
"label.delete-website": "Xáo website",
@ -37,7 +37,7 @@
"label.more": "Thêm",
"label.name": "Tên",
"label.new-password": "Mật khẩu mới",
"label.owner": "Owner",
"label.owner": "Chủ nhân",
"label.password": "Mật khẩu",
"label.passwords-dont-match": "Mật khẩu không đồng nhất",
"label.profile": "Hồ sơ",
@ -81,7 +81,7 @@
"message.reset-warning": "Tất cả số liệu thống kê của website này sẽ bị xoá, nhưng mã theo dõi sẽ vẫn giữ nguyên.",
"message.save-success": "Đã lưu thành công.",
"message.share-url": "Đây là đường dẫn URL cho {target}.",
"message.toggle-charts": "Toggle charts",
"message.toggle-charts": "Bật/tắt biểu đồ",
"message.track-stats": "Để theo dõi {target}, dán mã theo dõi vào {head} của website bạn.",
"message.type-delete": "Nhập {delete} bên dưới để xác nhận.",
"message.type-reset": "Nhập {reset} bên dưới để xác nhận.",
@ -99,12 +99,12 @@
"metrics.filter.combined": "Kết hợp",
"metrics.filter.domain-only": "Chỉ tên miền",
"metrics.filter.raw": "Gốc",
"metrics.languages": "Languages",
"metrics.languages": "Ngôn ngũ",
"metrics.operating-systems": "Hệ điều hành",
"metrics.page-views": "Lượt xem",
"metrics.pages": "Trang",
"metrics.referrers": "Liên kết giới thiệu",
"metrics.unique-visitors": "Khách truy cập duy nhất",
"metrics.unique-visitors": "Khách truy cập một lần",
"metrics.views": "Xem",
"metrics.visitors": "Khách"
}

View File

@ -11,23 +11,23 @@ const options = {
};
function logQuery(e) {
if (process.env.LOG_QUERY) {
console.log(chalk.yellow(e.params), '->', e.query, chalk.greenBright(`${e.duration}ms`));
}
console.log(chalk.yellow(e.params), '->', e.query, chalk.greenBright(`${e.duration}ms`));
}
let prisma;
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient(options);
prisma.$on('query', logQuery);
} else {
if (!global.prisma) {
global.prisma = new PrismaClient(options);
global.prisma.$on('query', logQuery);
}
prisma = global.prisma;
}
if (process.env.LOG_QUERY) {
prisma.$on('query', logQuery);
}
export default prisma;

View File

@ -53,6 +53,7 @@ export const languages = {
'fa-IR': { label: 'فارسی', dateLocale: faIR, dir: 'rtl' },
'fo-FO': { label: 'Føroyskt' },
'fr-FR': { label: 'Français', dateLocale: fr },
'ga-ES': { label: 'Galacian (Spain)', dateLocale: es },
'el-GR': { label: 'Ελληνικά', dateLocale: el },
'he-IL': { label: 'עברית', dateLocale: he },
'hi-IN': { label: 'हिन्दी', dateLocale: hi },

View File

@ -315,6 +315,13 @@ export async function getAccounts() {
username: 'asc',
},
],
select: {
user_id: true,
username: true,
is_admin: true,
created_at: true,
updated_at: true,
},
}),
);
}

View File

@ -3,7 +3,9 @@ const pkg = require('./package.json');
module.exports = {
env: {
VERSION: pkg.version,
currentVersion: pkg.version,
loginDisabled: process.env.DISABLE_LOGIN,
updatesDisabled: process.env.DISABLE_UPDATES,
},
basePath: process.env.BASE_PATH,
experimental: {

View File

@ -1,6 +1,6 @@
{
"name": "umami",
"version": "1.32.0",
"version": "1.33.0",
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
"author": "Mike Cao <mike@mikecao.com>",
"license": "MIT",
@ -12,20 +12,21 @@
"scripts": {
"dev": "next dev",
"build": "npm-run-all build-tracker build-geo build-db build-app",
"start": "next start",
"start": "npm-run-all check-db start-next",
"start-docker": "npm-run-all check-db start-server",
"start-env": "node -r dotenv/config scripts/start-env.js",
"start-server": "node server.js",
"start-next": "next start",
"build-app": "next build",
"build-tracker": "rollup -c rollup.tracker.config.js",
"build-db": "npm-run-all copy-db-schema build-db-client",
"build-db": "npm-run-all copy-db-files build-db-client",
"build-lang": "npm-run-all format-lang compile-lang",
"build-geo": "node scripts/build-geo.js",
"build-db-schema": "dotenv prisma introspect",
"build-db-client": "dotenv prisma generate",
"build-mysql-schema": "dotenv prisma db pull -- --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-client": "dotenv prisma generate -- --schema=./prisma/schema.postgresql.prisma",
"copy-db-schema": "node scripts/copy-db-schema.js",
"build-db-schema": "prisma db pull",
"build-db-client": "prisma generate",
"update-db": "prisma migrate deploy",
"check-db": "node scripts/check-db.js",
"copy-db-files": "node scripts/copy-db-files.js",
"generate-lang": "npm-run-all extract-lang merge-lang",
"extract-lang": "formatjs extract \"{pages,components}/**/*.js\" --out-file build/messages.json",
"merge-lang": "node scripts/merge-lang.js",
@ -54,13 +55,14 @@
},
"dependencies": {
"@fontsource/inter": "4.5.7",
"@prisma/client": "3.14.0",
"@prisma/client": "3.15.2",
"bcryptjs": "^2.4.3",
"chalk": "^4.1.1",
"chart.js": "^2.9.4",
"classnames": "^2.3.1",
"colord": "^2.9.2",
"cors": "^2.8.5",
"cross-spawn": "^7.0.3",
"date-fns": "^2.23.0",
"date-fns-tz": "^1.1.4",
"del": "^6.0.0",
@ -115,7 +117,7 @@
"postcss-preset-env": "7.4.3",
"postcss-rtlcss": "^3.6.1",
"prettier": "^2.6.2",
"prisma": "3.14.0",
"prisma": "3.15.2",
"prompts": "2.4.2",
"rollup": "^2.70.1",
"rollup-plugin-terser": "^7.0.2",

View File

@ -15,12 +15,6 @@ function customScriptName(req) {
}
}
function disableLogin(req) {
if (process.env.DISABLE_LOGIN && req.nextUrl.pathname.endsWith('/login')) {
return new Response('Login is disabled', { status: 403 });
}
}
function forceSSL(req, res) {
if (process.env.FORCE_SSL && req.nextUrl.protocol === 'http:') {
res.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
@ -30,7 +24,7 @@ function forceSSL(req, res) {
}
export function middleware(req) {
const fns = [customScriptName, disableLogin];
const fns = [customScriptName];
for (const fn of fns) {
const res = fn(req);

View File

@ -1,9 +1,10 @@
const { Resolver } = require('dns').promises;
import isbot from 'isbot';
import ipaddr from 'ipaddr.js';
import { savePageView, saveEvent } from 'lib/queries';
import { useCors, useSession } from 'lib/middleware';
import { getJsonBody, getIpAddress } from 'lib/request';
import { ok, send, badRequest } from 'lib/response';
import { ok, send, badRequest, forbidden } from 'lib/response';
import { createToken } from 'lib/crypto';
import { removeTrailingSlash } from 'lib/url';
@ -15,16 +16,35 @@ export default async (req, res) => {
}
const ignoreIps = process.env.IGNORE_IP;
if (ignoreIps) {
const ips = ignoreIps.split(',').map(n => n.trim());
const ip = getIpAddress(req);
const blocked = ips.find(i => {
if (i === ip) return true;
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
if (ignoreIps || ignoreHostnames) {
const ips = [];
if (ignoreIps) {
ips.push(...ignoreIps.split(',').map(n => n.trim()));
}
if (ignoreHostnames) {
const resolver = new Resolver();
const promises = ignoreHostnames
.split(',')
.map(n => resolver.resolve4(n.trim()).catch(() => {}));
await Promise.all(promises).then(resolvedIps => {
ips.push(...resolvedIps.filter(n => n).flatMap(n => n));
});
}
const clientIp = getIpAddress(req);
const blocked = ips.find(ip => {
if (ip === clientIp) return true;
// CIDR notation
if (i.indexOf('/') > 0) {
const addr = ipaddr.parse(ip);
const range = ipaddr.parseCIDR(i);
if (ip.indexOf('/') > 0) {
const addr = ipaddr.parse(clientIp);
const range = ipaddr.parseCIDR(ip);
if (addr.kind() === range[0].kind() && addr.match(range)) return true;
}
@ -33,7 +53,7 @@ export default async (req, res) => {
});
if (blocked) {
return ok(res);
return forbidden(res);
}
}

View File

@ -3,7 +3,7 @@ import Layout from 'components/layout/Layout';
import LoginForm from 'components/forms/LoginForm';
export default function LoginPage() {
if (process.env.DISABLE_LOGIN) {
if (process.env.loginDisabled) {
return null;
}

View File

@ -1 +0,0 @@
../schema.mysql.prisma

View File

@ -1 +0,0 @@
../schema.postgresql.prisma

View File

@ -1,29 +0,0 @@
const bcrypt = require('bcryptjs');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const SALT_ROUNDS = 10;
const hashPassword = password => {
return bcrypt.hashSync(password, SALT_ROUNDS);
};
async function main() {
await prisma.account.upsert({
where: { username: 'admin' },
update: {},
create: {
username: 'admin',
password: hashPassword(process.env.ADMIN_PASSWORD || 'umami'),
is_admin: true,
},
});
}
main()
.catch(e => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});

View File

@ -0,0 +1 @@
404: Not Found

View File

@ -0,0 +1 @@
404: Not Found

View File

@ -38,7 +38,7 @@
"label.all-time": [
{
"type": 0,
"value": "All time"
"value": "همه زمان"
}
],
"label.all-websites": [
@ -176,7 +176,7 @@
"label.language": [
{
"type": 0,
"value": "Language"
"value": "زبان"
}
],
"label.last-days": [
@ -190,7 +190,7 @@
},
{
"type": 0,
"value": " روز"
"value": " روز گذشته"
}
],
"label.last-hours": [
@ -204,7 +204,7 @@
},
{
"type": 0,
"value": " ساعت"
"value": " ساعت گذشته"
}
],
"label.logged-in-as": [
@ -250,7 +250,7 @@
"label.owner": [
{
"type": 0,
"value": "Owner"
"value": "ایجاد شده توسط"
}
],
"label.password": [
@ -304,7 +304,7 @@
"label.reset-website": [
{
"type": 0,
"value": "Reset statistics"
"value": "بازنشانی آمار"
}
],
"label.save": [
@ -334,7 +334,7 @@
"label.theme": [
{
"type": 0,
"value": "Theme"
"value": "تم"
}
],
"label.this-month": [
@ -448,7 +448,7 @@
"message.confirm-reset": [
{
"type": 0,
"value": "Are your sure you want to reset "
"value": "آیا از بازنشانی آمار "
},
{
"type": 1,
@ -456,7 +456,7 @@
},
{
"type": 0,
"value": "'s statistics?"
"value": " مطمئن هستید?"
}
],
"message.copied": [
@ -580,7 +580,7 @@
"message.reset-warning": [
{
"type": 0,
"value": "All statistics for this website will be deleted, but your tracking code will remain intact."
"value": "تمامی آمارهای این وب‌سایت حذف خواهد شد اما tracking code بدون تغییر باقی می‌ماند."
}
],
"message.save-success": [
@ -730,7 +730,7 @@
"metrics.languages": [
{
"type": 0,
"value": "Languages"
"value": "زبان‌ها"
}
],
"metrics.operating-systems": [

View File

@ -38,7 +38,7 @@
"label.all-time": [
{
"type": 0,
"value": "Toutes périodes"
"value": "Toutes les données"
}
],
"label.all-websites": [
@ -86,7 +86,7 @@
"label.custom-range": [
{
"type": 0,
"value": "Intervalle personnalisé"
"value": "Période personnalisée"
}
],
"label.dashboard": [
@ -98,13 +98,13 @@
"label.date-range": [
{
"type": 0,
"value": "Intervalle"
"value": "Période"
}
],
"label.default-date-range": [
{
"type": 0,
"value": "Intervalle par défaut"
"value": "Période par défaut"
}
],
"label.delete": [
@ -158,7 +158,7 @@
"label.enable-share-url": [
{
"type": 0,
"value": "Activer le partage d'URL"
"value": "Activer l'URL de partage"
}
],
"label.invalid": [
@ -476,13 +476,13 @@
"message.get-share-url": [
{
"type": 0,
"value": "Obtenez l'URL de partage"
"value": "Obtenir l'URL de partage"
}
],
"message.get-tracking-code": [
{
"type": 0,
"value": "Obtenez le code de suivi"
"value": "Obtenir le code de suivi"
}
],
"message.go-to-settings": [
@ -494,7 +494,7 @@
"message.incorrect-username-password": [
{
"type": 0,
"value": "nom d'utilisateurs/mot de passe incorrect."
"value": "Nom d'utilisateur/Mot de passe incorrect."
}
],
"message.log.visitor": [
@ -602,7 +602,7 @@
"message.toggle-charts": [
{
"type": 0,
"value": "Changer les graphiques"
"value": "Afficher/Masquer les graphiques"
}
],
"message.track-stats": [
@ -736,7 +736,7 @@
"metrics.filter.raw": [
{
"type": 0,
"value": "Brute"
"value": "Brut"
}
],
"metrics.languages": [
@ -766,7 +766,7 @@
"metrics.referrers": [
{
"type": 0,
"value": "URL Référentes"
"value": "Sources"
}
],
"metrics.unique-visitors": [

View File

@ -0,0 +1,794 @@
{
"label.accounts": [
{
"type": 0,
"value": "Contas"
}
],
"label.add-account": [
{
"type": 0,
"value": "Engadir conta"
}
],
"label.add-website": [
{
"type": 0,
"value": "Engadir sitio web"
}
],
"label.administrator": [
{
"type": 0,
"value": "Administradora"
}
],
"label.all": [
{
"type": 0,
"value": "Todo"
}
],
"label.all-events": [
{
"type": 0,
"value": "Tódolos eventos"
}
],
"label.all-time": [
{
"type": 0,
"value": "Sempre"
}
],
"label.all-websites": [
{
"type": 0,
"value": "Tódolos sitios web"
}
],
"label.back": [
{
"type": 0,
"value": "Atrás"
}
],
"label.cancel": [
{
"type": 0,
"value": "Cancelar"
}
],
"label.change-password": [
{
"type": 0,
"value": "Mudar contrasinal"
}
],
"label.confirm-password": [
{
"type": 0,
"value": "Confirmar contrasinal"
}
],
"label.copy-to-clipboard": [
{
"type": 0,
"value": "Copiar ao portapapeis"
}
],
"label.current-password": [
{
"type": 0,
"value": "Contrasinal actual"
}
],
"label.custom-range": [
{
"type": 0,
"value": "Rango personalizado"
}
],
"label.dashboard": [
{
"type": 0,
"value": "Taboleiro"
}
],
"label.date-range": [
{
"type": 0,
"value": "Rango temporal"
}
],
"label.default-date-range": [
{
"type": 0,
"value": "Rango temporal por defecto"
}
],
"label.delete": [
{
"type": 0,
"value": "Eliminar"
}
],
"label.delete-account": [
{
"type": 0,
"value": "Eliminar conta"
}
],
"label.delete-website": [
{
"type": 0,
"value": "Eliminar sitio web"
}
],
"label.dismiss": [
{
"type": 0,
"value": "Desbotar"
}
],
"label.domain": [
{
"type": 0,
"value": "Dominio"
}
],
"label.edit": [
{
"type": 0,
"value": "Editar"
}
],
"label.edit-account": [
{
"type": 0,
"value": "Editar conta"
}
],
"label.edit-website": [
{
"type": 0,
"value": "Editar sitio web"
}
],
"label.enable-share-url": [
{
"type": 0,
"value": "Activar URL de compartición"
}
],
"label.invalid": [
{
"type": 0,
"value": "Non válido"
}
],
"label.invalid-domain": [
{
"type": 0,
"value": "Dominio non válido"
}
],
"label.language": [
{
"type": 0,
"value": "Idioma"
}
],
"label.last-days": [
{
"type": 0,
"value": "Últimos "
},
{
"type": 1,
"value": "x"
},
{
"type": 0,
"value": " días"
}
],
"label.last-hours": [
{
"type": 0,
"value": "Últimas "
},
{
"type": 1,
"value": "x"
},
{
"type": 0,
"value": " horas"
}
],
"label.logged-in-as": [
{
"type": 0,
"value": "Sesión de "
},
{
"type": 1,
"value": "username"
}
],
"label.login": [
{
"type": 0,
"value": "Acceder"
}
],
"label.logout": [
{
"type": 0,
"value": "Pechar sesión"
}
],
"label.more": [
{
"type": 0,
"value": "Máis"
}
],
"label.name": [
{
"type": 0,
"value": "Nome"
}
],
"label.new-password": [
{
"type": 0,
"value": "Novo contrasinal"
}
],
"label.owner": [
{
"type": 0,
"value": "Dona"
}
],
"label.password": [
{
"type": 0,
"value": "Contrasinal"
}
],
"label.passwords-dont-match": [
{
"type": 0,
"value": "Non concordan os contrasinais"
}
],
"label.profile": [
{
"type": 0,
"value": "Perfil"
}
],
"label.realtime": [
{
"type": 0,
"value": "Agora mesmo"
}
],
"label.realtime-logs": [
{
"type": 0,
"value": "Rexistro neste intre"
}
],
"label.refresh": [
{
"type": 0,
"value": "Actualizar"
}
],
"label.required": [
{
"type": 0,
"value": "Requerido"
}
],
"label.reset": [
{
"type": 0,
"value": "Restablecer"
}
],
"label.reset-website": [
{
"type": 0,
"value": "Restablecer estatísticas"
}
],
"label.save": [
{
"type": 0,
"value": "Gardar"
}
],
"label.settings": [
{
"type": 0,
"value": "Axustes"
}
],
"label.share-url": [
{
"type": 0,
"value": "Compartir URL"
}
],
"label.single-day": [
{
"type": 0,
"value": "Un só día"
}
],
"label.theme": [
{
"type": 0,
"value": "Decorado"
}
],
"label.this-month": [
{
"type": 0,
"value": "Este mes"
}
],
"label.this-week": [
{
"type": 0,
"value": "Esta semana"
}
],
"label.this-year": [
{
"type": 0,
"value": "Este ano"
}
],
"label.timezone": [
{
"type": 0,
"value": "Zona horaria"
}
],
"label.today": [
{
"type": 0,
"value": "Hoxe"
}
],
"label.tracking-code": [
{
"type": 0,
"value": "Código de seguimento"
}
],
"label.unknown": [
{
"type": 0,
"value": "Descoñecido"
}
],
"label.username": [
{
"type": 0,
"value": "Identificador"
}
],
"label.view-details": [
{
"type": 0,
"value": "Ver detalles"
}
],
"label.websites": [
{
"type": 0,
"value": "Sitios web"
}
],
"message.active-users": [
{
"type": 1,
"value": "x"
},
{
"type": 0,
"value": " actual "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 0,
"value": "visitante"
}
]
},
"other": {
"value": [
{
"type": 0,
"value": "visitantes"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "x"
}
],
"message.confirm-delete": [
{
"type": 0,
"value": "Tes a certeza de querer eliminar "
},
{
"type": 1,
"value": "target"
},
{
"type": 0,
"value": "?"
}
],
"message.confirm-reset": [
{
"type": 0,
"value": "Tes a certeza de querer restablecer as estatísticas de "
},
{
"type": 1,
"value": "target"
},
{
"type": 0,
"value": "?"
}
],
"message.copied": [
{
"type": 0,
"value": "Copiado!"
}
],
"message.delete-warning": [
{
"type": 0,
"value": "Tamén serán borrados tódolos datos asociados."
}
],
"message.failure": [
{
"type": 0,
"value": "Houbo un fallo."
}
],
"message.get-share-url": [
{
"type": 0,
"value": "Obter URL de compartición"
}
],
"message.get-tracking-code": [
{
"type": 0,
"value": "Obter código de seguimento"
}
],
"message.go-to-settings": [
{
"type": 0,
"value": "Ir aos axustes"
}
],
"message.incorrect-username-password": [
{
"type": 0,
"value": "Credenciais incorrectas."
}
],
"message.log.visitor": [
{
"type": 0,
"value": "Visitante desde "
},
{
"type": 1,
"value": "country"
},
{
"type": 0,
"value": " usando "
},
{
"type": 1,
"value": "browser"
},
{
"type": 0,
"value": " en "
},
{
"type": 1,
"value": "os"
},
{
"type": 0,
"value": " "
},
{
"type": 1,
"value": "device"
}
],
"message.new-version-available": [
{
"type": 0,
"value": "A nova versión "
},
{
"type": 1,
"value": "version"
},
{
"type": 0,
"value": " de umami está dispoñible!"
}
],
"message.no-data-available": [
{
"type": 0,
"value": "Sen datos dispoñibles."
}
],
"message.no-websites-configured": [
{
"type": 0,
"value": "Non tes sitios web configurados."
}
],
"message.page-not-found": [
{
"type": 0,
"value": "Páxina non atopada."
}
],
"message.powered-by": [
{
"type": 0,
"value": "Funciona grazas a "
},
{
"type": 1,
"value": "name"
}
],
"message.reset-warning": [
{
"type": 0,
"value": "Vanse eliminar tódalas estatísticas deste sitio web, pero o código de seguimento permanecerá sen cambios."
}
],
"message.save-success": [
{
"type": 0,
"value": "Gardouse correctamente."
}
],
"message.share-url": [
{
"type": 0,
"value": "Este é o URL da compartición pública de "
},
{
"type": 1,
"value": "target"
},
{
"type": 0,
"value": "."
}
],
"message.toggle-charts": [
{
"type": 0,
"value": "Activación das gráficas"
}
],
"message.track-stats": [
{
"type": 0,
"value": "Para crear estatísticas de "
},
{
"type": 1,
"value": "target"
},
{
"type": 0,
"value": ", pon este código na sección "
},
{
"type": 1,
"value": "head"
},
{
"type": 0,
"value": " do teu sitio web."
}
],
"message.type-delete": [
{
"type": 0,
"value": "Escribe "
},
{
"type": 1,
"value": "delete"
},
{
"type": 0,
"value": " na caixa inferior para confirmar."
}
],
"message.type-reset": [
{
"type": 0,
"value": "Escribe "
},
{
"type": 1,
"value": "reset"
},
{
"type": 0,
"value": " na caixa inferior para confirmar."
}
],
"metrics.actions": [
{
"type": 0,
"value": "Accións"
}
],
"metrics.average-visit-time": [
{
"type": 0,
"value": "Tempo medio de visita"
}
],
"metrics.bounce-rate": [
{
"type": 0,
"value": "Proporción de rebote"
}
],
"metrics.browsers": [
{
"type": 0,
"value": "Navegadores"
}
],
"metrics.countries": [
{
"type": 0,
"value": "Países"
}
],
"metrics.device.desktop": [
{
"type": 0,
"value": "Escritorio"
}
],
"metrics.device.laptop": [
{
"type": 0,
"value": "Portátil"
}
],
"metrics.device.mobile": [
{
"type": 0,
"value": "Móbil"
}
],
"metrics.device.tablet": [
{
"type": 0,
"value": "Tableta"
}
],
"metrics.devices": [
{
"type": 0,
"value": "Dispositivos"
}
],
"metrics.events": [
{
"type": 0,
"value": "Eventos"
}
],
"metrics.filter.combined": [
{
"type": 0,
"value": "Combinado"
}
],
"metrics.filter.domain-only": [
{
"type": 0,
"value": "Só dominio"
}
],
"metrics.filter.raw": [
{
"type": 0,
"value": "Raw"
}
],
"metrics.languages": [
{
"type": 0,
"value": "Idiomas"
}
],
"metrics.operating-systems": [
{
"type": 0,
"value": "Sistemas operativos"
}
],
"metrics.page-views": [
{
"type": 0,
"value": "Vistas de páxinas"
}
],
"metrics.pages": [
{
"type": 0,
"value": "Páxinas"
}
],
"metrics.referrers": [
{
"type": 0,
"value": "Orixes"
}
],
"metrics.unique-visitors": [
{
"type": 0,
"value": "Visitas únicas"
}
],
"metrics.views": [
{
"type": 0,
"value": "Visualizacións"
}
],
"metrics.visitors": [
{
"type": 0,
"value": "Visitantes"
}
]
}

View File

@ -38,7 +38,7 @@
"label.all-time": [
{
"type": 0,
"value": "All time"
"value": "Бүх цаг үеийн"
}
],
"label.all-websites": [
@ -176,7 +176,7 @@
"label.language": [
{
"type": 0,
"value": "Language"
"value": "Хэл"
}
],
"label.last-days": [
@ -250,7 +250,7 @@
"label.owner": [
{
"type": 0,
"value": "Owner"
"value": "Эзэмшигч"
}
],
"label.password": [
@ -304,7 +304,7 @@
"label.reset-website": [
{
"type": 0,
"value": "Reset statistics"
"value": "Тоон үзүүлэлтийг дахин эхлүүлэх"
}
],
"label.save": [
@ -334,7 +334,7 @@
"label.theme": [
{
"type": 0,
"value": "Theme"
"value": "Загвар"
}
],
"label.this-month": [
@ -588,7 +588,7 @@
"message.reset-warning": [
{
"type": 0,
"value": "All statistics for this website will be deleted, but your tracking code will remain intact."
"value": "Энэ вебийн бүх тоон үзүүлэлтүүдийг устгах болно. Гэхдээ мөрдөх код хэвэндээ үлдэнэ."
}
],
"message.save-success": [
@ -610,7 +610,7 @@
"message.toggle-charts": [
{
"type": 0,
"value": "Toggle charts"
"value": "Графикийг харуулах/нуух"
}
],
"message.track-stats": [
@ -746,7 +746,7 @@
"metrics.languages": [
{
"type": 0,
"value": "Languages"
"value": "Хэл"
}
],
"metrics.operating-systems": [

View File

@ -32,13 +32,13 @@
"label.all-events": [
{
"type": 0,
"value": "All events"
"value": "Todos os eventos"
}
],
"label.all-time": [
{
"type": 0,
"value": "All time"
"value": "Todo o período"
}
],
"label.all-websites": [
@ -176,7 +176,7 @@
"label.language": [
{
"type": 0,
"value": "Language"
"value": "Idioma"
}
],
"label.last-days": [
@ -250,7 +250,7 @@
"label.owner": [
{
"type": 0,
"value": "Owner"
"value": "Proprietário"
}
],
"label.password": [
@ -304,7 +304,7 @@
"label.reset-website": [
{
"type": 0,
"value": "Reset statistics"
"value": "Redefinir estatísticas"
}
],
"label.save": [
@ -334,7 +334,7 @@
"label.theme": [
{
"type": 0,
"value": "Theme"
"value": "Tema"
}
],
"label.this-month": [
@ -452,7 +452,7 @@
"message.confirm-reset": [
{
"type": 0,
"value": "Are your sure you want to reset "
"value": "Você tem certeza que deseja redefinir as estatísticas de "
},
{
"type": 1,
@ -460,7 +460,7 @@
},
{
"type": 0,
"value": "'s statistics?"
"value": "?"
}
],
"message.copied": [
@ -584,7 +584,7 @@
"message.reset-warning": [
{
"type": 0,
"value": "All statistics for this website will be deleted, but your tracking code will remain intact."
"value": "Todas as estatísticas deste site serão removidas, mas seu código de rastreamento permanecerá intacto."
}
],
"message.save-success": [
@ -610,7 +610,7 @@
"message.toggle-charts": [
{
"type": 0,
"value": "Toggle charts"
"value": "Mostrar/Esconder gráficos"
}
],
"message.track-stats": [
@ -750,7 +750,7 @@
"metrics.languages": [
{
"type": 0,
"value": "Languages"
"value": "Idiomas"
}
],
"metrics.operating-systems": [

View File

@ -20,7 +20,7 @@
"label.administrator": [
{
"type": 0,
"value": "Quản Trị"
"value": "Quản trị"
}
],
"label.all": [
@ -32,19 +32,19 @@
"label.all-events": [
{
"type": 0,
"value": "Tất cả events"
"value": "Tất cả sự kiện"
}
],
"label.all-time": [
{
"type": 0,
"value": "All time"
"value": "Toàn thời gian"
}
],
"label.all-websites": [
{
"type": 0,
"value": "Tất cả websites"
"value": "Tất cả website"
}
],
"label.back": [
@ -104,7 +104,7 @@
"label.default-date-range": [
{
"type": 0,
"value": "Phạm vi ngày mặc định"
"value": "Khoảng thời gian mặc định"
}
],
"label.delete": [
@ -242,7 +242,7 @@
"label.owner": [
{
"type": 0,
"value": "Owner"
"value": "Chủ nhân"
}
],
"label.password": [
@ -590,7 +590,7 @@
"message.toggle-charts": [
{
"type": 0,
"value": "Toggle charts"
"value": "Bật/tắt biểu đồ"
}
],
"message.track-stats": [
@ -730,7 +730,7 @@
"metrics.languages": [
{
"type": 0,
"value": "Languages"
"value": "Ngôn ngũ"
}
],
"metrics.operating-systems": [
@ -760,7 +760,7 @@
"metrics.unique-visitors": [
{
"type": 0,
"value": "Khách truy cập duy nhất"
"value": "Khách truy cập một lần"
}
],
"metrics.views": [

100
scripts/check-db.js Normal file
View File

@ -0,0 +1,100 @@
require('dotenv').config();
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const chalk = require('chalk');
const spawn = require('cross-spawn');
let message = '';
const updateMessage = `To update your database, you need to run:\n${chalk.bold.whiteBright(
'yarn update-db',
)}`;
const baselineMessage = cmd =>
`You need to update your database by running:\n${chalk.bold.whiteBright(cmd)}`;
function success(msg) {
console.log(chalk.greenBright(`${msg}`));
}
async function checkEnv() {
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not defined.');
} else {
success('DATABASE_URL is defined.');
}
}
async function checkConnection() {
try {
await prisma.$connect();
success('Database connection successful.');
} catch (e) {
throw new Error('Unable to connect to the database.');
}
}
async function checkTables() {
try {
await prisma.account.findFirst();
success('Database tables found.');
} catch (e) {
message = updateMessage;
throw new Error('Database tables not found.');
}
}
async function run(cmd, args) {
const buffer = [];
const proc = spawn(cmd, args);
return new Promise((resolve, reject) => {
proc.stdout.on('data', data => buffer.push(data));
proc.on('error', () => {
reject(new Error('Failed to run Prisma.'));
});
proc.on('exit', () => resolve(buffer.join('')));
});
}
async function checkMigrations() {
const output = await run('prisma', ['migrate', 'status']);
const missingMigrations = output.includes('Following migration have not yet been applied');
const notManaged = output.includes('The current database is not managed');
if (notManaged) {
const cmd = output.match(/yarn prisma migrate resolve --applied ".*"/g);
message = baselineMessage(cmd[0]);
throw new Error('Database is out of date.');
} else if (missingMigrations) {
message = updateMessage;
throw new Error('Database is out of date.');
}
success('Database is up to date.');
}
(async () => {
let err = false;
for (let fn of [checkEnv, checkConnection, checkTables, checkMigrations]) {
try {
await fn();
} catch (e) {
console.log(chalk.red(`${e.message}`));
err = true;
} finally {
await prisma.$disconnect();
if (err) {
console.log(message);
process.exit(1);
}
}
}
})();

View File

@ -1,8 +1,9 @@
require('dotenv').config();
const fs = require('fs');
const fse = require('fs-extra');
const path = require('path');
const del = require('del');
function getDatabase() {
function getDatabaseType() {
const type =
process.env.DATABASE_TYPE ||
(process.env.DATABASE_URL && process.env.DATABASE_URL.split(':')[0]);
@ -14,7 +15,7 @@ function getDatabase() {
return type;
}
const databaseType = getDatabase();
const databaseType = getDatabaseType();
if (!databaseType || !['mysql', 'postgresql'].includes(databaseType)) {
throw new Error('Missing or invalid database');
@ -22,9 +23,11 @@ if (!databaseType || !['mysql', 'postgresql'].includes(databaseType)) {
console.log(`Database type detected: ${databaseType}`);
const src = path.resolve(__dirname, `../prisma/schema.${databaseType}.prisma`);
const dest = path.resolve(__dirname, '../prisma/schema.prisma');
const src = path.resolve(__dirname, `../db/${databaseType}`);
const dest = path.resolve(__dirname, '../prisma');
fs.copyFileSync(src, dest);
del.sync(dest);
fse.copySync(src, dest);
console.log(`Copied ${src} to ${dest}`);

View File

@ -4,7 +4,7 @@ const https = require('https');
const chalk = require('chalk');
const src = path.resolve(__dirname, '../lang');
const dest = path.resolve(__dirname, '../public/country');
const dest = path.resolve(__dirname, '../public/intl/country');
const files = fs.readdirSync(src);
const getUrl = locale =>

View File

@ -4,7 +4,7 @@ const https = require('https');
const chalk = require('chalk');
const src = path.resolve(__dirname, '../lang');
const dest = path.resolve(__dirname, '../public/language');
const dest = path.resolve(__dirname, '../public/intl/language');
const files = fs.readdirSync(src);
const getUrl = locale =>

View File

@ -71,4 +71,4 @@ create index event_created_at_idx on event(created_at);
create index event_website_id_idx on event(website_id);
create index event_session_id_idx on event(session_id);
insert into account (username, password, is_admin) values ('admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa', true);
insert into account (username, password, is_admin) values ('admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa', true);

View File

@ -4,12 +4,13 @@ import semver from 'semver';
import { VERSION_CHECK } from 'lib/constants';
import { getItem } from 'lib/web';
const REPO_URL = 'https://api.umami.is/v1/updates';
const UPDATES_URL = 'https://api.umami.is/v1/updates';
const initialState = {
current: process.env.VERSION,
current: process.env.currentVersion,
latest: null,
hasUpdate: false,
checked: false,
};
const store = create(() => ({ ...initialState }));
@ -17,8 +18,8 @@ const store = create(() => ({ ...initialState }));
export async function checkVersion() {
const { current } = store.getState();
const data = await fetch(REPO_URL, {
method: 'get',
const data = await fetch(`${UPDATES_URL}?v=${current}`, {
method: 'GET',
headers: {
Accept: 'application/json',
},
@ -38,11 +39,13 @@ export async function checkVersion() {
produce(state => {
const { latest } = data;
const lastCheck = getItem(VERSION_CHECK);
const hasUpdate = latest && semver.gt(latest, current) && lastCheck?.version !== latest;
const hasUpdate = !!(latest && lastCheck?.version !== latest && semver.gt(latest, current));
state.current = current;
state.latest = latest;
state.hasUpdate = hasUpdate;
state.checked = true;
return state;
}),

View File

@ -1359,22 +1359,22 @@
resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6"
integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==
"@prisma/client@3.14.0":
version "3.14.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.14.0.tgz#bb90405c012fcca11f4647d91153ed4c58f3bd48"
integrity sha512-atb41UpgTR1MCst0VIbiHTMw8lmXnwUvE1KyUCAkq08+wJyjRE78Due+nSf+7uwqQn+fBFYVmoojtinhlLOSaA==
"@prisma/client@3.15.2":
version "3.15.2"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.15.2.tgz#2181398147afc79bfe0d83c03a88dc45b49bd365"
integrity sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==
dependencies:
"@prisma/engines-version" "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
"@prisma/engines-version" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
"@prisma/engines-version@3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a":
version "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a.tgz#4edae57cf6527f35e22cebe75e49214fc0e99ac9"
integrity sha512-D+yHzq4a2r2Rrd0ZOW/mTZbgDIkUkD8ofKgusEI1xPiZz60Daks+UM7Me2ty5FzH3p/TgyhBpRrfIHx+ha20RQ==
"@prisma/engines-version@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e":
version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz#bf5e2373ca68ce7556b967cb4965a7095e93fe53"
integrity sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w==
"@prisma/engines@3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a":
version "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a.tgz#7fa11bc26a51d450185c816cc0ab8cac673fb4bf"
integrity sha512-LwZvI3FY6f43xFjQNRuE10JM5R8vJzFTSmbV9X0Wuhv9kscLkjRlZt0BEoiHmO+2HA3B3xxbMfB5du7ZoSFXGg==
"@prisma/engines@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e":
version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz#f691893df506b93e3cb1ccc15ec6e5ac64e8e570"
integrity sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==
"@react-spring/animated@~9.4.5":
version "9.4.5"
@ -2246,25 +2246,10 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001283:
version "1.0.30001314"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001314.tgz#65c7f9fb7e4594fca0a333bec1d8939662377596"
integrity sha512-0zaSO+TnCHtHJIbpLroX7nsD+vYuOVjl3uzFbJO1wMVbuveJA0RK2WcQA9ZUIOiO0/ArMiMgHJLxfEZhQiC0kw==
caniuse-lite@^1.0.30001313:
version "1.0.30001320"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz#8397391bec389b8ccce328636499b7284ee13285"
integrity sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==
caniuse-lite@^1.0.30001317, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001335:
version "1.0.30001344"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz#8a1e7fdc4db9c2ec79a05e9fd68eb93a761888bb"
integrity sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==
caniuse-lite@^1.0.30001349:
version "1.0.30001352"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz#cc6f5da3f983979ad1e2cdbae0505dccaa7c6a12"
integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==
caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001313, caniuse-lite@^1.0.30001317, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001349:
version "1.0.30001356"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001356.tgz"
integrity sha512-/30854bktMLhxtjieIxsrJBfs2gTM1pel6MXKF3K+RdIVJZcsn2A2QdhsuR4/p9+R204fZw0zCBBhktX8xWuyQ==
chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
@ -5077,12 +5062,12 @@ prettier@^2.6.2:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
prisma@3.14.0:
version "3.14.0"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.14.0.tgz#dd67ece37d7b5373e9fd9588971de0024b49be81"
integrity sha512-l9MOgNCn/paDE+i1K2fp9NZ+Du4trzPTJsGkaQHVBufTGqzoYHuNk8JfzXuIn0Gte6/ZjyKj652Jq/Lc1tp2yw==
prisma@3.15.2:
version "3.15.2"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.15.2.tgz#4ebe32fb284da3ac60c49fbc16c75e56ecf32067"
integrity sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==
dependencies:
"@prisma/engines" "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
"@prisma/engines" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
progress@^2.0.0:
version "2.0.3"