Merge pull request #694 from mikecao/dev

v1.18.0
This commit is contained in:
Mike Cao 2021-06-07 21:46:03 -07:00 committed by GitHub
commit 06535704ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 838 additions and 426 deletions

View File

@ -1,6 +1,8 @@
# Build image
FROM node:12.18-alpine AS build
ARG BASE_PATH
ARG DATABASE_TYPE
ENV BASE_PATH=$BASE_PATH
ENV DATABASE_URL "postgresql://umami:umami@db:5432/umami" \
DATABASE_TYPE=$DATABASE_TYPE
WORKDIR /build

1
assets/bars.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Pro 6.0.0-alpha1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M424 392H24C11 392 0 403 0 416V416C0 429 11 440 24 440H424C437 440 448 429 448 416V416C448 403 437 392 424 392ZM424 72H24C11 72 0 83 0 96V96C0 109 11 120 24 120H424C437 120 448 109 448 96V96C448 83 437 72 424 72ZM424 232H24C11 232 0 243 0 256V256C0 269 11 280 24 280H424C437 280 448 269 448 256V256C448 243 437 232 424 232Z"/></svg>

After

Width:  |  Height:  |  Size: 546 B

1
assets/xmark.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!-- Font Awesome Pro 6.0.0-alpha1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M345 375C354 384 354 400 345 409S320 418 311 409L192 290L73 409C64 418 48 418 39 409S30 384 39 375L158 256L39 137C30 128 30 112 39 103S64 94 73 103L192 222L311 103C320 94 336 94 345 103S354 128 345 137L226 256L345 375Z"/></svg>

After

Width:  |  Height:  |  Size: 441 B

View File

