From 6833a5bdb0b8748d0b752592626ea79b878e615b Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 4 Sep 2020 11:06:32 -0700 Subject: [PATCH 1/6] Detect when postgres is used instead of postgresql. --- lib/queries.js | 11 ++++++++--- package.json | 2 +- scripts/copy-db-schema.js | 17 ++++++++++++++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/queries.js b/lib/queries.js index 5b30c0a9..684b30d4 100644 --- a/lib/queries.js +++ b/lib/queries.js @@ -4,10 +4,15 @@ import { subMinutes } from 'date-fns'; import { MYSQL, POSTGRESQL, MYSQL_DATE_FORMATS, POSTGRESQL_DATE_FORMATS } from 'lib/constants'; export function getDatabase() { - return ( + const type = process.env.DATABASE_TYPE || - (process.env.DATABASE_URL && process.env.DATABASE_URL.split(':')[0]) - ); + (process.env.DATABASE_URL && process.env.DATABASE_URL.split(':')[0]); + + if (type === 'postgres') { + return 'postgresql'; + } + + return type; } export function getDateQuery(db, field, unit, timezone) { diff --git a/package.json b/package.json index e43acc20..2d58e380 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "0.22.0", + "version": "0.23.0", "description": "A simple, fast, website analytics alternative to Google Analytics. ", "author": "Mike Cao ", "license": "MIT", diff --git a/scripts/copy-db-schema.js b/scripts/copy-db-schema.js index 2d480545..7773e696 100644 --- a/scripts/copy-db-schema.js +++ b/scripts/copy-db-schema.js @@ -2,14 +2,25 @@ require('dotenv').config(); const fs = require('fs'); const path = require('path'); -const databaseType = - process.env.DATABASE_TYPE || (process.env.DATABASE_URL && process.env.DATABASE_URL.split(':')[0]); +function getDatabase() { + const type = + process.env.DATABASE_TYPE || + (process.env.DATABASE_URL && process.env.DATABASE_URL.split(':')[0]); + + if (type === 'postgres') { + return 'postgresql'; + } + + return type; +} + +const databaseType = getDatabase(); if (!databaseType || !['mysql', 'postgresql'].includes(databaseType)) { throw new Error('Missing or invalid database'); } -console.log(`Database schema detected: ${databaseType}`); +console.log(`Database type detected: ${databaseType}`); const src = path.resolve(__dirname, `../prisma/schema.${databaseType}.prisma`); const dest = path.resolve(__dirname, '../prisma/schema.prisma'); From f0ac9b6522f382484ec23fb38aea04a977ed8f76 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 5 Sep 2020 17:27:01 -0700 Subject: [PATCH 2/6] Convert text for internationalization. --- components/WebsiteDetails.js | 5 +- components/WebsiteList.js | 17 +- components/common/ButtonGroup.js | 25 +- components/common/CopyButton.js | 7 +- components/common/DateFilter.js | 40 ++- components/forms/AccountEditForm.js | 25 +- components/forms/ChangePasswordForm.js | 35 +- components/forms/DeleteForm.js | 34 +- components/forms/LoginForm.js | 11 +- components/forms/ShareUrlForm.js | 13 +- components/forms/TrackingCodeForm.js | 12 +- components/forms/WebsiteEditForm.js | 35 +- components/layout/Footer.js | 5 +- components/layout/Header.js | 9 +- components/metrics/ActiveUsers.js | 9 +- components/metrics/BrowsersTable.js | 5 +- components/metrics/CountriesTable.js | 5 +- components/metrics/DevicesTable.js | 5 +- components/metrics/EventsTable.js | 5 +- components/metrics/MetricsBar.js | 26 +- components/metrics/MetricsTable.js | 5 +- components/metrics/OSTable.js | 5 +- components/metrics/PagesTable.js | 37 ++- components/metrics/PageviewsChart.js | 13 +- components/metrics/QuickButtons.js | 18 +- components/metrics/ReferrersTable.js | 39 ++- components/metrics/WebsiteHeader.js | 5 +- components/settings/AccountSettings.js | 31 +- components/settings/ProfileSettings.js | 19 +- components/settings/Settings.js | 26 +- components/settings/WebsiteSettings.js | 74 +++-- lang/en.json | 227 +++++++++++++ lib/constants.js | 4 + package.json | 6 +- pages/_app.js | 9 +- yarn.lock | 441 ++++++++++++++++++++++++- 36 files changed, 1091 insertions(+), 196 deletions(-) create mode 100644 lang/en.json diff --git a/components/WebsiteDetails.js b/components/WebsiteDetails.js index bd8025ee..d58923c2 100644 --- a/components/WebsiteDetails.js +++ b/components/WebsiteDetails.js @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import WebsiteChart from 'components/metrics/WebsiteChart'; import WorldMap from 'components/common/WorldMap'; @@ -32,7 +33,9 @@ export default function WebsiteDetails({ websiteId }) { size="xsmall" onClick={() => setExpand(null)} > -
Back
+
+ +
); diff --git a/components/WebsiteList.js b/components/WebsiteList.js index 59573b96..f2a8c3db 100644 --- a/components/WebsiteList.js +++ b/components/WebsiteList.js @@ -1,4 +1,5 @@ import React from 'react'; +import { FormattedMessage } from 'react-intl'; import { useRouter } from 'next/router'; import WebsiteChart from 'components/metrics/WebsiteChart'; import Page from 'components/layout/Page'; @@ -24,9 +25,21 @@ export default function WebsiteList() { ))} {data.length === 0 && ( - + + } + > )} diff --git a/components/common/ButtonGroup.js b/components/common/ButtonGroup.js index 26707fb9..c91bb743 100644 --- a/components/common/ButtonGroup.js +++ b/components/common/ButtonGroup.js @@ -13,17 +13,20 @@ export default function ButtonGroup({ }) { return (
- {items.map(item => ( - - ))} + {items.map(item => { + const { label, value } = item; + return ( + + ); + })}
); } diff --git a/components/common/CopyButton.js b/components/common/CopyButton.js index 8d6b5db6..399da90d 100644 --- a/components/common/CopyButton.js +++ b/components/common/CopyButton.js @@ -1,7 +1,10 @@ import React, { useState } from 'react'; import Button from './Button'; +import { FormattedMessage } from 'react-intl'; -const defaultText = 'Copy to clipboard'; +const defaultText = ( + +); export default function CopyButton({ element, ...props }) { const [text, setText] = useState(defaultText); @@ -10,7 +13,7 @@ export default function CopyButton({ element, ...props }) { if (element?.current) { element.current.select(); document.execCommand('copy'); - setText('Copied!'); + setText(); window.getSelection().removeAllRanges(); } } diff --git a/components/common/DateFilter.js b/components/common/DateFilter.js index 7b7838dd..aaba8725 100644 --- a/components/common/DateFilter.js +++ b/components/common/DateFilter.js @@ -1,16 +1,40 @@ import React from 'react'; import { getDateRange } from 'lib/date'; import DropDown from './DropDown'; +import { FormattedMessage } from 'react-intl'; const filterOptions = [ - { label: 'Last 24 hours', value: '24hour' }, - { label: 'Last 7 days', value: '7day' }, - { label: 'Last 30 days', value: '30day' }, - { label: 'Last 90 days', value: '90day' }, - { label: 'Today', value: '1day' }, - { label: 'This week', value: '1week' }, - { label: 'This month', value: '1month' }, - { label: 'This year', value: '1year' }, + { + label: ( + + ), + value: '24hour', + }, + { + label: ( + + ), + value: '7day', + }, + { + label: ( + + ), + value: '30day', + }, + { + label: ( + + ), + value: '90day', + }, + { label: , value: '1day' }, + { label: , value: '1week' }, + { + label: , + value: '1month', + }, + { label: , value: '1year' }, ]; export default function DateFilter({ value, onChange, className }) { diff --git a/components/forms/AccountEditForm.js b/components/forms/AccountEditForm.js index 96eea3df..16c6fd3f 100644 --- a/components/forms/AccountEditForm.js +++ b/components/forms/AccountEditForm.js @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { FormattedMessage } from 'react-intl'; import { Formik, Form, Field } from 'formik'; import { post } from 'lib/web'; import Button from 'components/common/Button'; @@ -18,10 +19,10 @@ const validate = ({ user_id, username, password }) => { const errors = {}; if (!username) { - errors.username = 'Required'; + errors.username = ; } if (!user_id && !password) { - errors.password = 'Required'; + errors.password = ; } return errors; @@ -36,7 +37,11 @@ export default function AccountEditForm({ values, onSave, onClose }) { if (typeof response !== 'string') { onSave(); } else { - setMessage(response || 'Something went wrong'); + setMessage( + response || ( + + ), + ); } }; @@ -50,20 +55,26 @@ export default function AccountEditForm({ values, onSave, onClose }) { {() => (
- + - + + - {message}
diff --git a/components/forms/ChangePasswordForm.js b/components/forms/ChangePasswordForm.js index f3e5927a..e2f225b7 100644 --- a/components/forms/ChangePasswordForm.js +++ b/components/forms/ChangePasswordForm.js @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { FormattedMessage } from 'react-intl'; import { Formik, Form, Field } from 'formik'; import { post } from 'lib/web'; import Button from 'components/common/Button'; @@ -19,15 +20,17 @@ const validate = ({ current_password, new_password, confirm_password }) => { const errors = {}; if (!current_password) { - errors.current_password = 'Required'; + errors.current_password = ; } if (!new_password) { - errors.new_password = 'Required'; + errors.new_password = ; } if (!confirm_password) { - errors.confirm_password = 'Required'; + errors.confirm_password = ; } else if (new_password !== confirm_password) { - errors.confirm_password = `Passwords don't match`; + errors.confirm_password = ( + + ); } return errors; @@ -42,7 +45,11 @@ export default function ChangePasswordForm({ values, onSave, onClose }) { if (typeof response !== 'string') { onSave(); } else { - setMessage(response || 'Something went wrong'); + setMessage( + response || ( + + ), + ); } }; @@ -56,25 +63,33 @@ export default function ChangePasswordForm({ values, onSave, onClose }) { {() => (
- + - + - + + - {message}
diff --git a/components/forms/DeleteForm.js b/components/forms/DeleteForm.js index 650e802f..1ba81626 100644 --- a/components/forms/DeleteForm.js +++ b/components/forms/DeleteForm.js @@ -8,12 +8,17 @@ import FormLayout, { FormMessage, FormRow, } from 'components/layout/FormLayout'; +import { FormattedMessage } from 'react-intl'; const validate = ({ confirmation }) => { const errors = {}; if (confirmation !== 'DELETE') { - errors.confirmation = !confirmation ? 'Required' : 'Invalid'; + errors.confirmation = !confirmation ? ( + + ) : ( + + ); } return errors; @@ -28,7 +33,7 @@ export default function DeleteForm({ values, onSave, onClose }) { if (typeof response !== 'string') { onSave(); } else { - setMessage('Something went wrong'); + setMessage(); } }; @@ -42,11 +47,24 @@ export default function DeleteForm({ values, onSave, onClose }) { {() => (
- Are your sure you want to delete {values.name}? + {values.name} }} + /> +
+
+
-
All associated data will be deleted as well.

- Type DELETE in the box below to confirm. + DELETE }} + />

@@ -54,9 +72,11 @@ export default function DeleteForm({ values, onSave, onClose }) { + - {message}
diff --git a/components/forms/LoginForm.js b/components/forms/LoginForm.js index 78e0480e..0cac8ff8 100644 --- a/components/forms/LoginForm.js +++ b/components/forms/LoginForm.js @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { FormattedMessage } from 'react-intl'; import { Formik, Form, Field } from 'formik'; import Router from 'next/router'; import { post } from 'lib/web'; @@ -54,18 +55,22 @@ export default function LoginForm() { } size="xlarge" className={styles.icon} />

umami

- + - + {message} diff --git a/components/forms/ShareUrlForm.js b/components/forms/ShareUrlForm.js index 16a9ca4b..ea162f67 100644 --- a/components/forms/ShareUrlForm.js +++ b/components/forms/ShareUrlForm.js @@ -1,7 +1,8 @@ import React, { useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; import Button from 'components/common/Button'; import FormLayout, { FormButtons, FormRow } from 'components/layout/FormLayout'; -import CopyButton from '../common/CopyButton'; +import CopyButton from 'components/common/CopyButton'; export default function TrackingCodeForm({ values, onClose }) { const ref = useRef(); @@ -10,7 +11,11 @@ export default function TrackingCodeForm({ values, onClose }) { return (

- This is the publicly shared URL for {values.name}. + {values.name} }} + />