Compare commits

...

50 Commits

Author SHA1 Message Date
Iván Eixarch 732fc73fe6 Commit con #3 2023-06-05 18:29:26 +02:00
Mike Cao 586529a5ca
Merge pull request #2009 from franciscao633/master
Github Issues Organizational Templates
2023-04-28 12:49:29 -07:00
Francis Cao 22e2f79bff re order required fields 2023-04-28 11:35:01 -07:00
Francis Cao 6f4cc9e84c typo correction 2023-04-28 11:34:03 -07:00
Francis Cao e77e030f2c update bug report 2023-04-28 11:31:54 -07:00
Francis Cao a43bbc51f6 update feature name 2023-04-28 11:24:31 -07:00
Francis Cao bf26874965 clean up templates 2023-04-28 11:18:53 -07:00
Francis Cao 6cdf9a68d1 fix config and clean up feature request 2023-04-28 11:06:25 -07:00
Francis Cao 402acf563e fix bug report template 2023-04-28 11:01:40 -07:00
Francis Cao 2d6077fe16 clean-up template 2023-04-28 10:59:35 -07:00
Francis Cao c9ec055996 introduce templates 2023-04-28 10:04:23 -07:00
Francis Cao 3f0815855c issues template 2023-04-28 09:52:42 -07:00
Mike Cao 46615fe7eb
Merge pull request #1994 from umami-software/dev
v2.2.0
2023-04-25 21:49:42 -07:00
Mike Cao 9345c9ac9e Apply CSS to flag icons. 2023-04-25 21:18:03 -07:00
Mike Cao 938047ce45 Revert tracker logic. Bump search depth. 2023-04-25 21:03:26 -07:00
Mike Cao 34757e7b2a Bump version v2.2.0. 2023-04-25 20:52:05 -07:00
Mike Cao 01fe8b44a3 Updated packages. 2023-04-25 20:34:56 -07:00
Mike Cao ae85bf696b Allow tracker to traverse all levels to find anchor. 2023-04-25 19:12:17 -07:00
Mike Cao f450fc35fb Fixed city showing undefined. 2023-04-25 16:56:31 -07:00
Mike Cao 4c544361fd Fixed vercel again. 2023-04-25 16:51:42 -07:00
Mike Cao 4ea3eec98c Fixed vercel region issues. 2023-04-25 16:29:47 -07:00
Mike Cao 5864aee043 Fixed vercel issues. 2023-04-25 15:58:52 -07:00
Mike Cao c49e2c6974 Fixed Vercel geolocation. 2023-04-25 14:52:29 -07:00
Mike Cao 02f031bde1 Separate CLOUD_MODE and DISABLE_LOGIN. 2023-04-25 14:48:50 -07:00
Mike Cao 3f1ed750f0 Added error boundary. Fixed #1976. 2023-04-25 14:41:54 -07:00
Mike Cao d73def80c7 Update message bundles. 2023-04-25 12:46:19 -07:00
Mike Cao 4b72918a91 Merge branch 'dev' of https://github.com/umami-software/umami into dev 2023-04-25 12:45:12 -07:00
Mike Cao 44a1c7fa10
Merge pull request #1963 from RikoDEV/patch-1
i18n: update pl-PL translations
2023-04-25 12:44:51 -07:00
Mike Cao c47d246a79
Merge pull request #1974 from atmonshi/update-lang-ar
update AR phrases
2023-04-25 12:44:01 -07:00
Mike Cao 89d37e0045
Merge pull request #1975 from atmonshi/refresh-after-delete-user
small improve to Users List
2023-04-25 12:43:30 -07:00
Mike Cao 4045cfd039
Merge pull request #1980 from juangacovas/patch-3
Update es-MX.json
2023-04-25 12:42:25 -07:00
Mike Cao c3beb10e3b
Merge pull request #1992 from Truimo/dev
fix for #1940: check if there is no parent element
2023-04-25 12:36:26 -07:00
Mike Cao b6f3f57455 Added fallback in case country code not found. 2023-04-25 12:35:09 -07:00
Truimo 4876684461 fix: added check if there is no parent element 2023-04-26 00:10:33 +08:00
Mike Cao 9e11866ddd Remove password from logging. 2023-04-24 22:00:40 -07:00
Mike Cao 1e9a4ad08f Added login debugging. 2023-04-24 21:35:09 -07:00
Mike Cao 92ab391ef8 Use Vercel headers for location. 2023-04-24 19:29:31 -07:00
Mike Cao c23373d164 Check page title exists first. 2023-04-24 18:07:27 -07:00
Juanga Covas 1b8c2c4dde
Update es-MX.json
Update missing translations
2023-04-24 21:27:53 +02:00
Mike Cao 26a9cb67d8 Merge remote-tracking branch 'origin/dev' 2023-04-23 19:55:35 -07:00
Mike Cao 4a51a5db3f Added country flags. 2023-04-23 19:52:44 -07:00
Ash Monsh 8bc0fb92ba Update UsersList.js
show toast after creating user and refresh the list after delete
2023-04-23 05:43:09 +03:00
Ash Monsh ff9a6335d1 update AR phrases 2023-04-23 05:39:35 +03:00
Mike Cao c5bffb97cc Improved error handling for useSession middleware. 2023-04-22 14:17:57 -07:00
RikoDEV 2e3d2924e7
Update pl-PL.json 2023-04-22 10:10:50 +02:00
Mike Cao 8666965930 Updated lang files. 2023-04-22 00:55:55 -07:00
Mike Cao 5027d9d945 Merge branch 'dev' 2023-04-21 22:05:38 -07:00
Mike Cao 6cb2429ee1 Bump version v2.1.1. 2023-04-21 22:05:06 -07:00
Mike Cao ef59e93adc Merge branch 'dev' 2023-04-21 21:29:17 -07:00
Mike Cao 47f63d80c4 Fix tracker. 2023-04-21 21:28:02 -07:00
287 changed files with 661 additions and 500 deletions

