From c97d4037a6189a69ccebbc84cb5fc5c023c3f981 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 29 Aug 2023 13:02:51 -0700 Subject: [PATCH 1/9] update node.js version in readme for prisma 5.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19935ed5..02a44e1e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A detailed getting started guide can be found at [https://umami.is/docs/](https: ### Requirements -- A server with Node.js version 12 or newer +- A server with Node.js version 16.13 or newer - A database. Umami supports [MySQL](https://www.mysql.com/) and [Postgresql](https://www.postgresql.org/) databases. ### Install Yarn From 69410f4a55ac813867811136a6e6d2b5a2dd20ab Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 29 Aug 2023 13:56:40 -0700 Subject: [PATCH 2/9] fix yup validation on password change --- src/pages/api/me/password.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/api/me/password.ts b/src/pages/api/me/password.ts index 6f49a182..a601f5d6 100644 --- a/src/pages/api/me/password.ts +++ b/src/pages/api/me/password.ts @@ -23,7 +23,6 @@ export interface UserPasswordRequestBody { const schema = { POST: yup.object().shape({ - id: yup.string().uuid().required(), currentPassword: yup.string().required(), newPassword: yup.string().min(8).required(), }), From e1909364d3fbb21b5bb1d254dfd63dae1d75726b Mon Sep 17 00:00:00 2001 From: essesoul <58624474+essesoul@users.noreply.github.com> Date: Wed, 30 Aug 2023 23:36:39 +0800 Subject: [PATCH 3/9] Update zh-CN.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Chinese translation✍️ --- src/lang/zh-CN.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json index 91043dac..bab833c0 100644 --- a/src/lang/zh-CN.json +++ b/src/lang/zh-CN.json @@ -16,19 +16,19 @@ "label.before": "之前", "label.bounce-rate": "跳出率", "label.breakdown": "故障", - "label.browser": "Browser", + "label.browser": "浏览器", "label.browsers": "浏览器", "label.cancel": "取消", "label.change-password": "更新密码", "label.cities": "市/县", - "label.city": "City", + "label.city": "市/县", "label.clear-all": "清除全部", "label.confirm": "确认", "label.confirm-password": "确认密码", "label.contains": "包含", "label.continue": "继续", "label.countries": "国家/地区", - "label.country": "Country", + "label.country": "国家/地区", "label.create-report": "创建报告", "label.create-team": "创建团队", "label.create-user": "创建用户", @@ -37,9 +37,9 @@ "label.custom-range": "自定义时间段", "label.dashboard": "仪表板", "label.data": "统计数据", - "label.date": "Date", + "label.date": "日期", "label.date-range": "时间段", - "label.day": "Day", + "label.day": "日", "label.default-date-range": "默认时间段", "label.delete": "删除", "label.delete-team": "删除团队", @@ -48,7 +48,7 @@ "label.description": "描述", "label.desktop": "台式机", "label.details": "详细信息", - "label.device": "Device", + "label.device": "设备", "label.devices": "设备", "label.dismiss": "关闭", "label.does-not-contain": "不包含", @@ -72,8 +72,8 @@ "label.insights": "见解", "label.is": "等于", "label.is-not": "不等于", - "label.is-not-set": "Is not set", - "label.is-set": "Is set", + "label.is-not-set": "未设置", + "label.is-set": "已设置", "label.join": "加入", "label.join-team": "加入团队", "label.language": "语言", @@ -92,16 +92,16 @@ "label.min": "最小", "label.mobile": "手机", "label.more": "更多", - "label.my-websites": "My websites", + "label.my-websites": "我的网站", "label.name": "名字", "label.new-password": "新密码", "label.none": "无", "label.os": "OS", "label.overview": "概览", "label.owner": "所有者", - "label.page-of": "Page {current} of {total}", + "label.page-of": "总{total}中的第{current}页", "label.page-views": "页面浏览量", - "label.pageTitle": "Page title", + "label.pageTitle": "标题", "label.pages": "网页", "label.password": "密码", "label.powered-by": "由 {name} 提供支持", @@ -110,18 +110,18 @@ "label.query": "查询", "label.query-parameters": "查询参数", "label.realtime": "实时", - "label.referrer": "Referrer", + "label.referrer": "来源", "label.referrers": "来源域名", "label.refresh": "刷新", "label.regenerate": "重新生成", - "label.region": "Region", + "label.region": "州/省", "label.regions": "州/省", "label.remove": "移除", "label.reports": "报告", "label.required": "必填", "label.reset": "重置", "label.reset-website": "重置统计数据", - "label.retention": "Retention", + "label.retention": "保留", "label.role": "角色", "label.run-query": "查询", "label.save": "保存", @@ -138,9 +138,9 @@ "label.team-guest": "团队访客", "label.team-id": "团队 ID", "label.team-member": "团队成员", - "label.team-name": "Team name", + "label.team-name": "团队名称", "label.team-owner": "团队所有者", - "label.team-websites": "Team websites", + "label.team-websites": "团队网站", "label.teams": "团队", "label.theme": "主题", "label.this-month": "本月", @@ -155,19 +155,19 @@ "label.tracking-code": "跟踪代码", "label.true": "是", "label.type": "类型", - "label.unique": "Unique", + "label.unique": "独立", "label.unique-visitors": "独立访客", "label.unknown": "未知", "label.untitled": "未命名", - "label.url": "URL", - "label.urls": "URLs", + "label.url": "网址", + "label.urls": "网址", "label.user": "用户", "label.username": "用户名", "label.users": "用户", "label.value": "值", "label.view": "查看", "label.view-details": "查看更多", - "label.view-only": "View only", + "label.view-only": "仅浏览量", "label.views": "浏览量", "label.visitors": "访客", "label.website": "网站", @@ -183,7 +183,7 @@ "message.delete-website": "确定删除该网站, 请在下面的输入框中输入 {confirmation} 进行二次确认。", "message.delete-website-warning": "所有相关数据将会被删除。", "message.error": "出现错误。", - "message.event-log": "{event} on {url}", + "message.event-log": "{url}上的{event}", "message.go-to-settings": "去设置", "message.incorrect-username-password": "用户名或密码不正确。", "message.invalid-domain": "无效域名", From 50b4ac9889c2bc74a4fdd3aabbb719b774acc56e Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 30 Aug 2023 09:52:24 -0700 Subject: [PATCH 4/9] add optional check for subdivision --- src/lib/detect.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/detect.ts b/src/lib/detect.ts index 86f812bd..d6043506 100644 --- a/src/lib/detect.ts +++ b/src/lib/detect.ts @@ -71,7 +71,7 @@ export async function getLocation(ip, req) { return { country, - subdivision1: subdivision1.includes('-') ? subdivision1 : `${country}-${subdivision1}`, + subdivision1: subdivision1?.includes('-') ? subdivision1 : `${country}-${subdivision1}`, city, }; } @@ -84,7 +84,7 @@ export async function getLocation(ip, req) { return { country, - subdivision1: subdivision1.includes('-') ? subdivision1 : `${country}-${subdivision1}`, + subdivision1: subdivision1?.includes('-') ? subdivision1 : `${country}-${subdivision1}`, city, }; } From 33ffa0b3d13a5fe96f25337c9115b120cc4825a8 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 30 Aug 2023 15:23:08 -0700 Subject: [PATCH 5/9] Added paging to dashboard. --- src/components/pages/dashboard/Dashboard.js | 53 +++++++++---------- .../pages/dashboard/DashboardEdit.js | 28 ++++++---- src/pages/api/websites/index.ts | 9 +++- src/queries/admin/report.ts | 1 + src/queries/admin/team.ts | 1 + src/queries/admin/website.ts | 3 +- 6 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/components/pages/dashboard/Dashboard.js b/src/components/pages/dashboard/Dashboard.js index 8248cc81..2294b8be 100644 --- a/src/components/pages/dashboard/Dashboard.js +++ b/src/components/pages/dashboard/Dashboard.js @@ -1,8 +1,8 @@ -import { useState } from 'react'; -import { Button, Icon, Icons, Text, Flexbox } from 'react-basics'; +import { Button, Icon, Icons, Text } from 'react-basics'; import Link from 'next/link'; import Page from 'components/layout/Page'; import PageHeader from 'components/layout/PageHeader'; +import Pager from 'components/common/Pager'; import WebsiteChartList from 'components/pages/websites/WebsiteChartList'; import DashboardSettingsButton from 'components/pages/dashboard/DashboardSettingsButton'; import DashboardEdit from 'components/pages/dashboard/DashboardEdit'; @@ -11,23 +11,24 @@ import useApi from 'components/hooks/useApi'; import useDashboard from 'store/dashboard'; import useMessages from 'components/hooks/useMessages'; import useLocale from 'components/hooks/useLocale'; +import useApiFilter from 'components/hooks/useApiFilter'; export function Dashboard() { const { formatMessage, labels, messages } = useMessages(); - const dashboard = useDashboard(); - const { showCharts, limit, editing } = dashboard; - const [max, setMax] = useState(limit); - const { get, useQuery } = useApi(); - const { data, isLoading, error } = useQuery(['websites'], () => - get('/websites', { includeTeams: 1 }), - ); - const hasData = data && data?.data.length !== 0; - + const { showCharts, editing } = useDashboard(); const { dir } = useLocale(); - - function handleMore() { - setMax(max + limit); - } + const { get, useQuery } = useApi(); + const { page, handlePageChange } = useApiFilter(); + const pageSize = 10; + const { + data: result, + isLoading, + error, + } = useQuery(['websites', page, pageSize], () => + get('/websites', { includeTeams: 1, page, pageSize }), + ); + const { data, count } = result || {}; + const hasData = data && data?.length !== 0; return ( @@ -48,19 +49,17 @@ export function Dashboard() { )} {hasData && ( <> - {editing && } + {editing && } {!editing && ( - - )} - {max < data.length && ( - - - + <> + + + )} )} diff --git a/src/components/pages/dashboard/DashboardEdit.js b/src/components/pages/dashboard/DashboardEdit.js index 4eb259d6..f628599f 100644 --- a/src/components/pages/dashboard/DashboardEdit.js +++ b/src/components/pages/dashboard/DashboardEdit.js @@ -5,23 +5,33 @@ import { Button } from 'react-basics'; import { firstBy } from 'thenby'; import useDashboard, { saveDashboard } from 'store/dashboard'; import useMessages from 'components/hooks/useMessages'; +import useApi from 'components/hooks/useApi'; import styles from './DashboardEdit.module.css'; +import Page from 'components/layout/Page'; const dragId = 'dashboard-website-ordering'; -export function DashboardEdit({ websites }) { +export function DashboardEdit() { const settings = useDashboard(); const { websiteOrder } = settings; const { formatMessage, labels } = useMessages(); const [order, setOrder] = useState(websiteOrder || []); + const { get, useQuery } = useApi(); + const { + data: result, + isLoading, + error, + } = useQuery(['websites'], () => get('/websites', { includeTeams: 1 })); + const { data: websites } = result || {}; - const ordered = useMemo( - () => - websites + const ordered = useMemo(() => { + if (websites) { + return websites .map(website => ({ ...website, order: order.indexOf(website.id) })) - .sort(firstBy('order')), - [websites, order], - ); + .sort(firstBy('order')); + } + return []; + }, [websites, order]); function handleWebsiteDrag({ destination, source }) { if (!destination || destination.index === source.index) return; @@ -49,7 +59,7 @@ export function DashboardEdit({ websites }) { } return ( - <> +
- +
); } diff --git a/src/pages/api/websites/index.ts b/src/pages/api/websites/index.ts index d724f12f..d6009caf 100644 --- a/src/pages/api/websites/index.ts +++ b/src/pages/api/websites/index.ts @@ -42,8 +42,13 @@ export default async ( } = req.auth; if (req.method === 'GET') { - req.query.id = userId; - req.query.pageSize = 100; + if (!req.query.id) { + req.query.id = userId; + } + + if (!req.query.pageSize) { + req.query.pageSize = 100; + } return userWebsites(req as any, res); } diff --git a/src/queries/admin/report.ts b/src/queries/admin/report.ts index a053ba92..59eb7035 100644 --- a/src/queries/admin/report.ts +++ b/src/queries/admin/report.ts @@ -142,6 +142,7 @@ export async function getReports( ...pageFilters, ...(options?.include && { include: options.include }), }); + const count = await prisma.client.report.count({ where, }); diff --git a/src/queries/admin/team.ts b/src/queries/admin/team.ts index 284b218e..cf731ad4 100644 --- a/src/queries/admin/team.ts +++ b/src/queries/admin/team.ts @@ -135,6 +135,7 @@ export async function getTeams( ...pageFilters, ...(options?.include && { include: options?.include }), }); + const count = await prisma.client.team.count({ where }); return { data: teams, count, ...getParameters }; diff --git a/src/queries/admin/website.ts b/src/queries/admin/website.ts index 3d0c773b..cf4570cf 100644 --- a/src/queries/admin/website.ts +++ b/src/queries/admin/website.ts @@ -107,7 +107,8 @@ export async function getWebsites( ...pageFilters, ...(options?.include && { include: options.include }), }); - const count = await prisma.client.website.count({ where }); + + const count = await prisma.client.website.count({ where: { ...where, deletedAt: null } }); return { data: websites, count, ...getParameters }; } From 61df80112a1396e8826a55b13319e33a925cc277 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 30 Aug 2023 15:43:39 -0700 Subject: [PATCH 6/9] Updated subdivision check. --- src/lib/detect.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib/detect.ts b/src/lib/detect.ts index d6043506..b16bab7a 100644 --- a/src/lib/detect.ts +++ b/src/lib/detect.ts @@ -57,6 +57,14 @@ export function getDevice(screen, os) { } } +function getRegionCode(country, region) { + if (!country || !region) { + return undefined; + } + + return region.includes('-') ? region : `${country}-${region}`; +} + export async function getLocation(ip, req) { // Ignore local ips if (await isLocalhost(ip)) { @@ -71,7 +79,7 @@ export async function getLocation(ip, req) { return { country, - subdivision1: subdivision1?.includes('-') ? subdivision1 : `${country}-${subdivision1}`, + subdivision1: getRegionCode(country, subdivision1), city, }; } @@ -84,7 +92,7 @@ export async function getLocation(ip, req) { return { country, - subdivision1: subdivision1?.includes('-') ? subdivision1 : `${country}-${subdivision1}`, + subdivision1: getRegionCode(country, subdivision1), city, }; } From 62434a3e0ceb5eb8fd9f32701bb28c0a16a38fb6 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 30 Aug 2023 15:49:26 -0700 Subject: [PATCH 7/9] Bump version v2.6.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4fda0a22..9745b780 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "2.6.0", + "version": "2.6.1", "description": "A simple, fast, privacy-focused alternative to Google Analytics.", "author": "Mike Cao ", "license": "MIT", From fac306328dd75f6a5676addae2c968a0be5a40ee Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 30 Aug 2023 16:13:05 -0700 Subject: [PATCH 8/9] Updated language. --- public/intl/messages/zh-CN.json | 60 ++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/public/intl/messages/zh-CN.json b/public/intl/messages/zh-CN.json index 71234564..ec5c441b 100644 --- a/public/intl/messages/zh-CN.json +++ b/public/intl/messages/zh-CN.json @@ -104,7 +104,7 @@ "label.browser": [ { "type": 0, - "value": "Browser" + "value": "浏览器" } ], "label.browsers": [ @@ -134,7 +134,7 @@ "label.city": [ { "type": 0, - "value": "City" + "value": "市/县" } ], "label.clear-all": [ @@ -176,7 +176,7 @@ "label.country": [ { "type": 0, - "value": "Country" + "value": "国家/地区" } ], "label.create-report": [ @@ -230,7 +230,7 @@ "label.date": [ { "type": 0, - "value": "Date" + "value": "日期" } ], "label.date-range": [ @@ -242,7 +242,7 @@ "label.day": [ { "type": 0, - "value": "Day" + "value": "日" } ], "label.default-date-range": [ @@ -296,7 +296,7 @@ "label.device": [ { "type": 0, - "value": "Device" + "value": "设备" } ], "label.devices": [ @@ -440,13 +440,13 @@ "label.is-not-set": [ { "type": 0, - "value": "Is not set" + "value": "未设置" } ], "label.is-set": [ { "type": 0, - "value": "Is set" + "value": "已设置" } ], "label.join": [ @@ -576,7 +576,7 @@ "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "我的网站" } ], "label.name": [ @@ -618,7 +618,15 @@ "label.page-of": [ { "type": 0, - "value": "Page " + "value": "总" + }, + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": "中的第" }, { "type": 1, @@ -626,11 +634,7 @@ }, { "type": 0, - "value": " of " - }, - { - "type": 1, - "value": "total" + "value": "页" } ], "label.page-views": [ @@ -642,7 +646,7 @@ "label.pageTitle": [ { "type": 0, - "value": "Page title" + "value": "标题" } ], "label.pages": [ @@ -704,7 +708,7 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "来源" } ], "label.referrers": [ @@ -728,7 +732,7 @@ "label.region": [ { "type": 0, - "value": "Region" + "value": "州/省" } ], "label.regions": [ @@ -770,7 +774,7 @@ "label.retention": [ { "type": 0, - "value": "Retention" + "value": "保留" } ], "label.role": [ @@ -872,7 +876,7 @@ "label.team-name": [ { "type": 0, - "value": "Team name" + "value": "团队名称" } ], "label.team-owner": [ @@ -884,7 +888,7 @@ "label.team-websites": [ { "type": 0, - "value": "Team websites" + "value": "团队网站" } ], "label.teams": [ @@ -974,7 +978,7 @@ "label.unique": [ { "type": 0, - "value": "Unique" + "value": "独立" } ], "label.unique-visitors": [ @@ -998,13 +1002,13 @@ "label.url": [ { "type": 0, - "value": "URL" + "value": "网址" } ], "label.urls": [ { "type": 0, - "value": "URLs" + "value": "网址" } ], "label.user": [ @@ -1046,7 +1050,7 @@ "label.view-only": [ { "type": 0, - "value": "View only" + "value": "仅浏览量" } ], "label.views": [ @@ -1190,15 +1194,15 @@ "message.event-log": [ { "type": 1, - "value": "event" + "value": "url" }, { "type": 0, - "value": " on " + "value": "上的" }, { "type": 1, - "value": "url" + "value": "event" } ], "message.go-to-settings": [ From a7ea2027853be35254039df07422d9e89fa0607c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 30 Aug 2023 16:33:06 -0700 Subject: [PATCH 9/9] Check for payload. --- src/pages/api/send.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/api/send.ts b/src/pages/api/send.ts index a379f261..56a748e1 100644 --- a/src/pages/api/send.ts +++ b/src/pages/api/send.ts @@ -79,6 +79,10 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => { const { type, payload } = getJsonBody(req); + if (!type || !payload) { + return badRequest(res); + } + req.yup = schema; await useValidate(req, res);