Merge branch 'umami-software:master' into master

This commit is contained in:
clumsy 2025-11-19 20:14:14 +01:00 committed by GitHub
commit cf7d6f4dc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 848 additions and 533 deletions

View file

@ -61,7 +61,7 @@ function getDateStringSQL(data: any, unit: string = 'utc', timezone?: string) {
function getDateSQL(field: string, unit: string, timezone?: string) {
if (timezone) {
return `toDateTime(date_trunc('${unit}', ${field}, '${timezone}'), '${timezone}')`;
return `toDateTime(date_trunc('${unit}', ${field}, '${timezone}'))`;
}
return `toDateTime(date_trunc('${unit}', ${field}))`;
}

View file

@ -5,6 +5,7 @@ export const TIMEZONE_CONFIG = 'umami.timezone';
export const DATE_RANGE_CONFIG = 'umami.date-range';
export const THEME_CONFIG = 'zen.theme';
export const DASHBOARD_CONFIG = 'umami.dashboard';
export const LAST_TEAM_CONFIG = 'umami.last-team';
export const VERSION_CHECK = 'umami.version-check';
export const SHARE_TOKEN_HEADER = 'x-umami-share-token';
export const HOMEPAGE_URL = 'https://umami.is';
@ -658,3 +659,24 @@ export const CURRENCIES = [
{ id: 'OMR', name: 'Omani Rial' },
{ id: 'GHS', name: 'Ghanaian Cedi' },
];
export const TIMEZONE_LEGACY: Record<string, string> = {
'Asia/Batavia': 'Asia/Jakarta',
'Asia/Calcutta': 'Asia/Kolkata',
'Asia/Chongqing': 'Asia/Shanghai',
'Asia/Harbin': 'Asia/Shanghai',
'Asia/Jayapura': 'Asia/Pontianak',
'Asia/Katmandu': 'Asia/Kathmandu',
'Asia/Macao': 'Asia/Macau',
'Asia/Rangoon': 'Asia/Yangon',
'Asia/Saigon': 'Asia/Ho_Chi_Minh',
'Europe/Kiev': 'Europe/Kyiv',
'Europe/Zaporozhye': 'Europe/Kyiv',
'Etc/UTC': 'UTC',
'US/Arizona': 'America/Phoenix',
'US/Central': 'America/Chicago',
'US/Eastern': 'America/New_York',
'US/Mountain': 'America/Denver',
'US/Pacific': 'America/Los_Angeles',
'US/Samoa': 'Pacific/Pago_Pago',
};

View file

@ -1,44 +1,45 @@
import {
addMinutes,
addHours,
addDays,
addMonths,
addYears,
subMinutes,
subHours,
subDays,
subMonths,
subYears,
startOfMinute,
startOfHour,
startOfDay,
startOfWeek,
startOfMonth,
startOfYear,
endOfHour,
endOfDay,
endOfWeek,
endOfMonth,
endOfYear,
differenceInMinutes,
differenceInHours,
differenceInCalendarDays,
differenceInCalendarWeeks,
differenceInCalendarMonths,
differenceInCalendarYears,
format,
max,
min,
isDate,
addWeeks,
subWeeks,
endOfMinute,
isSameDay,
isBefore,
isEqual,
} from 'date-fns';
import { getDateLocale } from '@/lib/lang';
import { DateRange } from '@/lib/types';
import {
addDays,
addHours,
addMinutes,
addMonths,
addWeeks,
addYears,
differenceInCalendarDays,
differenceInCalendarMonths,
differenceInCalendarWeeks,
differenceInCalendarYears,
differenceInHours,
differenceInMinutes,
endOfDay,
endOfHour,
endOfMinute,
endOfMonth,
endOfWeek,
endOfYear,
format,
isBefore,
isDate,
isEqual,
isSameDay,
max,
min,
startOfDay,
startOfHour,
startOfMinute,
startOfMonth,
startOfWeek,
startOfYear,
subDays,
subHours,
subMinutes,
subMonths,
subWeeks,
subYears,
} from 'date-fns';
import { utcToZonedTime } from 'date-fns-tz';
export const TIME_UNIT = {
minute: 'minute',
@ -135,7 +136,7 @@ export function parseDateValue(value: string) {
return { num: +num, unit };
}
export function parseDateRange(value: string, locale = 'en-US'): DateRange {
export function parseDateRange(value: string, locale = 'en-US', timezone?: string): DateRange {
if (typeof value !== 'string') {
return null;
}
@ -156,7 +157,8 @@ export function parseDateRange(value: string, locale = 'en-US'): DateRange {
};
}
const now = new Date();
const date = new Date();
const now = timezone ? utcToZonedTime(date, timezone) : date;
const dateLocale = getDateLocale(locale);
const { num = 1, unit } = parseDateValue(value);
@ -367,3 +369,7 @@ export function getDateRangeValue(startDate: Date, endDate: Date) {
export function getMonthDateRangeValue(date: Date) {
return getDateRangeValue(startOfMonth(date), endOfMonth(date));
}
export function isInvalidDate(date: any) {
return date instanceof Date && isNaN(date.getTime());
}

View file

@ -1,16 +1,16 @@
export const IP_ADDRESS_HEADERS = [
'true-client-ip', // CDN
'x-real-ip', // Reverse proxy
'x-forwarded-for',
'cf-connecting-ip', // Cloudflare
'fastly-client-ip', // Fastly
'x-nf-client-connection-ip', // Netlify
'do-connecting-ip', // Digital Ocean
'x-appengine-user-ip', // Google App Ending
'x-real-ip', // Reverse proxy
'x-appengine-user-ip', // Google App Engine
'x-forwarded-for',
'forwarded',
'x-client-ip',
'x-cluster-client-ip',
'x-forwarded',
'forwarded',
];
export function getIpAddress(headers: Headers) {

View file

@ -27,6 +27,14 @@ const DATE_FORMATS = {
year: 'YYYY-01-01 HH24:00:00',
};
const DATE_FORMATS_UTC = {
minute: 'YYYY-MM-DD"T"HH24:MI:00"Z"',
hour: 'YYYY-MM-DD"T"HH24:00:00"Z"',
day: 'YYYY-MM-DD"T"HH24:00:00"Z"',
month: 'YYYY-MM-01"T"HH24:00:00"Z"',
year: 'YYYY-01-01"T"HH24:00:00"Z"',
};
function getAddIntervalQuery(field: string, interval: string): string {
return `${field} + interval '${interval}'`;
}
@ -40,11 +48,11 @@ function getCastColumnQuery(field: string, type: string): string {
}
function getDateSQL(field: string, unit: string, timezone?: string): string {
if (timezone) {
if (timezone && timezone !== 'utc') {
return `to_char(date_trunc('${unit}', ${field} at time zone '${timezone}'), '${DATE_FORMATS[unit]}')`;
}
return `to_char(date_trunc('${unit}', ${field}), '${DATE_FORMATS[unit]}')`;
return `to_char(date_trunc('${unit}', ${field}), '${DATE_FORMATS_UTC[unit]}')`;
}
function getDateWeeklySQL(field: string, timezone?: string) {

View file

@ -116,3 +116,23 @@ export interface PageResult<T> {
sortDescending?: boolean;
search?: string;
}
export interface RealtimeData {
countries: Record<string, number>;
events: any[];
pageviews: any[];
referrers: Record<string, number>;
timestamp: number;
series: {
views: any[];
visitors: any[];
};
totals: {
views: number;
visitors: number;
events: number;
countries: number;
};
urls: Record<string, number>;
visitors: any[];
}