32
.github/ISSUE_TEMPLATE/1.bug_report.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: "🐛 Bug Report"
description: Create a bug report for Umami.
body:
- type: textarea
attributes:
label: Describe the Bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: dropdown
attributes:
label: Database
description: What database are you using?
options:
- PostgreSQL
- MySQL
- Umami Cloud
validations:
required: true
- type: textarea
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: input
attributes:
label: Which browser are you using? (if relevant)
description: 'For example: Chrome, Edge, Firefox, etc'
- type: input
attributes:
label: How are you deploying your application? (if relevant)
description: 'For example: Vercel, Railway, Docker, etc'

View File

@ -0,0 +1,10 @@
name: "✨ Feature Request"
description: Create a feature or enhancement request for Umami.
labels: ['enhancement']
body:
- type: textarea
attributes:
label: Describe the feature or enhancement
description: A clear and concise description of what the feature or enhancement is.
validations:
required: true

6
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: "🤔 Ask a question"
url: https://github.com/umami-software/umami/discussions
about: Ask questions and discuss with other community members.

View File

@ -1,6 +1,6 @@
# umami
Umami is a simple, fast, privacy-focused alternative to Google Analytics.
Umami MAMI is a simple, fast, privacy-focused alternative to Google Analytics.
## Getting started

View File

@ -0,0 +1,33 @@
/* eslint-disable no-console */
import { ErrorBoundary as Boundary } from 'react-error-boundary';
import { Button } from 'react-basics';
import useMessages from 'hooks/useMessages';
import styles from './ErrorBoundry.module.css';
const logError = (error, info) => {
console.error(error, info.componentStack);
};
export function ErrorBoundary({ children }) {
const { formatMessage, messages } = useMessages();
const fallbackRender = ({ error, resetErrorBoundary }) => {
console.log({ error });
return (
<div className={styles.error} role="alert">
<h1>{formatMessage(messages.error)}</h1>
<h3>{error.message}</h3>
<pre>{error.stack}</pre>
<Button onClick={resetErrorBoundary}>OK</Button>
</div>
);
};
return (
<Boundary fallbackRender={fallbackRender} onError={logError}>
{children}
</Boundary>
);
}
export default ErrorBoundary;

View File

@ -0,0 +1,19 @@
.error {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
z-index: var(--z-index-overlay);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 600px;
gap: 20px;
}
.error button {
align-self: center;
}

View File