@ -4,11 +4,15 @@ import { FormattedMessage } from 'react-intl';
import Link from 'components/common/Link';
import styles from './Footer.module.css';
import useVersion from 'hooks/useVersion';
import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang';
export default function Footer() {
const { current } = useVersion();
const [locale] = useLocale();
return (
<footer className="container">
<footer className="container" dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}>
<div className={classNames(styles.footer, 'row')}>
<div className="col-12 col-md-4" />
<div className="col-12 col-md-4">

View File

@ -8,19 +8,25 @@ import LanguageButton from 'components/settings/LanguageButton';
import ThemeButton from 'components/settings/ThemeButton';
import UpdateNotice from 'components/common/UpdateNotice';
import UserButton from 'components/settings/UserButton';
import Button from 'components/common/Button';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang';
import XMark from 'assets/xmark.svg';
import Bars from 'assets/bars.svg';
export default function Header() {
const user = useSelector(state => state.user);
const [active, setActive] = useState(false);
const [locale] = useLocale();
function handleClick() {
setActive(state => !state);
}
return (
<nav className="container">
<nav className="container" dir={rtlLocales.includes(locale) ? 'rtl' : 'ltr'}>
{user?.is_admin && <UpdateNotice />}
<div className={classNames(styles.header, 'row align-items-center')}>
<div className={styles.nav}>
@ -30,23 +36,11 @@ export default function Header() {
<Link href={user ? '/' : 'https://umami.is'}>umami</Link>
</div>
</div>
<button
onClick={handleClick}
role="button"
<Button
className={styles.burger}
aria-label="menu"
aria-expanded="false"
>
{active ? (
<div> X </div>
) : (
<>
<span></span>
<span></span>
<span></span>
</>
)}
</button>
icon={active ? <XMark /> : <Bars />}
onClick={handleClick}
/>
{user && (
<div className={styles.items}>
<div className={active ? classNames(styles.active) : ''}>

View File

@ -86,7 +86,6 @@
.items {
display: flex;
justify-content: unset;
align-items: left;
font-size: var(--font-size-normal);
font-weight: 600;
}
@ -106,34 +105,12 @@
.burger {
display: block;
/* color: #4a4a4a; */
background: none;
border: 1px solid var(--gray900);
border-radius: 4px;
cursor: pointer;
height: 3.25rem;
width: 3.25rem;
margin-left: auto;
position: absolute;
right: 0;
top: 0;
}
.burger span {
transform: translateX(25%);
padding: 1px 0px;
margin: 6px 0;
width: 65%;
display: block;
background-color: var(--gray900);
}
.burger div {
/* height: 100%; */
color: var(--gray900);
text-align: center;
margin: auto;
font-size: 1.5rem;
/* transform: translateX(-50%); */
right: 0;
}
}

View File

@ -2,17 +2,25 @@ import React from 'react';
import Head from 'next/head';
import Header from 'components/layout/Header';
import Footer from 'components/layout/Footer';
import useLocale from 'hooks/useLocale';
import { rtlLocales } from 'lib/lang';
export default function Layout({ title, children, header = true, footer = true }) {
const [locale] = useLocale();
const dir = rtlLocales.includes(locale) ? 'rtl' : 'ltr';
return (
<>
<Head>
<title>umami{title && ` - ${title}`}</title>
</Head>
{header && <Header />}
<main className="container">{children}</main>
<main className="container" dir={dir}>
{children}
</main>
{footer && <Footer />}
<div id="__modals" />
<div id="__modals" dir={dir} />
</>
);
}

View File

@ -1,5 +1,4 @@
import React from 'react';
import Head from 'next/head';
import { menuOptions } from 'lib/lang';
import useLocale from 'hooks/useLocale';
import MenuButton from 'components/common/MenuButton';
@ -13,36 +12,32 @@ export default function LanguageButton() {
setLocale(value);
}
switch (locale) {
case 'zh-CN':
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-sc/chinese-simplified-400.css');
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-sc/chinese-simplified-500.css');
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-sc/chinese-simplified-700.css');
break;
case 'zh-TW':
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-tc/chinese-traditional-400.css');
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-tc/chinese-traditional-500.css');
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-tc/chinese-traditional-700.css');
break;
case 'ja-JP':
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-jp/japanese-400.css');
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-jp/japanese-500.css');
import(/* webpackMode: "eager" */ '@fontsource/noto-sans-jp/japanese-700.css');
break;
}
return (
<>
<Head>
{locale === 'zh-CN' && (
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap"
rel="stylesheet"
/>
)}
{locale === 'zh-TW' && (
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&display=swap"
rel="stylesheet"
/>
)}
{locale === 'ja-JP' && (
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap"
rel="stylesheet"
/>
)}
</Head>
<MenuButton
icon={<Globe />}
options={menuOptions}
value={locale}
menuClassName={styles.menu}
renderValue={option => option?.display}
onSelect={handleSelect}
/>
</>
<MenuButton
icon={<Globe />}
options={menuOptions}
value={locale}
menuClassName={styles.menu}
renderValue={option => option?.display}
onSelect={handleSelect}
/>
);
}

100
lang/ar-SA.json Normal file
View File

@ -0,0 +1,100 @@
{
"label.accounts": "الحسابات",
"label.add-account": "إضافة حساب",
"label.add-website": "إضافة موقع",
"label.administrator": "مدير عام؟",
"label.all": "الكل",
"label.all-events": "كافة الأحداث",
"label.all-websites": "كافة المواقع",
"label.back": "للخلف",
"label.cancel": "إلغاء",
"label.change-password": "تغيير كلمة المرور",
"label.confirm-password": "تأكيد كلمة المرور",
"label.copy-to-clipboard": "نسخ للحافظة",
"label.current-password": "كلمة المرور الحالية",
"label.custom-range": "فترة مخصصة",
"label.dashboard": "الشاشة الرئيسية",
"label.date-range": "فترة مخصصة",
"label.default-date-range": "الفترة المخصصة الافتراضية",
"label.delete": "حذف",
"label.delete-account": "حذف الحساب",
"label.delete-website": "حذف الموقع",
"label.dismiss": "اخفاء",
"label.domain": "نطاق",
"label.edit": "تعديل",
"label.edit-account": "تعديل الحساب",
"label.edit-website": "تعديل الموقع",
"label.enable-share-url": "تفعيل مشاركة الرابط",
"label.invalid": "غير صحيح",
"label.invalid-domain": "النطاق غير صحيح",
"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.password": "كلمة المرور",
"label.passwords-dont-match": "كلمة المرور غير متطابقة",
"label.profile": "الملف الشخصي",
"label.realtime": "الوقت الفعلي",
"label.realtime-logs": "سجلات الوقت الفعلي",
"label.refresh": "تحديث",
"label.required": "اجباري",
"label.reset": "اعادة تعيين",
"label.save": "حفظ",
"label.settings": "اعدادات",
"label.share-url": "مشاركة الرابط",
"label.single-day": "يوم واحد",
"label.this-month": "الشهر الحالي",
"label.this-week": "الاسبوع الحالي",
"label.this-year": "السنة الحالية",
"label.timezone": "المنطقة الزمنية",
"label.today": "اليوم",
"label.tracking-code": "كود التتبع",
"label.unknown": "غير معروف",
"label.username": "اسم المستخدم",
"label.view-details": "عرض التفاصيل",
"label.websites": "المواقع",
"message.active-users": "{x} حاليا {x, plural, one {زائر واحد} other {زوار}}",
"message.confirm-delete": "هل أنت متأكد من حذف {target}?",
"message.copied": "تم النسخ!",
"message.delete-warning": "كافة البيانات المرتبطة سيم حذفها ايضا.",
"message.failure": "حدث خطأ ما.",
"message.get-share-url": "احصل على رابط المشاركة",
"message.get-tracking-code": "احصل على كود التتبع",
"message.go-to-settings": "الذهاب إلى الإعدادات",
"message.incorrect-username-password": "اسم المستخدم او كلمة المرور غير صحيحة.",
"message.log.visitor": "زائر من {country} يستخدم {browser} على {os} {device}",
"message.new-version-available": "توجد نسخة جديدة من umami رقم {version} سارع بالتحديث!",
"message.no-data-available": "لا توجد بيانات متاحة.",
"message.no-websites-configured": "لم تقم بإعداد اي موقع.",
"message.page-not-found": "الصفحة غير موجودة.",
"message.powered-by": "مشغل بواسطة {name}",
"message.save-success": "تم الحفظ بنجاح.",
"message.share-url": "هذا الرابط الذي تم مشاركته بشكل عام لـ {target}.",
"message.track-stats": "لتتبع الاحصاىيات لـ {target}, ضع الكود التالي في منطقة {head} في موقعك.",
"message.type-delete": "اكتب {delete} في الحقل التالي لتأكيد الحذف.",
"metrics.actions": "اجراءات",
"metrics.average-visit-time": "متوسط وقت الزيارة",
"metrics.bounce-rate": "معدل الارتداد",
"metrics.browsers": "المتصفحات",
"metrics.countries": "الدول",
"metrics.device.desktop": "كمبيوتر",
"metrics.device.laptop": "لابتوب",
"metrics.device.mobile": "جوال",
"metrics.device.tablet": "تابلت",
"metrics.devices": "الأجهزة",
"metrics.events": "الأحداث",
"metrics.filter.combined": "مجمعة",
"metrics.filter.domain-only": "نطاق فقط",
"metrics.filter.raw": "مفصلة",
"metrics.operating-systems": "نظام التشغيل",
"metrics.page-views": "مشاهدات الصفحة",
"metrics.pages": "الصفحات",
"metrics.referrers": "التحويلات",
"metrics.unique-visitors": "زائرون فريدون",
"metrics.views": "مشاهدات",
"metrics.visitors": "زوار"
}

100
lang/ca-ES.json Normal file
View File

@ -0,0 +1,100 @@
{
"label.accounts": "Comptes",
"label.add-account": "Afegeix compte",
"label.add-website": "Afegeix lloc web",
"label.administrator": "Administrador",
"label.all": "Tots",
"label.all-events": "Tots els esdeveniments",
"label.all-websites": "Tots els llocs web",
"label.back": "Enrere",
"label.cancel": "Cancel·la",
"label.change-password": "Canvia la contrasenya",
"label.confirm-password": "Confirma la contrasenya",
"label.copy-to-clipboard": "Copia al porta-retalls",
"label.current-password": "Contrasenya actual",
"label.custom-range": "Rang personalitzat",
"label.dashboard": "Panell",
"label.date-range": "Interval de dates",
"label.default-date-range": "Interval de dates per defecte",
"label.delete": "Esborra",
"label.delete-account": "Esborra el compte",
"label.delete-website": "Esborra el lloc web",
"label.dismiss": "Descarta",
"label.domain": "Domini",
"label.edit": "Edita",
"label.edit-account": "Edita el compte",
"label.edit-website": "Edita el lloc web",
"label.enable-share-url": "Activa l'enllaç per compartir",
"label.invalid": "Invàlid",
"label.invalid-domain": "Domini invàlid",
"label.last-days": "Últims {x} dies",
"label.last-hours": "Últimes {x} hores",
"label.logged-in-as": "Connectat com {username}",
"label.login": "Connecta't",
"label.logout": "Desconnecta't",
"label.more": "Més",
"label.name": "Nom",
"label.new-password": "Contrasenya nova",
"label.password": "Contrasenya",
"label.passwords-dont-match": "Les contrasenyes no coincideixen",
"label.profile": "Perfil",
"label.realtime": "Temps real",
"label.realtime-logs": "Registres a temps real",
"label.refresh": "Refresca",
"label.required": "Obligatori",
"label.reset": "Restableix",
"label.save": "Desa",
"label.settings": "Configuració",
"label.share-url": "Enllaç per compartir",
"label.single-day": "Un sol dia",
"label.this-month": "Aquest mes",
"label.this-week": "Aquesta setmana",
"label.this-year": "Aquest any",
"label.timezone": "Zona horària",
"label.today": "Avui",
"label.tracking-code": "Codi de seguiment",
"label.unknown": "Desconegut",
"label.username": "Nom d'usuari",
"label.view-details": "Veure els detalls",
"label.websites": "Llocs web",
"message.active-users": "{x} {x, plural, one {visitant actual} other {visitants actuals}}",
"message.confirm-delete": "Segur que vols esborrar {target}?",
"message.copied": "S'ha copiat",
"message.delete-warning": "També s'esborraran totes les dades relacionades.",
"message.failure": "S'ha produït un error.",
"message.get-share-url": "Obté l'enllaç per compartir",
"message.get-tracking-code": "Obté el codi de seguiment",
"message.go-to-settings": "Vés a la configuració",
"message.incorrect-username-password": "Nom d'usuari o contrasenya incorrectes.",
"message.log.visitor": "Visitant de {country} usant {browser} a {os} {device}",
"message.new-version-available": "Hi ha disponible una nova versió d'umami {version}!",
"message.no-data-available": "No hi ha dades disponibles.",
"message.no-websites-configured": "No hi ha cap lloc web configurat.",
"message.page-not-found": "No s'ha trobat la pàgina.",
"message.powered-by": "Funciona amb {name}",
"message.save-success": "S'ha desat amb èxit.",
"message.share-url": "Aquest és l'enllaç públic per compartir de {target}.",
"message.track-stats": "Per seguir les estadístiques de {target}, col·loca el codi següent a la secció {head} del teu lloc web.",
"message.type-delete": "Escriu {delete} al quadre següent per confirmar.",
"metrics.actions": "Accions",
"metrics.average-visit-time": "Temps mitjà de visita",
"metrics.bounce-rate": "Percentatge de rebot",
"metrics.browsers": "Navegadors",
"metrics.countries": "Països",
"metrics.device.desktop": "Escriptori",
"metrics.device.laptop": "Portàtil",
"metrics.device.mobile": "Mòbil",
"metrics.device.tablet": "Tauleta",
"metrics.devices": "Dispositius",
"metrics.events": "Esdeveniments",
"metrics.filter.combined": "Combinat",
"metrics.filter.domain-only": "Només domini",
"metrics.filter.raw": "En cru",
"metrics.operating-systems": "Sistemes operatius",
"metrics.page-views": "Pàgines vistes",
"metrics.pages": "Pàgines",
"metrics.referrers": "Referents",
"metrics.unique-visitors": "Visitants únics",
"metrics.views": "Vistes",
"metrics.visitors": "Visitants"
}

View File

@ -1,32 +1,32 @@
{
"label.accounts": "حساب ها",
"label.accounts": "حسابها",
"label.add-account": "افزودن حساب",
"label.add-website": "افزودن وب سایت",
"label.add-website": "افزودن وبسایت",
"label.administrator": "مدیر",
"label.all": "همه",
"label.all-websites": "همه وب سایت ها",
"label.all-events": "همه رویداد ها",
"label.all-websites": "همه‌ی وب‌سایت‌ها",
"label.all-events": "همه‌ی رویدادها",
"label.back": "برگشت",
"label.cancel": "انصراف",
"label.change-password": "تغییر رمز",
"label.confirm-password": "تایید رمز",
"label.copy-to-clipboard": "کپی به حافظه",
"label.current-password": "رمز فعلی",
"label.custom-range": "محدوده دلخواه",
"label.custom-range": "محدوده‌ی دلخواه",
"label.dashboard": "داشبورد",
"label.date-range": "محدوده تاریخ",
"label.default-date-range": "محدوده پیشفرض تاریخ",
"label.date-range": "محدوده‌ی تاریخ",
"label.default-date-range": "محدوده‌ی پیشفرض تاریخ",
"label.delete": "حذف",
"label.delete-account": "حذف حساب",
"label.delete-website": "حذف وب سایت",
"label.delete-website": "حذف وبسایت",
"label.dismiss": "رد کردن",
"label.domain": "دامنه",
"label.edit": "ویرایش",
"label.edit-account": "ویرایش حساب",
"label.edit-website": "ویرایش وب سایت",
"label.edit-website": "ویرایش وبسایت",
"label.enable-share-url": "فعال کردن اشتراک گذاری URL",
"label.invalid": "نامعتبر",
"label.invalid-domain": "دامنه نامعتبر",
"label.invalid-domain": "دامنه‌ی نامعتبر",
"label.last-days": "لیست {x} روز",
"label.last-hours": "لیست {x} ساعت",
"label.logged-in-as": "وارد شده به عنوان {username}",
@ -36,13 +36,13 @@
"label.name": "نام",
"label.new-password": "رمز جدید",
"label.password": "رمز",
"label.passwords-dont-match": "رمز ها یکسان نیستند",
"label.passwords-dont-match": "رمزها یکسان نیستند",
"label.profile": "پروفایل",
"label.realtime": "آمار هم اکنون",
"label.realtime-logs": "لاگ های هم اکنون",
"label.refresh": "تازه کردن",
"label.required": "لازم",
"label.reset": "ریست",
"label.realtime": "آمار زنده",
"label.realtime-logs": "لاگ‌های زنده",
"label.refresh": "به‌روزرسانی",
"label.required": "ضروری",
"label.reset": "بازنشانی",
"label.save": "ذخیره",
"label.settings": "تنظیمات",
"label.share-url": "به اشتراک گذاری URL",
@ -50,51 +50,51 @@
"label.this-month": "این ماه",
"label.this-week": "این هفته",
"label.this-year": "امسال",
"label.timezone": "منطقه زمانی",
"label.timezone": "منطقه‌ی زمانی",
"label.today": "امروز",
"label.tracking-code": "کد رهگیری",
"label.unknown": "ناشناخته",
"label.username": "نام کاربری",
"label.view-details": "مشاهده جزئیات",
"label.websites": "وب سایت ها",
"label.view-details": "مشاهده‌ی جزئیات",
"label.websites": "وب‌سایت‌ها",
"message.active-users": "{x} هم اکنون {x, plural, one {یک} other {از میان}}",
"message.confirm-delete": "آیا مطمئن هستید می خواهید {target} را حذف کنید?",
"message.confirm-delete": "آیا مطمئن هستید میخواهید {target} را حذف کنید?",
"message.copied": "کپی شد!",
"message.delete-warning": "همه داده های مرتبط هم حذف خواهد شد.",
"message.delete-warning": "همه‌ی داده‌های مرتبط هم حذف خواهد شد.",
"message.failure": "مشکلی پیش آمده است.",
"message.get-share-url": "دریافت URL برای اشتراک گذاری",
"message.get-tracking-code": "گرفتن کد رهگیری",
"message.go-to-settings": "رفتن به تنظیمات",
"message.incorrect-username-password": "نام کاربری / رمز نادرست است.",
"message.log.visitor": "بازدید کننده از کشور {country} با مروگر {browser} در {os} {device}",
"message.new-version-available": "نسخه جدید umami ({version}) وجود است!",
"message.log.visitor": "بازدیدکننده از کشور {country} با مروگر {browser} در {os} {device}",
"message.new-version-available": "نسخه‌ی جدید umami ({version}) موجود است!",
"message.no-data-available": "اطلاعاتی موجود نیست.",
"message.no-websites-configured": "شما هیچ وب سایتی را پیکر بندی نکرده اید.",
"message.no-websites-configured": "شما هیچ وب‌سایتی را پیکربندی نکرده‌اید.",
"message.page-not-found": "صفحه یافت نشد.",
"message.powered-by": "قدرت گرفته توسط {name}",
"message.save-success": "با موفقیت ذخیره شد.",
"message.share-url": "این URL به اشتراک گذاشته شده عمومی برای {target} است.",
"message.track-stats": "برای ردیابی آمار {target}, کد روبرو را در قسمت {head} وب سایت قرار دهید.",
"message.track-stats": "برای ردیابی آمار {target}, کد روبرو را در قسمت {head} وبسایت قرار دهید.",
"message.type-delete": "جهت اطمینان '{delete}' را در کادر زیر بنویسید.",
"metrics.actions": "اقدامات",
"metrics.average-visit-time": "میانگین زمان بازدید",
"metrics.bounce-rate": "نرخ Bounce",
"metrics.browsers": "مروگر ها",
"metrics.countries": "کشور ها",
"metrics.browsers": "مروگرها",
"metrics.countries": "کشورها",
"metrics.device.desktop": "دسکتاپ",
"metrics.device.laptop": "لپ تاپ",
"metrics.device.laptop": "لپتاپ",
"metrics.device.mobile": "موبایل",
"metrics.device.tablet": "تبلت",
"metrics.devices": "دستگاه ها",
"metrics.events": "رویداد ها",
"metrics.devices": "دستگاهها",
"metrics.events": "رویدادها",
"metrics.filter.combined": "ترکیب شده",
"metrics.filter.domain-only": "فقط دامنه",
"metrics.filter.raw": "خام",
"metrics.operating-systems": "سیستم عامل ها",
"metrics.operating-systems": "سیستم‌عامل‌ها",
"metrics.page-views": "بازدید صفحه",
"metrics.pages": "صفحه ها",
"metrics.pages": "صفحهها",
"metrics.referrers": "ارجاع دهندگان",
"metrics.unique-visitors": "بازدید کننده خالص",
"metrics.unique-visitors": "بازدیدکننده‌های یکتا",
"metrics.views": "بازدید",
"metrics.visitors": "بازدید کننده"
"metrics.visitors": "بازدیدکننده"
}

View File

@ -56,7 +56,7 @@
"label.username": "Käyttäjänimi",
"label.view-details": "Katso tiedot",
"label.websites": "Verkkosivut",
"message.active-users": "{x} nykyinen {x, plural, yksi {visitor} muut {visitors}}",
"message.active-users": "{x} nykyinen {x, plural, one {yksi} other {muut}}",
"message.confirm-delete": "Haluatko varmasti poistaa {target}?",
"message.copied": "Kopioitu!",
"message.delete-warning": "Kaikki siihen liittyvät tiedot poistetaan.",

View File

@ -5,6 +5,7 @@
"label.administrator": "Админ",
"label.all": "Бүх",
"label.all-websites": "Бүх вебүүд",
"label.all-events": "Бүх үйл явдал",
"label.back": "Буцах",
"label.cancel": "Цуцлах",
"label.change-password": "Нууц үг солих",

View File

@ -79,7 +79,7 @@
"metrics.average-visit-time": "平均访问时间",
"metrics.bounce-rate": "跳出率",
"metrics.browsers": "浏览器",
"metrics.countries": "国家",
"metrics.countries": "国家/地区",
"metrics.device.desktop": "桌面电脑",
"metrics.device.laptop": "笔记本",
"metrics.device.mobile": "手机",

View File

@ -39,11 +39,11 @@ export function getRandomChars(n) {
return s;
}
export async function hashPassword(password) {
export function hashPassword(password) {
return bcrypt.hashSync(password, SALT_ROUNDS);
}
export async function checkPassword(password, hash) {
export function checkPassword(password, hash) {
return bcrypt.compareSync(password, hash);
}

View File

@ -3,7 +3,7 @@ import { removeTrailingSlash, removeWWW, getDomainName } from './url';
export const urlFilter = (data, { raw }) => {
const isValidUrl = url => {
return url !== '' && !url.startsWith('#');
return url !== '' && url !== null && !url.startsWith('#');
};
if (raw) {
@ -51,7 +51,7 @@ export const refFilter = (data, { domain, domainOnly, raw }) => {
const links = {};
const isValidRef = ref => {
return ref !== '' && !ref.startsWith('/') && !ref.startsWith('#');
return ref !== '' && ref !== null && !ref.startsWith('/') && !ref.startsWith('#');
};
if (raw) {

View File

@ -1,4 +1,5 @@
import {
arSA,
cs,
sk,
da,
@ -14,6 +15,7 @@ import {
id,
it,
ja,
mn,
ms,
nb,
nl,
@ -28,7 +30,9 @@ import {
uk,
zhCN,
zhTW,
ca,
} from 'date-fns/locale';
import arSAMessages from 'lang-compiled/ar-SA.json';
import enMessages from 'lang-compiled/en-US.json';
import nlMessages from 'lang-compiled/nl-NL.json';
import zhCNMessages from 'lang-compiled/zh-CN.json';
@ -60,8 +64,10 @@ import heMessages from 'lang-compiled/he-IL.json';
import itMessages from 'lang-compiled/it-IT.json';
import faIRMessages from 'lang-compiled/fa-IR.json';
import msMYMessages from 'lang-compiled/ms-MY.json';
import caMessages from 'lang-compiled/ca-ES.json';
export const messages = {
'ar-SA': arSAMessages,
'en-US': enMessages,
'nl-NL': nlMessages,
'zh-CN': zhCNMessages,
@ -93,9 +99,13 @@ export const messages = {
'it-IT': itMessages,
'fa-IR': faIRMessages,
'ms-MY': msMYMessages,
'ca-ES': caMessages,
};
export const rtlLocales = ['ar-SA', 'fa-IR'];
export const dateLocales = {
'ar-SA': arSA,
'en-US': enUS,
'nl-NL': nl,
'zh-CN': zhCN,
@ -108,7 +118,7 @@ export const dateLocales = {
'ja-JP': ja,
'es-MX': es,
'fr-FR': fr,
'mn-MN': enUS,
'mn-MN': mn,
'el-GR': el,
'fo-FO': da,
'pt-PT': pt,
@ -127,11 +137,14 @@ export const dateLocales = {
'it-IT': it,
'fa-IR': faIR,
'ms-MY': ms,
'ca-ES': ca,
};
export const menuOptions = [
{ label: 'العربية', value: 'ar-SA', display: 'ar' },
{ label: '中文', value: 'zh-CN', display: 'cn' },
{ label: '中文(繁體)', value: 'zh-TW', display: 'tw' },
{ label: 'Català', value: 'ca-ES', display: 'ca' },
{ label: 'Čeština', value: 'cs-CZ', display: 'cs' },
{ label: 'Dansk', value: 'da-DK', display: 'da' },
{ label: 'Deutsch', value: 'de-DE', display: 'de' },

View File

@ -1,6 +1,6 @@
{
"name": "umami",
"version": "1.17.0",
"version": "1.18.0",
"description": "A simple, fast, website analytics alternative to Google Analytics. ",
"author": "Mike Cao <mike@mikecao.com>",
"license": "MIT",
@ -56,7 +56,11 @@
}
},
"dependencies": {
"@prisma/client": "2.21.2",
"@fontsource/inter": "^4.3.0",
"@fontsource/noto-sans-jp": "^4.3.0",
"@fontsource/noto-sans-sc": "^4.3.0",
"@fontsource/noto-sans-tc": "^4.3.0",
"@prisma/client": "2.23.0",
"@reduxjs/toolkit": "^1.5.1",
"bcryptjs": "^2.4.3",
"chalk": "^4.1.1",
@ -64,19 +68,19 @@
"classnames": "^2.3.1",
"cookie": "^0.4.1",
"cors": "^2.8.5",
"date-fns": "^2.16.1",
"date-fns": "^2.21.3",
"date-fns-tz": "^1.0.12",
"detect-browser": "^5.2.0",
"dotenv": "^8.2.0",
"formik": "^2.2.6",
"immer": "^8.0.1",
"formik": "^2.2.7",
"immer": "^9.0.2",
"ipaddr.js": "^2.0.0",
"is-localhost-ip": "^1.4.0",
"isbot": "^3.0.26",
"jose": "2.0.5",
"maxmind": "^4.3.1",
"moment-timezone": "^0.5.33",
"next": "^10.1.3",
"next": "^10.2.2",
"prompts": "2.4.1",
"prop-types": "^15.7.2",
"react": "^17.0.2",
@ -85,7 +89,7 @@
"react-redux": "^7.2.4",
"react-simple-maps": "^2.3.0",
"react-spring": "^8.0.27",
"react-tooltip": "^4.2.18",
"react-tooltip": "^4.2.19",
"react-use-measure": "^2.0.4",
"react-window": "^1.8.6",
"redux": "^4.1.0",
@ -98,7 +102,7 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"@formatjs/cli": "^2.13.16",
"@formatjs/cli": "^4.2.15",
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-replace": "^2.3.4",
@ -106,26 +110,28 @@
"cross-env": "^7.0.3",
"del": "^6.0.0",
"dotenv-cli": "^4.0.0",
"eslint": "^7.25.0",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.2.0",
"extract-react-intl-messages": "^4.1.1",
"husky": "^4.3.8",
"lint-staged": "^10.5.4",
"lint-staged": "^11.0.0",
"loadtest": "5.1.2",
"npm-run-all": "^4.1.5",
"postcss": "^8.2.15",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^13.0.0",
"postcss-preset-env": "^6.7.0",
"prettier": "^2.2.1",
"postcss-rtlcss": "^3.3.2",
"prettier": "^2.3.0",
"prettier-eslint": "^12.0.0",
"prisma": "2.21.2",
"rollup": "^2.45.2",
"prisma": "2.23.0",
"rollup": "^2.48.0",
"rollup-plugin-hashbang": "^2.2.2",
"rollup-plugin-terser": "^7.0.2",
"stylelint": "^13.13.0",
"stylelint": "^13.13.1",
"stylelint-config-css-modules": "^2.2.0",
"stylelint-config-prettier": "^8.0.1",
"stylelint-config-recommended": "^5.0.0",

View File

@ -10,6 +10,8 @@ import { messages } from 'lib/lang';
import 'styles/variables.css';
import 'styles/bootstrap-grid.css';
import 'styles/index.css';
import '@fontsource/inter/400.css';
import '@fontsource/inter/600.css';
const Intl = ({ children }) => {
const [locale] = useLocale();
@ -38,10 +40,6 @@ export default function App({ Component, pageProps }) {
<link rel="manifest" href={`${basePath}/site.webmanifest`} />
<link rel="mask-icon" href={`${basePath}/safari-pinned-tab.svg`} color="#5bbad5" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
rel="stylesheet"
/>
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

View File

@ -18,7 +18,7 @@ export default async (req, res) => {
const data = {};
if (password) {
data.password = await hashPassword(password);
data.password = hashPassword(password);
}
// Only admin can change these fields
@ -51,7 +51,7 @@ export default async (req, res) => {
return badRequest(res, 'Account already exists');
}
const created = await createAccount({ username, password: await hashPassword(password) });
const created = await createAccount({ username, password: hashPassword(password) });
return ok(res, created);
}

View File

@ -15,13 +15,13 @@ export default async (req, res) => {
if (req.method === 'POST') {
const account = await getAccountById(user_id);
const valid = await checkPassword(current_password, account.password);
const valid = checkPassword(current_password, account.password);
if (!valid) {
return badRequest(res, 'Current password is incorrect');
}
const password = await hashPassword(new_password);
const password = hashPassword(new_password);
const updated = await updateAccount(user_id, { password });

View File

@ -1,5 +1,6 @@
module.exports = {
plugins: [
'postcss-rtlcss',
'postcss-flexbugs-fixes',
[
'postcss-preset-env',

View File

@ -1,14 +1,14 @@
const bcrypt = require('bcrypt');
const bcrypt = require('bcryptjs');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const SALT_ROUNDS = 10;
const hashPassword = password => {
return bcrypt.hash(password, SALT_ROUNDS);
return bcrypt.hashSync(password, SALT_ROUNDS);
};
async function main() {
const password = await hashPassword(process.env.ADMIN_PASSWORD || 'umami');
const password = hashPassword(process.env.ADMIN_PASSWORD || 'umami');
await prisma.account.upsert({
where: { username: 'admin' },
update: {},

View File

@ -0,0 +1 @@
{"AF":"Afganistan","AL":"Alb\u00e0nia","DE":"Alemanya","DZ":"Alg\u00e8ria","AD":"Andorra","AO":"Angola","AI":"Anguilla","AQ":"Ant\u00e0rtida","AG":"Antigua i Barbuda","SA":"Ar\u00e0bia Saudita","AR":"Argentina","AM":"Arm\u00e8nia","AW":"Aruba","AU":"Austr\u00e0lia","AT":"\u00c0ustria","AZ":"Azerbaidjan","BS":"Bahames","BH":"Bahrain","BD":"Bangladesh","BB":"Barbados","BY":"Belar\u00fas","BE":"B\u00e8lgica","BZ":"Belize","BJ":"Ben\u00edn","BM":"Bermudes","BT":"Bhutan","BO":"Bol\u00edvia","BA":"B\u00f2snia i Hercegovina","BW":"Botswana","BV":"Bouvet","BR":"Brasil","BN":"Brunei","BG":"Bulg\u00e0ria","BF":"Burkina Faso","BI":"Burundi","KH":"Cambodja","CM":"Camerun","CA":"Canad\u00e0","CV":"Cap Verd","BQ":"Carib Neerland\u00e8s","VA":"Ciutat del Vatic\u00e0","CO":"Col\u00f2mbia","KM":"Comores","CG":"Congo - Brazzaville","CD":"Congo - Kinshasa","KP":"Corea del Nord","KR":"Corea del Sud","CR":"Costa Rica","CI":"C\u00f4te d\u2019Ivoire","HR":"Cro\u00e0cia","CU":"Cuba","CW":"Cura\u00e7ao","DK":"Dinamarca","DJ":"Djibouti","DM":"Dominica","EG":"Egipte","SV":"El Salvador","AE":"Emirats \u00c0rabs Units","EC":"Equador","ER":"Eritrea","SK":"Eslov\u00e0quia","SI":"Eslov\u00e8nia","ES":"Espanya","US":"Estats Units","EE":"Est\u00f2nia","SZ":"eSwatini","ET":"Eti\u00f2pia","FJ":"Fiji","PH":"Filipines","FI":"Finl\u00e0ndia","FR":"Fran\u00e7a","GA":"Gabon","GM":"G\u00e0mbia","GE":"Ge\u00f2rgia","GH":"Ghana","GI":"Gibraltar","GR":"Gr\u00e8cia","GD":"Grenada","GL":"Groenl\u00e0ndia","GP":"Guadeloupe","GF":"Guaiana Francesa","GU":"Guam","GT":"Guatemala","GG":"Guernsey","GN":"Guinea","GW":"Guinea Bissau","GQ":"Guinea Equatorial","GY":"Guyana","HT":"Hait\u00ed","HN":"Hondures","HK":"Hong Kong (RAE Xina)","HU":"Hongria","YE":"Iemen","CX":"Illa Christmas","RE":"Illa de la Reuni\u00f3","IM":"Illa de Man","HM":"Illa Heard i Illes McDonald","AX":"Illes \u00c5land","KY":"Illes Caiman","CC":"Illes Cocos","CK":"Illes Cook","FO":"Illes F\u00e8roe","GS":"Illes Ge\u00f2rgia del Sud i Sandwich del Sud","FK":"Illes Malvines","MP":"Illes Mariannes del Nord","MH":"Illes Marshall","UM":"Illes Perif\u00e8riques Menors dels EUA","PN":"Illes Pitcairn","SB":"Illes Salom\u00f3","TC":"Illes Turks i Caicos","VG":"Illes Verges Brit\u00e0niques","VI":"Illes Verges Nord-americanes","IN":"\u00cdndia","ID":"Indon\u00e8sia","IR":"Iran","IQ":"Iraq","IE":"Irlanda","IS":"Isl\u00e0ndia","IL":"Israel","IT":"It\u00e0lia","JM":"Jamaica","JP":"Jap\u00f3","JE":"Jersey","JO":"Jord\u00e0nia","KZ":"Kazakhstan","KE":"Kenya","KG":"Kirguizistan","KI":"Kiribati","KW":"Kuwait","LA":"Laos","LS":"Lesotho","LV":"Let\u00f2nia","LB":"L\u00edban","LR":"Lib\u00e8ria","LY":"L\u00edbia","LI":"Liechtenstein","LT":"Litu\u00e0nia","LU":"Luxemburg","MO":"Macau (RAE Xina)","MK":"Maced\u00f2nia del Nord","MG":"Madagascar","MY":"Mal\u00e0isia","MW":"Malawi","MV":"Maldives","ML":"Mali","MT":"Malta","MA":"Marroc","MQ":"Martinica","MU":"Maurici","MR":"Maurit\u00e0nia","YT":"Mayotte","MX":"M\u00e8xic","FM":"Micron\u00e8sia","MZ":"Mo\u00e7ambic","MD":"Mold\u00e0via","MC":"M\u00f2naco","MN":"Mong\u00f2lia","ME":"Montenegro","MS":"Montserrat","MM":"Myanmar (Birm\u00e0nia)","NA":"Nam\u00edbia","NR":"Nauru","NP":"Nepal","NI":"Nicaragua","NE":"N\u00edger","NG":"Nig\u00e8ria","NU":"Niue","NF":"Norfolk","NO":"Noruega","NC":"Nova Caled\u00f2nia","NZ":"Nova Zelanda","OM":"Oman","NL":"Pa\u00efsos Baixos","PK":"Pakistan","PW":"Palau","PA":"Panam\u00e0","PG":"Papua Nova Guinea","PY":"Paraguai","PE":"Per\u00fa","PF":"Polin\u00e8sia Francesa","PL":"Pol\u00f2nia","PT":"Portugal","PR":"Puerto Rico","QA":"Qatar","GB":"Regne Unit","CF":"Rep\u00fablica Centreafricana","ZA":"Rep\u00fablica de Sud-\u00e0frica","DO":"Rep\u00fablica Dominicana","RO":"Romania","RW":"Ruanda","RU":"R\u00fassia","EH":"S\u00e0hara Occidental","BL":"Saint Barth\u00e9lemy","KN":"Saint Christopher i Nevis","SH":"Saint Helena","LC":"Saint Lucia","MF":"Saint Martin","VC":"Saint Vincent i les Grenadines","PM":"Saint-Pierre-et-Miquelon","WS":"Samoa","AS":"Samoa Nord-americana","SM":"San Marino","ST":"S\u00e3o Tom\u00e9 i Pr\u00edncipe","SN":"Senegal","RS":"S\u00e8rbia","SC":"Seychelles","SL":"Sierra Leone","SG":"Singapur","SX":"Sint Maarten","SY":"S\u00edria","SO":"Som\u00e0lia","LK":"Sri Lanka","SD":"Sudan","SS":"Sudan del Sud","SE":"Su\u00e8cia","CH":"Su\u00efssa","SR":"Surinam","SJ":"Svalbard i Jan Mayen","TJ":"Tadjikistan","TH":"Tail\u00e0ndia","TW":"Taiwan","TZ":"Tanz\u00e0nia","IO":"Territori Brit\u00e0nic de l\u2019Oce\u00e0 \u00cdndic","TF":"Territoris Australs Francesos","PS":"Territoris palestins","TL":"Timor Oriental","TG":"Togo","TK":"Tokelau","TO":"Tonga","TT":"Trinitat i Tobago","TN":"Tun\u00edsia","TM":"Turkmenistan","TR":"Turquia","TV":"Tuvalu","TD":"Txad","CZ":"Tx\u00e8quia","UA":"Ucra\u00efna","UG":"Uganda","UY":"Uruguai","UZ":"Uzbekistan","VU":"Vanuatu","VE":"Vene\u00e7uela","VN":"Vietnam","WF":"Wallis i Futuna","CL":"Xile","CN":"Xina","CY":"Xipre","ZM":"Z\u00e0mbia","ZW":"Zimb\u00e0bue"}

View File

@ -1,5 +1,5 @@
require('dotenv').config();
const bcrypt = require('bcrypt');
const bcrypt = require('bcryptjs');
const chalk = require('chalk');
const prompts = require('prompts');
const { PrismaClient } = require('@prisma/client');
@ -25,11 +25,11 @@ const updateAccountByUsername = (username, data) => {
};
const hashPassword = password => {
return bcrypt.hash(password, SALT_ROUNDS);
return bcrypt.hashSync(password, SALT_ROUNDS);
};
const changePassword = async (username, newPassword) => {
const password = await hashPassword(newPassword);
const password = hashPassword(newPassword);
return updateAccountByUsername(username, { password });
};

View File

@ -12,9 +12,12 @@ body {
flex: 1;
font-size: var(--font-size-normal);
overflow-y: overlay;
}
body {
color: var(--gray900);
background: var(--gray75);
overflow-y: overlay;
}
.zh-CN {

769
yarn.lock

File diff suppressed because it is too large Load Diff