@ -6,24 +6,23 @@ import usePageQuery from 'hooks/usePageQuery';
import useMessages from 'hooks/useMessages';
import styles from './FilterLink.module.css';
export function FilterLink({ id, value, label, externalUrl }) {
export function FilterLink({ id, value, label, externalUrl, children, className }) {
const { formatMessage, labels } = useMessages();
const { resolveUrl, query } = usePageQuery();
const active = query[id] !== undefined;
const selected = query[id] === value;
return (
<div className={styles.row}>
<div
className={classNames(styles.row, className, {
[styles.inactive]: active && !selected,
[styles.active]: active && selected,
})}
>
{children}
{!value && `(${label || formatMessage(labels.unknown)})`}
{value && (
<Link
href={resolveUrl({ [id]: value })}
className={classNames(styles.label, {
[styles.inactive]: active && !selected,
[styles.active]: active && selected,
})}
replace
>
<Link href={resolveUrl({ [id]: value })} className={styles.label} replace>
{safeDecodeURI(label || value)}
</Link>
)}

View File

@ -1,13 +1,18 @@
.row {
display: flex;
align-items: center;
gap: 10px;
}
.row .inactive {
.row.inactive {
color: var(--base500);
}
.row .active {
.row.inactive img {
opacity: 0.35;
}
.row.active {
color: var(--base900);
font-weight: 600;
}

View File

@ -23,7 +23,7 @@ export function DateFilter({ websiteId, value, className }) {
if (data) {
setDateRange({ value, ...getDateRangeValues(new Date(data.createdAt), Date.now()) });
}
} else {
} else if (value !== 'all') {
setDateRange(value);
}
}
@ -61,7 +61,7 @@ export function DateFilter({ websiteId, value, className }) {
value: '90day',
},
{ label: formatMessage(labels.thisYear), value: '1year' },
{
websiteId && {
label: formatMessage(labels.allTime),
value: 'all',
divider: true,
@ -71,7 +71,7 @@ export function DateFilter({ websiteId, value, className }) {
value: 'custom',
divider: true,
},
];
].filter(n => n);
const renderValue = value => {
return value === 'custom' ? (

View File

@ -11,9 +11,14 @@ export function CountriesTable({ websiteId, ...props }) {
function renderLink({ x: code }) {
return (
<div className={locale}>
<FilterLink id="country" value={countryNames[code] && code} label={countryNames[code]} />
</div>
<FilterLink
id="country"
className={locale}
value={countryNames[code] && code}
label={countryNames[code]}
>
<img src={`/images/flags/${code?.toLowerCase() || 'xx'}.png`} alt={code} />
</FilterLink>
);
}

View File

@ -15,11 +15,11 @@ export function RegionsTable({ websiteId, ...props }) {
return regions[x] ? `${regions[x]}, ${countryNames[x.split('-')[0]]}` : x;
};
const renderLink = ({ x }) => {
const renderLink = ({ x: code }) => {
return (
<div className={locale}>
<FilterLink id="region" value={x} label={renderLabel(x)} />
</div>
<FilterLink id="region" className={locale} value={code} label={renderLabel(code)}>
<img src={`/images/flags/${code?.split('-')?.[0]?.toLowerCase() || 'xx'}.png`} alt={code} />
</FilterLink>
);
};

View File

@ -18,10 +18,15 @@ export function UsersList() {
const { toast, showToast } = useToast();
const hasData = data && data.length !== 0;
const handleSave = () => refetch();
const handleSave = () => {
refetch().then(() => showToast({ message: formatMessage(messages.saved), variant: 'success' }));
};
const handleDelete = () =>
showToast({ message: formatMessage(messages.userDeleted), variant: 'success' });
const handleDelete = () => {
refetch().then(() =>
showToast({ message: formatMessage(messages.userDeleted), variant: 'success' }),
);
};
return (
<Page loading={isLoading} error={error}>

View File

@ -1,6 +1,6 @@
{
"label.access-code": "Access code",
"label.actions": "اجراءات",
"label.access-code": "كود الدعوة",
"label.actions": "الإجراءات",
"label.activity-log": "سجل الأحداث",
"label.add-website": "إضافة موقع",
"label.admin": "مدير",
@ -59,7 +59,7 @@
"label.more": "المزيد",
"label.name": "الإسم",
"label.new-password": "كلمة مرور جديدة",
"label.none": "لا شيء",
"label.none": "غير معرف",
"label.operating-systems": "نظام التشغيل",
"label.owner": "المالك",
"label.page-views": "مشاهدات الصفحة",
@ -109,8 +109,8 @@
"label.users": "المستخدمين",
"label.view": "عرض",
"label.view-details": "عرض التفاصيل",
"label.views": "مشاهدات",
"label.visitors": "زوار",
"label.views": "المشاهدات",
"label.visitors": "الزوار",
"label.website-id": "معرف الموقع",
"label.websites": "المواقع",
"label.yesterday": "الأمس",
@ -136,7 +136,7 @@
"message.saved": "تم الحفظ بنجاح.",
"message.share-url": "هذا الرابط الذي تم مشاركته بشكل عام لـ {target}.",
"message.team-already-member": "أنت عضو في المجموعة",
"message.team-not-found": "لم يتم العثور على المجموعة found.",
"message.team-not-found": "لم يتم العثور على المجموعة",
"message.tracking-code": "كود التتبع",
"message.user-deleted": "تم حذف المستخدم.",
"message.visitor-log": "زائر من {country} يستخدم {browser} على {os} {device}",

View File

@ -1,39 +1,39 @@
{
"label.access-code": "Access code",
"label.access-code": "Código de acceso",
"label.actions": "Acciones",
"label.activity-log": "Activity log",
"label.add-website": "Agregar sitio",
"label.activity-log": "Registro de actividad",
"label.add-website": "Nuevo sitio web",
"label.admin": "Administrador",
"label.all": "Todos",
"label.all-time": "Todos los tiempos",
"label.analytics": "Analytics",
"label.analytics": "Analíticas",
"label.average-visit-time": "Tiempo promedio de visita",
"label.back": "Atrás",
"label.bounce-rate": "Porcentaje de rebote",
"label.browsers": "Navegadores",
"label.cancel": "Cancelar",
"label.change-password": "Cambiar contraseña",
"label.cities": "Cities",
"label.clear-all": "Clear all",
"label.confirm": "Confirm",
"label.cities": "Ciudades",
"label.clear-all": "Limpiar todo",
"label.confirm": "Confirmar",
"label.confirm-password": "Confirmar contraseña",
"label.continue": "Continue",
"label.continue": "Continuar",
"label.countries": "Países",
"label.create-team": "Create team",
"label.create-user": "Create user",
"label.created": "Created",
"label.create-team": "Crear equipo",
"label.create-user": "Crear usuario",
"label.created": "Creado",
"label.current-password": "Contraseña actual",
"label.custom-range": "Intervalo personalizado",
"label.dashboard": "Panel de control",
"label.data": "Data",
"label.date-range": "Fechas",
"label.data": "Datos",
"label.date-range": "Intervalo de fechas",
"label.default-date-range": "Intervalo por defecto",
"label.delete": "Eliminar",
"label.delete-team": "Delete team",
"label.delete-user": "Delete user",
"label.delete-team": "Eliminar team",
"label.delete-user": "Eliminar usuario",
"label.delete-website": "Eliminar sitio",
"label.desktop": "Escritorio",
"label.details": "Details",
"label.details": "Detalles",
"label.devices": "Dispositivos",
"label.dismiss": "Ignorar",
"label.domain": "Dominio",
@ -43,18 +43,18 @@
"label.events": "Eventos",
"label.filter-combined": "Combinado",
"label.filter-raw": "Personalizado",
"label.join": "Join",
"label.join-team": "Join team",
"label.join": "Unir",
"label.join-team": "Unir a equipo",
"label.language": "Idioma",
"label.languages": "Idiomas",
"label.laptop": "Portátil",
"label.last-days": "Últimos {x} días",
"label.last-hours": "Últimas {x} horas",
"label.leave": "Leave",
"label.leave-team": "Leave team",
"label.leave": "Abandonar",
"label.leave-team": "Abandonar equipo",
"label.login": "Iniciar sesión",
"label.logout": "Cerrar sesión",
"label.members": "Members",
"label.members": "Miembros",
"label.mobile": "Móvil",
"label.more": "Más",
"label.name": "Nombre",
@ -67,80 +67,80 @@
"label.password": "Contraseña",
"label.powered-by": "Analíticas de {name}",
"label.profile": "Perfil",
"label.queries": "Queries",
"label.queries": "Consultas",
"label.query-parameters": "Parámetros de petición",
"label.realtime": "Tiempo real",
"label.referrers": "Referido desde",
"label.refresh": "Actualizar",
"label.regenerate": "Regenerate",
"label.regions": "Regions",
"label.remove": "Remove",
"label.regenerate": "Regenerar",
"label.regions": "Regiones",
"label.remove": "Quitar",
"label.required": "Obligatorio",
"label.reset": "Reiniciar",
"label.reset-website": "Reiniciar estadísticas",
"label.role": "Role",
"label.role": "Rol",
"label.save": "Guardar",
"label.screens": "Pantallas",
"label.select-website": "Select website",
"label.sessions": "Sessions",
"label.select-website": "Seleccionar sitio web",
"label.sessions": "Sesiones",
"label.settings": "Configuraciones",
"label.share-url": "Compartir URL",
"label.single-day": "Dia",
"label.single-day": "Día",
"label.tablet": "Tableta",
"label.team": "Team",
"label.team-guest": "Team guest",
"label.team-id": "Team ID",
"label.team-member": "Team member",
"label.team-owner": "Team owner",
"label.teams": "Teams",
"label.team": "Equipo",
"label.team-guest": "Invitado de equipo",
"label.team-id": "ID de equipo",
"label.team-member": "Miembro de equipo",
"label.team-owner": "Admin. del equipo",
"label.teams": "Equipos",
"label.theme": "Tema",
"label.this-month": "Este mes",
"label.this-week": "Esta semana",
"label.this-year": "Este año",
"label.timezone": "Zona horaria",
"label.title": "Title",
"label.title": "Título",
"label.today": "Hoy",
"label.toggle-charts": "Alternar gráficas",
"label.tracking-code": "Código de rastreo",
"label.unique-visitors": "Visitantes únicos",
"label.unknown": "Desconocida",
"label.user": "User",
"label.user": "Usuario",
"label.username": "Nombre de usuario",
"label.users": "Users",
"label.view": "View",
"label.users": "Usuarios",
"label.view": "Visualizar",
"label.view-details": "Ver detalles",
"label.views": "Vistas",
"label.visitors": "Visitantes",
"label.website-id": "Website ID",
"label.website-id": "ID del sitio web",
"label.websites": "Sitios",
"label.yesterday": "Ayer",
"message.active-users": "{x} {x, plural, one {activo} other {activos}}",
"message.confirm-delete": "¿Estás seguro(a) de querer eliminar {target}?",
"message.confirm-leave": "Are you sure you want to leave {target}?",
"message.confirm-reset": "¿Seguro que deseas restablecer las estadísticas de {target}?",
"message.delete-website": "Eliminar sitio",
"message.confirm-delete": "¿Seguro que quieres eliminar {target}?",
"message.confirm-leave": "¿Seguro que quieres abandonar {target}?",
"message.confirm-reset": "¿Seguro que quieres BORRAR las analíticas de {target}?",
"message.delete-website": "Eliminar sitio web",
"message.delete-website-warning": "Toda la información relacionada será eliminada.",
"message.error": "Algo falló.",
"message.event-log": "{event} on {url}",
"message.event-log": "{event} en {url}",
"message.go-to-settings": "Ir a la configuración",
"message.incorrect-username-password": "Nombre de usuario o contraseña incorrectos.",
"message.invalid-domain": "Dominio inválido",
"message.min-password-length": "Minimum length of {n} characters",
"message.min-password-length": "Longitud mínima de {n} caracteres",
"message.no-data-available": "No hay información disponible.",
"message.no-match-password": "Las contraseñas no coinciden",
"message.no-teams": "You have not created any teams.",
"message.no-users": "There are no users.",
"message.no-teams": "No has creado ningún equipo.",
"message.no-users": "No hay usuarios.",
"message.page-not-found": "Página no encontrada",
"message.reset-website": "Reiniciar estadísticas",
"message.reset-website-warning": "Todas las estadísticas de esta página serán eliminadas, pero el código de rastreo permanecerá intacto.",
"message.saved": "Guardado exitosamente.",
"message.saved": "Guardado.",
"message.share-url": "Esta es la URL compartida públicamente para {target}.",
"message.team-already-member": "You are already a member of the team.",
"message.team-not-found": "Team not found.",
"message.team-already-member": "Ya eres miembro de este equipo.",
"message.team-not-found": "Equipo no encontrado.",
"message.tracking-code": "Código de rastreo",
"message.user-deleted": "User deleted.",
"message.user-deleted": "Usuario eliminado.",
"message.visitor-log": "Visitante desde {country} usando {browser} en {os} {device}",
"messages.no-team-websites": "This team does not have any websites.",
"messages.no-team-websites": "Este equipo no tiene ningún sitio web configurado.",
"messages.no-websites-configured": "No tienes ningún sitio configurado.",
"messages.team-websites-info": "Websites can be viewed by anyone on the team."
"messages.team-websites-info": "Las analíticas de tus sitios pueden verse por cualquier miembro del equipo."
}

View File

@ -1,27 +1,27 @@
{
"label.access-code": "Access code",
"label.access-code": "Kod dostępu",
"label.actions": "Działania",
"label.activity-log": "Activity log",
"label.activity-log": "Dziennik aktywności",
"label.add-website": "Dodaj witrynę",
"label.admin": "Administrator",
"label.all": "Wszystkie",
"label.all-time": "Cały czas",
"label.analytics": "Analytics",
"label.analytics": "Analityka",
"label.average-visit-time": "Średni czas wizyty",
"label.back": "Powrót",
"label.bounce-rate": "Współczynnik odrzuceń",
"label.browsers": "Przeglądarki",
"label.cancel": "Anuluj",
"label.change-password": "Zmień hasło",
"label.cities": "Cities",
"label.clear-all": "Clear all",
"label.confirm": "Confirm",
"label.cities": "Miasta",
"label.clear-all": "Wyczyść wszystko",
"label.confirm": "Potwierdź",
"label.confirm-password": "Potwierdź hasło",
"label.continue": "Continue",
"label.continue": "Kontynuuj",
"label.countries": "Kraje",
"label.create-team": "Create team",
"label.create-user": "Create user",
"label.created": "Created",
"label.create-team": "Utwórz zespół",
"label.create-user": "Utwórz użytkownika",
"label.created": "Utworzony",
"label.current-password": "Aktualne hasło",
"label.custom-range": "Zakres niestandardowy",
"label.dashboard": "Panel",
@ -29,11 +29,11 @@
"label.date-range": "Zakres dat",
"label.default-date-range": "Domyślny zakres dat",
"label.delete": "Usuń",
"label.delete-team": "Delete team",
"label.delete-user": "Delete user",
"label.delete-team": "Usuń zespół",
"label.delete-user": "Usuń użytkownika",
"label.delete-website": "Usuń witrynę",
"label.desktop": "Komputer",
"label.details": "Details",
"label.details": "Szczegóły",
"label.devices": "Urządzenia",
"label.dismiss": "Odrzuć",
"label.domain": "Domena",
@ -43,18 +43,18 @@
"label.events": "Zdarzenia",
"label.filter-combined": "Połączone",
"label.filter-raw": "Surowe dane",
"label.join": "Join",
"label.join-team": "Join team",
"label.join": "Dołącz",
"label.join-team": "Dołącz do zespołu",
"label.language": "Język",
"label.languages": "Języki",
"label.laptop": "Laptop",
"label.last-days": "Ostatnie {x} dni",
"label.last-hours": "Ostatnie {x} godzin",
"label.leave": "Leave",
"label.leave-team": "Leave team",
"label.leave": "Opuść",
"label.leave-team": "Opuść zespół",
"label.login": "Zaloguj się",
"label.logout": "Wyloguj",
"label.members": "Members",
"label.members": "Członkowie",
"label.mobile": "Smartfon",
"label.more": "Więcej",
"label.name": "Nazwa",
@ -67,80 +67,80 @@
"label.password": "Hasło",
"label.powered-by": "Obsługiwane przez {name}",
"label.profile": "Profil",
"label.queries": "Queries",
"label.queries": "Zapytania",
"label.query-parameters": "Parametry query",
"label.realtime": "Czas rzeczywisty",
"label.referrers": "Źródła odsyłające",
"label.refresh": "Odśwież",
"label.regenerate": "Regenerate",
"label.regions": "Regions",
"label.remove": "Remove",
"label.regenerate": "Wygeneruj ponownie",
"label.regions": "Regiony",
"label.remove": "Usuń",
"label.required": "Wymagany",
"label.reset": "Zresetuj",
"label.reset-website": "Zresetuj statystyki",
"label.role": "Role",
"label.save": "Zapisz",
"label.screens": "Ekrany",
"label.select-website": "Select website",
"label.sessions": "Sessions",
"label.select-website": "Wybierz witrynę",
"label.sessions": "Sesje",
"label.settings": "Ustawienia",
"label.share-url": "Udostępnij adres URL",
"label.single-day": "W tym dniu",
"label.tablet": "Tablet",
"label.team": "Team",
"label.team-guest": "Team guest",
"label.team-id": "Team ID",
"label.team-member": "Team member",
"label.team-owner": "Team owner",
"label.teams": "Teams",
"label.team": "Zespół",
"label.team-guest": "Gość zespołu",
"label.team-id": "ID zespołu",
"label.team-member": "Członek zespołu",
"label.team-owner": "Właściciel zespołu",
"label.teams": "Zespoły",
"label.theme": "Motyw",
"label.this-month": "W tym miesiącu",
"label.this-week": "W tym tygodniu",
"label.this-year": "W tym roku",
"label.timezone": "Strefa czasowa",
"label.title": "Title",
"label.title": "Tytuł",
"label.today": "Dzisiaj",
"label.toggle-charts": "Przełącz wykresy",
"label.tracking-code": "Kod śledzenia",
"label.unique-visitors": "Unikalni odwiedzający",
"label.unknown": "Nieznany",
"label.user": "User",
"label.user": "Użytkownik",
"label.username": "Nazwa użytkownika",
"label.users": "Users",
"label.view": "View",
"label.users": "Użytkownicy",
"label.view": "Zobacz",
"label.view-details": "Pokaż szczegóły",
"label.views": "Wyświetlenia",
"label.visitors": "Odwiedzający",
"label.website-id": "Website ID",
"label.website-id": "ID witryny",
"label.websites": "Witryny",
"label.yesterday": "Wczoraj",
"message.active-users": "{x} aktualnie {x, plural, one {odwiedzający} other {odwiedzających}}",
"message.confirm-delete": "Czy na pewno chcesz usunąć {target}?",
"message.confirm-leave": "Are you sure you want to leave {target}?",
"message.confirm-leave": "Czy na pewno chcesz opuścić {target}?",
"message.confirm-reset": "Czy na pewno chcesz zresetować statystyki {target}?",
"message.delete-website": "Usuń witrynę",
"message.delete-website-warning": "Wszystkie powiązane dane również zostaną usunięte.",
"message.error": "Coś poszło nie tak.",
"message.event-log": "{event} on {url}",
"message.event-log": "{event} na {url}",
"message.go-to-settings": "Przejdź do ustawień",
"message.incorrect-username-password": "Nieprawidłowa nazwa użytkownika/hasło.",
"message.invalid-domain": "Nieprawidłowa witryna",
"message.min-password-length": "Minimum length of {n} characters",
"message.min-password-length": "Minimalna długość {n} znaków",
"message.no-data-available": "Brak dostępnych danych.",
"message.no-match-password": "Hasła się nie zgadzają",
"message.no-teams": "You have not created any teams.",
"message.no-users": "There are no users.",
"message.no-teams": "Nie stworzyłeś żadnych zespołów.",
"message.no-users": "Nie ma żadnych użytkowników.",
"message.page-not-found": "Strona nie znaleziona.",
"message.reset-website": "Zresetuj statystyki",
"message.reset-website-warning": "Wszystkie statystyki tej witryny zostaną usunięte, ale kod śledzenia pozostanie nienaruszony.",
"message.saved": "Zapisano pomyślnie.",
"message.share-url": "To jest publicznie udostępniany adres URL dla {target}.",
"message.team-already-member": "You are already a member of the team.",
"message.team-not-found": "Team not found.",
"message.team-already-member": "Jesteś już członkiem zespołu.",
"message.team-not-found": "Nie znaleziono zespołu.",
"message.tracking-code": "Kod śledzenia",
"message.user-deleted": "User deleted.",
"message.user-deleted": "Użytkownik usunięty.",
"message.visitor-log": "Odwiedzający z {country} używa {browser} na {os} {device}",
"messages.no-team-websites": "This team does not have any websites.",
"messages.no-team-websites": "Ten zespół nie ma żadnych witryn internetowych.",
"messages.no-websites-configured": "Nie masz skonfigurowanych żadnych witryn internetowych.",
"messages.team-websites-info": "Websites can be viewed by anyone on the team."
"messages.team-websites-info": "Strony internetowe mogą być przeglądane przez każdego członka zespołu."
}

View File

@ -53,7 +53,7 @@ export function parseDateRange(value, locale = 'en-US') {
const match = value.match(/^(?<num>[0-9-]+)(?<unit>hour|day|week|month|year)$/);
if (!match) return;
if (!match) return {};
const { num, unit } = match.groups;

View File

@ -56,12 +56,24 @@ export function getDevice(screen, os) {
}
}
export async function getLocation(ip) {
export async function getLocation(ip, req) {
// Ignore local ips
if (await isLocalhost(ip)) {
return;
}
if (req.headers['x-vercel-ip-country']) {
const country = req.headers['x-vercel-ip-country'];
const region = req.headers['x-vercel-ip-country-region'];
const city = req.headers['x-vercel-ip-city'];
return {
country,
subdivision1: region,
city: city ? decodeURIComponent(city) : undefined,
};
}
// Database lookup
if (!lookup) {
const dir = path.join(process.cwd(), 'geo');
@ -70,18 +82,21 @@ export async function getLocation(ip) {
}
const result = lookup.get(ip);
const country = result?.country?.iso_code ?? result?.registered_country?.iso_code;
const subdivision1 = result?.subdivisions?.[0]?.iso_code;
const subdivision2 = result?.subdivisions?.[1]?.names?.en;
const city = result?.city?.names?.en;
return { country, subdivision1, subdivision2, city };
if (result) {
return {
country: result.country?.iso_code ?? result?.registered_country?.iso_code,
subdivision1: result.subdivisions?.[0]?.iso_code,
subdivision2: result.subdivisions?.[1]?.names?.en,
city: result.city?.names?.en,
};
}
}
export async function getClientInfo(req: NextApiRequestCollect, { screen }) {
const userAgent = req.headers['user-agent'];
const ip = getIpAddress(req);
const location = await getLocation(ip);
const location = await getLocation(ip, req);
const country = location?.country;
const subdivision1 = location?.subdivision1;
const subdivision2 = location?.subdivision2;

View File

@ -20,14 +20,19 @@ export const useCors = createMiddleware(
);
export const useSession = createMiddleware(async (req, res, next) => {
const session = await findSession(req as NextApiRequestCollect);
try {
const session = await findSession(req as NextApiRequestCollect);
if (!session) {
log('useSession: Session not found');
return badRequest(res, 'Session not found.');
if (!session) {
log('useSession: Session not found');
return badRequest(res, 'Session not found.');
}
(req as any).session = session;
} catch (e: any) {
return badRequest(res, e.message);
}
(req as any).session = session;
next();
});

View File

@ -11,7 +11,7 @@ export async function findSession(req: NextApiRequestCollect) {
const { payload } = getJsonBody<CollectRequestBody>(req);
if (!payload) {
return null;
throw new Error('Invalid payload.');
}
// Check if cache token is passed
@ -29,14 +29,14 @@ export async function findSession(req: NextApiRequestCollect) {
const { website: websiteId, hostname, screen, language } = payload;
if (!validate(websiteId)) {
return null;
throw new Error('Invalid website ID.');
}
// Find website
const website = await loadWebsite(websiteId);
if (!website) {
throw new Error(`Website not found: ${websiteId}`);
throw new Error(`Website not found: ${websiteId}.`);
}
const { userAgent, browser, os, ip, country, subdivision1, subdivision2, city, device } =

View File

@ -65,10 +65,10 @@ const redirects = [
},
];
if (process.env.CLOUD_MODE) {
if (process.env.CLOUD_MODE && process.env.DISABLE_LOGIN && process.env.CLOUD_URL) {
redirects.push({
source: '/login',
destination: CLOUD_URL,
destination: process.env.CLOUD_URL,
permanent: false,
});
}

View File

@ -1,6 +1,6 @@
{
"name": "umami",
"version": "2.1.0",
"version": "2.2.0",
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
"author": "Mike Cao <mike@mikecao.com>",
"license": "MIT",
@ -32,7 +32,7 @@
"merge-messages": "node scripts/merge-messages.js",
"generate-lang": "npm-run-all extract-messages merge-messages",
"format-lang": "node scripts/format-lang.js",
"compile-lang": "formatjs compile-folder --ast build public/intl/messages",
"compile-lang": "formatjs compile-folder --ast build/messages public/intl/messages",
"check-lang": "node scripts/check-lang.js",
"download-country-names": "node scripts/download-country-names.js",
"download-language-names": "node scripts/download-language-names.js",
@ -97,6 +97,7 @@
"react-basics": "^0.77.0",
"react-beautiful-dnd": "^13.1.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.4",
"react-intl": "^5.24.7",
"react-simple-maps": "^2.3.0",
"react-spring": "^9.4.4",

View File

@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import Head from 'next/head';
import Script from 'next/script';
import { useRouter } from 'next/router';
import ErrorBoundary from 'components/common/ErrorBoundary';
import useLocale from 'hooks/useLocale';
import useConfig from 'hooks/useConfig';
import '@fontsource/inter/400.css';
@ -41,21 +42,37 @@ export default function App({ Component, pageProps }) {
textComponent={Wrapper}
onError={() => null}
>
<Head>
<link rel="icon" href={`${basePath}/favicon.ico`} />
<link rel="apple-touch-icon" sizes="180x180" href={`${basePath}/apple-touch-icon.png`} />
<link rel="icon" type="image/png" sizes="32x32" href={`${basePath}/favicon-32x32.png`} />
<link rel="icon" type="image/png" sizes="16x16" href={`${basePath}/favicon-16x16.png`} />
<link rel="manifest" href={`${basePath}/site.webmanifest`} />
<link rel="mask-icon" href={`${basePath}/safari-pinned-tab.svg`} color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#fafafa" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#2f2f2f" media="(prefers-color-scheme: dark)" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex,nofollow" />
</Head>
<Component {...pageProps} />
{!pathname.includes('/share/') && <Script src={`${basePath}/telemetry.js`} />}
<ErrorBoundary>
<Head>
<link rel="icon" href={`${basePath}/favicon.ico`} />
<link
rel="apple-touch-icon"
sizes="180x180"
href={`${basePath}/apple-touch-icon.png`}
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href={`${basePath}/favicon-32x32.png`}
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href={`${basePath}/favicon-16x16.png`}
/>
<link rel="manifest" href={`${basePath}/site.webmanifest`} />
<link rel="mask-icon" href={`${basePath}/safari-pinned-tab.svg`} color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#fafafa" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#2f2f2f" media="(prefers-color-scheme: dark)" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex,nofollow" />
</Head>
<Component {...pageProps} />
{!pathname.includes('/share/') && <Script src={`${basePath}/telemetry.js`} />}
</ErrorBoundary>
</IntlProvider>
</QueryClientProvider>
);

View File

@ -1,3 +1,4 @@
import debug from 'debug';
import { NextApiResponse } from 'next';
import {
ok,
@ -13,6 +14,8 @@ import { secret } from 'lib/crypto';
import { NextApiRequestQueryBody, User } from 'lib/types';
import { setAuthKey } from 'lib/auth';
const log = debug('umami:auth');
export interface LoginRequestBody {
username: string;
password: string;
@ -51,6 +54,8 @@ export default async (
});
}
log('Login failed:', { username, user });
return unauthorized(res, 'message.incorrect-username-password');
}

View File

@ -16,7 +16,7 @@ export default function LoginPage({ disabled }) {
export async function getServerSideProps() {
return {
props: {
disabled: !!(process.env.DISABLE_LOGIN || process.env.CLOUD_MODE),
disabled: !!process.env.DISABLE_LOGIN,
},
};
}

BIN
public/images/flags/ad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

BIN
public/images/flags/ae.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

BIN
public/images/flags/af.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

BIN
public/images/flags/ag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

BIN
public/images/flags/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

BIN
public/images/flags/al.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
public/images/flags/am.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

BIN
public/images/flags/ao.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

BIN
public/images/flags/aq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

BIN
public/images/flags/ar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

BIN
public/images/flags/as.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

BIN
public/images/flags/at.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

BIN
public/images/flags/au.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

BIN
public/images/flags/aw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

BIN
public/images/flags/ax.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

BIN
public/images/flags/az.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

BIN
public/images/flags/ba.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

BIN
public/images/flags/bb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

BIN
public/images/flags/bd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

BIN
public/images/flags/be.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

BIN
public/images/flags/bf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

BIN
public/images/flags/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

BIN
public/images/flags/bh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

BIN
public/images/flags/bi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

BIN
public/images/flags/bj.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

BIN
public/images/flags/bl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

BIN
public/images/flags/bm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

BIN
public/images/flags/bn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

BIN
public/images/flags/bo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

BIN
public/images/flags/bq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

BIN
public/images/flags/br.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

BIN
public/images/flags/bs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

BIN
public/images/flags/bt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

BIN
public/images/flags/bv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

BIN
public/images/flags/bw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

BIN
public/images/flags/by.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

BIN
public/images/flags/bz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

BIN
public/images/flags/ca.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

BIN
public/images/flags/cc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

BIN
public/images/flags/cd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

BIN
public/images/flags/cf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

BIN
public/images/flags/cg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

BIN
public/images/flags/ch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

BIN
public/images/flags/ci.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

BIN
public/images/flags/ck.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

BIN
public/images/flags/cl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

BIN
public/images/flags/cm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

BIN
public/images/flags/cn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

BIN
public/images/flags/co.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

BIN
public/images/flags/cr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

BIN
public/images/flags/cu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

BIN
public/images/flags/cv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

BIN
public/images/flags/cw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

BIN
public/images/flags/cx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

BIN
public/images/flags/cy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
public/images/flags/cz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
public/images/flags/de.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

BIN
public/images/flags/dj.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

BIN
public/images/flags/dk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

BIN
public/images/flags/dm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

BIN
public/images/flags/do.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

BIN
public/images/flags/dz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

BIN
public/images/flags/ec.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

BIN
public/images/flags/ee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

BIN
public/images/flags/eg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

BIN
public/images/flags/eh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

BIN
public/images/flags/er.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

BIN
public/images/flags/es.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

BIN
public/images/flags/et.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

BIN
public/images/flags/fi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

BIN
public/images/flags/fj.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

BIN
public/images/flags/fk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

BIN
public/images/flags/fm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

BIN
public/images/flags/fo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

BIN
public/images/flags/fr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

BIN
public/images/flags/ga.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Some files were not shown because too many files have changed in this diff Show More