diff --git a/.eslintrc.json b/.eslintrc.json
index 215d6491fe..6ea8791edd 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -4,7 +4,12 @@
"es2020": true,
"node": true
},
- "extends": ["eslint:recommended", "plugin:prettier/recommended", "next"],
+ "extends": [
+ "eslint:recommended",
+ "plugin:prettier/recommended",
+ "plugin:import/recommended",
+ "next"
+ ],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
@@ -12,7 +17,27 @@
"ecmaVersion": 11,
"sourceType": "module"
},
+ "settings": {
+ "import/resolver": {
+ "alias": {
+ "map": [
+ ["assets", "./assets"],
+ ["components", "./components"],
+ ["db", "./db"],
+ ["hooks", "./hooks"],
+ ["lang", "./lang"],
+ ["lib", "./lib"],
+ ["public", "./public"],
+ ["queries", "./queries"],
+ ["store", "./store"],
+ ["styles", "./styles"]
+ ],
+ "extensions": [".ts", ".js", ".jsx", ".json"]
+ }
+ }
+ },
"rules": {
+ "no-console": "error",
"react/display-name": "off",
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
diff --git a/components/common/FilterLink.js b/components/common/FilterLink.js
index 459a8ae1d6..f16258f1a6 100644
--- a/components/common/FilterLink.js
+++ b/components/common/FilterLink.js
@@ -1,10 +1,10 @@
import React from 'react';
-import Link from 'next/link';
import classNames from 'classnames';
+import Link from 'next/link';
+import { safeDecodeURI } from 'next-basics';
import usePageQuery from 'hooks/usePageQuery';
-import { safeDecodeURI } from 'lib/url';
-import Icon from './Icon';
import External from 'assets/arrow-up-right-from-square.svg';
+import Icon from './Icon';
import styles from './FilterLink.module.css';
export default function FilterLink({ id, value, label, externalUrl }) {
@@ -25,7 +25,7 @@ export default function FilterLink({ id, value, label, externalUrl }) {
{externalUrl && (
-
+
} className={styles.icon} />
)}
diff --git a/components/common/UpdateNotice.js b/components/common/UpdateNotice.js
index 8bba1694e4..7419abe85c 100644
--- a/components/common/UpdateNotice.js
+++ b/components/common/UpdateNotice.js
@@ -1,8 +1,8 @@
import { useState, useEffect, useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
+import { setItem } from 'next-basics';
import ButtonLayout from 'components/layout/ButtonLayout';
import useStore, { checkVersion } from 'store/version';
-import { setItem } from 'lib/web';
import { REPO_URL, VERSION_CHECK } from 'lib/constants';
import Button from './Button';
import styles from './UpdateNotice.module.css';
diff --git a/components/forms/LoginForm.js b/components/forms/LoginForm.js
index 3c131558b3..8c8aa09e47 100644
--- a/components/forms/LoginForm.js
+++ b/components/forms/LoginForm.js
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { Formik, Form, Field } from 'formik';
+import { setItem } from 'next-basics';
import { useRouter } from 'next/router';
import Button from 'components/common/Button';
import FormLayout, {
@@ -11,7 +12,6 @@ import FormLayout, {
} from 'components/layout/FormLayout';
import Icon from 'components/common/Icon';
import useApi from 'hooks/useApi';
-import { setItem } from 'lib/web';
import { AUTH_TOKEN } from 'lib/constants';
import { setUser } from 'store/app';
import Logo from 'assets/logo.svg';
diff --git a/components/layout/Header.js b/components/layout/Header.js
index c6942ef5cd..fe2043e133 100644
--- a/components/layout/Header.js
+++ b/components/layout/Header.js
@@ -9,7 +9,7 @@ import HamburgerButton from 'components/common/HamburgerButton';
import UpdateNotice from 'components/common/UpdateNotice';
import UserButton from 'components/settings/UserButton';
import { HOMEPAGE_URL } from 'lib/constants';
-import useConfig from '/hooks/useConfig';
+import useConfig from 'hooks/useConfig';
import useUser from 'hooks/useUser';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
diff --git a/components/metrics/FilterTags.js b/components/metrics/FilterTags.js
index 5b8ed63c59..be65540e18 100644
--- a/components/metrics/FilterTags.js
+++ b/components/metrics/FilterTags.js
@@ -1,8 +1,8 @@
import React from 'react';
import classNames from 'classnames';
+import { safeDecodeURI } from 'next-basics';
import Button from 'components/common/Button';
import Times from 'assets/times.svg';
-import { safeDecodeURI } from 'lib/url';
import styles from './FilterTags.module.css';
export default function FilterTags({ params, onClick }) {
diff --git a/components/metrics/QueryParametersTable.js b/components/metrics/QueryParametersTable.js
index 2d92ad925f..6743e6c6eb 100644
--- a/components/metrics/QueryParametersTable.js
+++ b/components/metrics/QueryParametersTable.js
@@ -1,10 +1,10 @@
import { useState } from 'react';
import { useIntl, defineMessages } from 'react-intl';
-import MetricsTable from './MetricsTable';
+import { safeDecodeURI } from 'next-basics';
import Tag from 'components/common/Tag';
+import FilterButtons from 'components/common/FilterButtons';
import { paramFilter } from 'lib/filters';
-import { safeDecodeURI } from 'lib/url';
-import FilterButtons from '../common/FilterButtons';
+import MetricsTable from './MetricsTable';
const FILTER_COMBINED = 0;
const FILTER_RAW = 1;
diff --git a/components/pages/DashboardEdit.js b/components/pages/DashboardEdit.js
index d06f32f95c..d231a8fb8f 100644
--- a/components/pages/DashboardEdit.js
+++ b/components/pages/DashboardEdit.js
@@ -23,8 +23,6 @@ export default function DashboardEdit({ websites }) {
const ordered = useMemo(() => sortArrayByMap(websites, order, 'website_id'), [websites, order]);
- console.log({ order, ordered });
-
function handleWebsiteDrag({ destination, source }) {
if (!destination || destination.index === source.index) return;
diff --git a/components/pages/TestConsole.js b/components/pages/TestConsole.js
index e4fa5a52f3..efeb026420 100644
--- a/components/pages/TestConsole.js
+++ b/components/pages/TestConsole.js
@@ -28,8 +28,6 @@ export default function TestConsole() {
const website = data.find(({ website_id }) => website_id === +websiteId);
const selectedValue = options.find(({ value }) => value === website?.website_id)?.value;
- console.log({ websiteId, data, options, website });
-
function handleSelect(value) {
router.push(`/console/${value}`);
}
diff --git a/components/pages/WebsiteList.js b/components/pages/WebsiteList.js
index 4de8d05db5..2c19c167a2 100644
--- a/components/pages/WebsiteList.js
+++ b/components/pages/WebsiteList.js
@@ -24,8 +24,6 @@ export default function WebsiteList({ websites, showCharts, limit }) {
const { websiteOrder } = useDashboard();
const { formatMessage } = useIntl();
- console.log({ websiteOrder });
-
const ordered = useMemo(
() => sortArrayByMap(websites, websiteOrder, 'website_id'),
[websites, websiteOrder],
diff --git a/components/settings/UserButton.js b/components/settings/UserButton.js
index cf11eee6f3..8e0ee35034 100644
--- a/components/settings/UserButton.js
+++ b/components/settings/UserButton.js
@@ -1,11 +1,11 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useRouter } from 'next/router';
+import { removeItem } from 'next-basics';
import MenuButton from 'components/common/MenuButton';
import Icon from 'components/common/Icon';
import User from 'assets/user.svg';
import styles from './UserButton.module.css';
-import { removeItem } from 'lib/web';
import { AUTH_TOKEN } from 'lib/constants';
import useUser from 'hooks/useUser';
diff --git a/hooks/useApi.js b/hooks/useApi.js
index 63e88a46e5..662a1da935 100644
--- a/hooks/useApi.js
+++ b/hooks/useApi.js
@@ -1,6 +1,6 @@
import { useCallback } from 'react';
import { useRouter } from 'next/router';
-import { get, post, put, del, getItem } from 'lib/web';
+import { get, post, put, del, getItem } from 'next-basics';
import { AUTH_TOKEN, SHARE_TOKEN_HEADER } from 'lib/constants';
import useStore from 'store/app';
diff --git a/hooks/useCountryNames.js b/hooks/useCountryNames.js
index 6dce6cb7aa..7c15779f25 100644
--- a/hooks/useCountryNames.js
+++ b/hooks/useCountryNames.js
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
-import { get } from 'lib/web';
+import { get } from 'next-basics';
import enUS from 'public/intl/country/en-US.json';
const countryNames = {
diff --git a/hooks/useDateRange.js b/hooks/useDateRange.js
index 81fc68463f..be42f7d314 100644
--- a/hooks/useDateRange.js
+++ b/hooks/useDateRange.js
@@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react';
import { parseISO } from 'date-fns';
import { getDateRange } from 'lib/date';
-import { getItem, setItem } from 'lib/web';
+import { getItem, setItem } from 'next-basics';
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
import useForceUpdate from './useForceUpdate';
import useLocale from './useLocale';
diff --git a/hooks/useFetch.js b/hooks/useFetch.js
index 5b824d1bf6..bf50b917f1 100644
--- a/hooks/useFetch.js
+++ b/hooks/useFetch.js
@@ -8,7 +8,7 @@ export default function useFetch(url, options = {}, update = []) {
const [loading, setLoading] = useState(false);
const [count, setCount] = useState(0);
const { get } = useApi();
- const { params = {}, headers = {}, disabled, delay = 0, interval, onDataLoad } = options;
+ const { params = {}, headers = {}, disabled = false, delay = 0, interval, onDataLoad } = options;
async function loadData(params) {
try {
@@ -29,7 +29,9 @@ export default function useFetch(url, options = {}, update = []) {
onDataLoad?.(data);
} catch (e) {
+ // eslint-disable-next-line no-console
console.error(e);
+
setError(e);
} finally {
setLoading(false);
@@ -44,7 +46,7 @@ export default function useFetch(url, options = {}, update = []) {
clearTimeout(id);
};
}
- }, [url, !!disabled, count, ...update]);
+ }, [url, disabled, count, ...update]);
useEffect(() => {
if (interval && !disabled) {
@@ -54,7 +56,7 @@ export default function useFetch(url, options = {}, update = []) {
clearInterval(id);
};
}
- }, [interval, !!disabled]);
+ }, [interval, disabled]);
return { ...response, error, loading };
}
diff --git a/hooks/useLanguageNames.js b/hooks/useLanguageNames.js
index 2fc313f2a3..86a358ace5 100644
--- a/hooks/useLanguageNames.js
+++ b/hooks/useLanguageNames.js
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
-import { get } from 'lib/web';
+import { get } from 'next-basics';
import enUS from 'public/intl/language/en-US.json';
const languageNames = {
diff --git a/hooks/useLocale.js b/hooks/useLocale.js
index 339ff4ba26..06c3bc6b14 100644
--- a/hooks/useLocale.js
+++ b/hooks/useLocale.js
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useRouter } from 'next/router';
-import { get, setItem } from 'lib/web';
+import { get, setItem } from 'next-basics';
import { LOCALE_CONFIG } from 'lib/constants';
import { getDateLocale, getTextDirection } from 'lib/lang';
import useStore, { setLocale } from 'store/app';
diff --git a/hooks/usePageQuery.js b/hooks/usePageQuery.js
index 7cce756daf..1884f12e81 100644
--- a/hooks/usePageQuery.js
+++ b/hooks/usePageQuery.js
@@ -1,6 +1,9 @@
import { useMemo } from 'react';
import { useRouter } from 'next/router';
-import { getQueryString } from 'lib/url';
+
+function getQueryString(params) {
+ return new URLSearchParams({ ...params }).toString();
+}
export default function usePageQuery() {
const router = useRouter();
diff --git a/hooks/useTheme.js b/hooks/useTheme.js
index 47ac31dd47..5c21bf1ceb 100644
--- a/hooks/useTheme.js
+++ b/hooks/useTheme.js
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import useStore, { setTheme } from 'store/app';
-import { getItem, setItem } from 'lib/web';
+import { getItem, setItem } from 'next-basics';
import { THEME_CONFIG } from 'lib/constants';
const selector = state => state.theme;
diff --git a/hooks/useTimezone.js b/hooks/useTimezone.js
index 5032a6deff..8eb5d5f82d 100644
--- a/hooks/useTimezone.js
+++ b/hooks/useTimezone.js
@@ -1,6 +1,6 @@
import { useState, useCallback } from 'react';
import { getTimezone } from 'lib/date';
-import { getItem, setItem } from 'lib/web';
+import { getItem, setItem } from 'next-basics';
import { TIMEZONE_CONFIG } from 'lib/constants';
export default function useTimezone() {
diff --git a/lib/auth.js b/lib/auth.js
index 7f5700316f..816a73ff05 100644
--- a/lib/auth.js
+++ b/lib/auth.js
@@ -1,12 +1,13 @@
-import { parseSecureToken, parseToken } from './crypto';
+import { parseSecureToken, parseToken } from 'next-basics';
import { SHARE_TOKEN_HEADER } from './constants';
import { getWebsiteById } from 'queries';
+import { secret } from './crypto';
export async function getAuthToken(req) {
try {
const token = req.headers.authorization;
- return parseSecureToken(token.split(' ')[1]);
+ return parseSecureToken(token.split(' ')[1], secret());
} catch {
return null;
}
@@ -14,7 +15,7 @@ export async function getAuthToken(req) {
export async function isValidToken(token, validation) {
try {
- const result = await parseToken(token);
+ const result = parseToken(token, secret());
if (typeof validation === 'object') {
return !Object.keys(validation).find(key => result[key] !== validation[key]);
diff --git a/lib/clickhouse.js b/lib/clickhouse.js
index 1548b77bae..5f4cd63b77 100644
--- a/lib/clickhouse.js
+++ b/lib/clickhouse.js
@@ -12,13 +12,9 @@ export const CLICKHOUSE_DATE_FORMATS = {
year: '%Y-01-01',
};
-const log = debug('clickhouse');
+const log = debug('umami:clickhouse');
function getClient() {
- if (!process.env.CLICKHOUSE_URL) {
- return null;
- }
-
const {
hostname,
port,
@@ -149,13 +145,13 @@ function parseFilters(table, column, filters = {}, params = [], sessionKey = 'se
};
}
-function replaceQuery(string, params = []) {
- let formattedString = string;
+function formatQuery(str, params = []) {
+ let formattedString = str;
- params.forEach((a, i) => {
- let replace = a;
+ params.forEach((param, i) => {
+ let replace = param;
- if (typeof a === 'string' || a instanceof String) {
+ if (typeof param === 'string' || param instanceof String) {
replace = `'${replace}'`;
}
@@ -165,11 +161,11 @@ function replaceQuery(string, params = []) {
return formattedString;
}
-async function rawQuery(query, params = [], debug = false) {
- let formattedQuery = replaceQuery(query, params);
+async function rawQuery(query, params = []) {
+ let formattedQuery = formatQuery(query, params);
- if (debug || process.env.LOG_QUERY) {
- console.log(formattedQuery);
+ if (process.env.LOG_QUERY) {
+ log(formattedQuery);
}
return clickhouse.query(formattedQuery).toPromise();
@@ -188,7 +184,7 @@ async function findFirst(data) {
}
// Initialization
-const clickhouse = global[CLICKHOUSE] || getClient();
+const clickhouse = process.env.CLICKHOUSE_URL && (global[CLICKHOUSE] || getClient());
export default {
client: clickhouse,
@@ -199,8 +195,7 @@ export default {
getBetweenDates,
getFilterQuery,
parseFilters,
- replaceQuery,
- rawQuery,
findUnique,
findFirst,
+ rawQuery,
};
diff --git a/lib/crypto.js b/lib/crypto.js
index e59b01934e..1e53aa9d74 100644
--- a/lib/crypto.js
+++ b/lib/crypto.js
@@ -1,24 +1,15 @@
-import crypto from 'crypto';
-import { v4, v5, validate } from 'uuid';
-import bcrypt from 'bcryptjs';
-import { JWT, JWE, JWK } from 'jose';
+import { v4, v5 } from 'uuid';
import { startOfMonth } from 'date-fns';
-
-const SALT_ROUNDS = 10;
-const KEY = JWK.asKey(Buffer.from(secret()));
-const ROTATING_SALT = hash(startOfMonth(new Date()).toUTCString());
-const CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
-
-export function hash(...args) {
- return crypto.createHash('sha512').update(args.join('')).digest('hex');
-}
+import { hash } from 'next-basics';
export function secret() {
return hash(process.env.HASH_SALT || process.env.DATABASE_URL);
}
export function salt() {
- return v5(hash(secret(), ROTATING_SALT), v5.DNS);
+ const ROTATING_SALT = hash(startOfMonth(new Date()).toUTCString());
+
+ return hash([secret(), ROTATING_SALT]);
}
export function uuid(...args) {
@@ -26,49 +17,3 @@ export function uuid(...args) {
return v5(args.join(''), salt());
}
-
-export function isValidUuid(s) {
- return validate(s);
-}
-
-export function getRandomChars(n) {
- let s = '';
- for (let i = 0; i < n; i++) {
- s += CHARS[Math.floor(Math.random() * CHARS.length)];
- }
- return s;
-}
-
-export function hashPassword(password) {
- return bcrypt.hashSync(password, SALT_ROUNDS);
-}
-
-export function checkPassword(password, hash) {
- return bcrypt.compareSync(password, hash);
-}
-
-export async function createToken(payload) {
- return JWT.sign(payload, KEY);
-}
-
-export async function parseToken(token) {
- try {
- return JWT.verify(token, KEY);
- } catch {
- return null;
- }
-}
-
-export async function createSecureToken(payload) {
- return JWE.encrypt(await createToken(payload), KEY);
-}
-
-export async function parseSecureToken(token) {
- try {
- const result = await JWE.decrypt(token, KEY);
-
- return parseToken(result.toString());
- } catch {
- return null;
- }
-}
diff --git a/lib/db.js b/lib/db.js
index 9db00c9244..58e769b7d2 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -4,6 +4,7 @@ export const MYSQL = 'mysql';
export const CLICKHOUSE = 'clickhouse';
export const KAFKA = 'kafka';
export const KAFKA_PRODUCER = 'kafka-producer';
+export const REDIS = 'redis';
// Fixes issue with converting bigint values
BigInt.prototype.toJSON = function () {
diff --git a/lib/filters.js b/lib/filters.js
index 27b88e20e2..087b92bf52 100644
--- a/lib/filters.js
+++ b/lib/filters.js
@@ -1,5 +1,3 @@
-import { removeWWW } from './url';
-
export const urlFilter = data => {
const isValidUrl = url => {
return url !== '' && url !== null && !url.startsWith('#');
@@ -49,7 +47,7 @@ export const refFilter = data => {
try {
const url = new URL(x);
- id = removeWWW(url.hostname) || url.href;
+ id = url.hostname.replace('www', '') || url.href;
} catch {
id = '';
}
@@ -94,11 +92,7 @@ export const paramFilter = data => {
return obj;
}, {});
- const d = Object.keys(map).flatMap(key =>
+ return Object.keys(map).flatMap(key =>
Object.keys(map[key]).map(n => ({ x: `${key}=${n}`, p: key, v: n, y: map[key][n] })),
);
-
- console.log({ map, d });
-
- return d;
};
diff --git a/lib/kafka.js b/lib/kafka.js
index 01b69603ce..02c865e06e 100644
--- a/lib/kafka.js
+++ b/lib/kafka.js
@@ -3,13 +3,9 @@ import dateFormat from 'dateformat';
import debug from 'debug';
import { KAFKA, KAFKA_PRODUCER } from 'lib/db';
-const log = debug('kafka');
+const log = debug('umami:kafka');
function getClient() {
- if (!process.env.KAFKA_URL || !process.env.KAFKA_BROKER) {
- return null;
- }
-
const { username, password } = new URL(process.env.KAFKA_URL);
const brokers = process.env.KAFKA_BROKER.split(',');
@@ -73,8 +69,11 @@ let kafka;
let producer;
(async () => {
- kafka = global[KAFKA] || getClient();
- producer = global[KAFKA_PRODUCER] || (await getProducer());
+ kafka = process.env.KAFKA_URL && process.env.KAFKA_BROKER && (global[KAFKA] || getClient());
+
+ if (kafka) {
+ producer = global[KAFKA_PRODUCER] || (await getProducer());
+ }
})();
export default {
diff --git a/lib/middleware.js b/lib/middleware.js
index 0c6fd081c2..f728933597 100644
--- a/lib/middleware.js
+++ b/lib/middleware.js
@@ -1,19 +1,7 @@
+import { createMiddleware, unauthorized, badRequest, serverError } from 'next-basics';
import cors from 'cors';
import { getSession } from './session';
import { getAuthToken } from './auth';
-import { unauthorized, badRequest, serverError } from './response';
-
-export function createMiddleware(middleware) {
- return (req, res) =>
- new Promise((resolve, reject) => {
- middleware(req, res, result => {
- if (result instanceof Error) {
- return reject(result);
- }
- return resolve(result);
- });
- });
-}
export const useCors = createMiddleware(cors());
@@ -23,7 +11,9 @@ export const useSession = createMiddleware(async (req, res, next) => {
try {
session = await getSession(req);
} catch (e) {
+ // eslint-disable-next-line no-console
console.error(e);
+
return serverError(res, e.message);
}
diff --git a/lib/prisma.js b/lib/prisma.js
index af8759ba22..af2408d5ec 100644
--- a/lib/prisma.js
+++ b/lib/prisma.js
@@ -2,9 +2,8 @@ import { PrismaClient } from '@prisma/client';
import chalk from 'chalk';
import moment from 'moment-timezone';
import debug from 'debug';
-import { PRISMA, MYSQL, POSTGRESQL } from 'lib/db';
+import { PRISMA, MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db';
import { FILTER_IGNORED } from 'lib/constants';
-import { getDatabaseType } from 'lib/db';
const MYSQL_DATE_FORMATS = {
minute: '%Y-%m-%d %H:%i:00',
@@ -22,7 +21,7 @@ const POSTGRESQL_DATE_FORMATS = {
year: 'YYYY-01-01',
};
-const log = debug('prisma');
+const log = debug('umami:prisma');
const PRISMA_OPTIONS = {
log: [
diff --git a/lib/redis.js b/lib/redis.js
index e307729b31..01fef5a061 100644
--- a/lib/redis.js
+++ b/lib/redis.js
@@ -1,10 +1,11 @@
import { createClient } from 'redis';
import { startOfMonth } from 'date-fns';
-import { getSessions, getAllWebsites } from '/queries';
import debug from 'debug';
+import { getSessions, getAllWebsites } from 'queries';
+import { REDIS } from 'lib/db';
-const log = debug('db:redis');
-const REDIS = Symbol.for('redis');
+const log = debug('umami:redis');
+const INITIALIZED = 'redis:initialized';
async function getClient() {
const redis = new createClient({
@@ -38,7 +39,7 @@ async function stageData() {
await addRedis(sessionUuids);
await addRedis(websiteIds);
- await redis.set('initialized', 'initialized');
+ await redis.set(INITIALIZED, 1);
}
async function addRedis(ids) {
@@ -52,12 +53,12 @@ async function addRedis(ids) {
let redis = null;
(async () => {
- redis = global[REDIS] || (await getClient());
+ redis = process.env.REDIS_URL && (global[REDIS] || (await getClient()));
- const value = await redis.get('initialized');
-
- if (!value) {
- await stageData();
+ if (redis) {
+ if (!(await redis.get(INITIALIZED))) {
+ await stageData();
+ }
}
})();
diff --git a/lib/response.js b/lib/response.js
deleted file mode 100644
index 72525f1895..0000000000
--- a/lib/response.js
+++ /dev/null
@@ -1,43 +0,0 @@
-export function ok(res, data = {}) {
- return json(res, data);
-}
-
-export function json(res, data = {}) {
- return res.status(200).json(data);
-}
-
-export function send(res, data, type = 'text/plain') {
- res.setHeader('Content-Type', type);
-
- return res.status(200).send(data);
-}
-
-export function redirect(res, url) {
- res.setHeader('Location', url);
-
- return res.status(303).end();
-}
-
-export function badRequest(res, msg = '400 Bad Request') {
- return res.status(400).end(msg);
-}
-
-export function unauthorized(res, msg = '401 Unauthorized') {
- return res.status(401).end(msg);
-}
-
-export function forbidden(res, msg = '403 Forbidden') {
- return res.status(403).end(msg);
-}
-
-export function notFound(res, msg = '404 Not Found') {
- return res.status(404).end(msg);
-}
-
-export function methodNotAllowed(res, msg = '405 Method Not Allowed') {
- res.status(405).end(msg);
-}
-
-export function serverError(res, msg = '500 Internal Server Error') {
- res.status(500).end(msg);
-}
diff --git a/lib/security.js b/lib/security.js
new file mode 100644
index 0000000000..9a2a9ca131
--- /dev/null
+++ b/lib/security.js
@@ -0,0 +1,8 @@
+import { getItem } from 'next-basics';
+import { AUTH_TOKEN } from './constants';
+
+export function getAuthHeader() {
+ const token = getItem(AUTH_TOKEN);
+
+ return token ? { authorization: `Bearer ${token}` } : {};
+}
diff --git a/lib/session.js b/lib/session.js
index 769cc0dc8d..6b8bd97b63 100644
--- a/lib/session.js
+++ b/lib/session.js
@@ -1,4 +1,6 @@
-import { isValidUuid, parseToken, uuid } from 'lib/crypto';
+import { parseToken } from 'next-basics';
+import { validate } from 'uuid';
+import { uuid } from 'lib/crypto';
import redis from 'lib/redis';
import { getClientInfo, getJsonBody } from 'lib/request';
import { createSession, getSessionByUuid, getWebsiteByUuid } from 'queries';
@@ -22,8 +24,8 @@ export async function getSession(req) {
const { website: website_uuid, hostname, screen, language } = payload;
- if (!isValidUuid(website_uuid)) {
- throw new Error(`Invalid website: ${website_uuid}`);
+ if (!validate(website_uuid)) {
+ return null;
}
let websiteId = null;
@@ -52,7 +54,6 @@ export async function getSession(req) {
if (process.env.REDIS_URL) {
sessionCreated = (await redis.get(`session:${session_uuid}`)) !== null;
} else {
- console.log('test');
session = await getSessionByUuid(session_uuid);
sessionCreated = !!session;
sessionId = session ? session.session_id : null;
@@ -60,7 +61,6 @@ export async function getSession(req) {
if (!sessionCreated) {
try {
- console.log('test2');
session = await createSession(websiteId, {
session_uuid,
hostname,
diff --git a/lib/url.js b/lib/url.js
deleted file mode 100644
index 644770b6b7..0000000000
--- a/lib/url.js
+++ /dev/null
@@ -1,35 +0,0 @@
-export function removeTrailingSlash(url) {
- return url && url.length > 1 && url.endsWith('/') ? url.slice(0, -1) : url;
-}
-
-export function removeWWW(url) {
- return url && url.length > 1 && url.startsWith('www.') ? url.slice(4) : url;
-}
-
-export function getQueryString(params = {}) {
- const map = Object.keys(params).reduce((arr, key) => {
- if (params[key] !== undefined) {
- return arr.concat(`${key}=${encodeURIComponent(params[key])}`);
- }
- return arr;
- }, []);
-
- if (map.length) {
- return `?${map.join('&')}`;
- }
-
- return '';
-}
-
-export function makeUrl(url, params) {
- return `${url}${getQueryString(params)}`;
-}
-
-export function safeDecodeURI(s) {
- try {
- return decodeURI(s);
- } catch (e) {
- console.error(e);
- }
- return s;
-}
diff --git a/lib/web.js b/lib/web.js
deleted file mode 100644
index 7317d32243..0000000000
--- a/lib/web.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { makeUrl } from './url';
-
-export const apiRequest = (method, url, body, headers) => {
- return fetch(url, {
- method,
- cache: 'no-cache',
- credentials: 'same-origin',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- ...headers,
- },
- body,
- }).then(res => {
- if (res.ok) {
- return res.json().then(data => ({ ok: res.ok, status: res.status, data }));
- }
-
- return res.text().then(data => ({ ok: res.ok, status: res.status, res: res, data }));
- });
-};
-
-export const get = (url, params, headers) =>
- apiRequest('get', makeUrl(url, params), undefined, headers);
-
-export const del = (url, params, headers) =>
- apiRequest('delete', makeUrl(url, params), undefined, headers);
-
-export const post = (url, params, headers) =>
- apiRequest('post', url, JSON.stringify(params), headers);
-
-export const put = (url, params, headers) =>
- apiRequest('put', url, JSON.stringify(params), headers);
-
-export const hook = (_this, method, callback) => {
- const orig = _this[method];
-
- return (...args) => {
- callback.apply(null, args);
-
- return orig.apply(_this, args);
- };
-};
-
-export const doNotTrack = () => {
- const { doNotTrack, navigator, external } = window;
-
- const msTrackProtection = 'msTrackingProtectionEnabled';
- const msTracking = () => {
- return external && msTrackProtection in external && external[msTrackProtection]();
- };
-
- const dnt = doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack || msTracking();
-
- return dnt == '1' || dnt === 'yes';
-};
-
-export const setItem = (key, data, session) => {
- if (typeof window !== 'undefined' && data) {
- (session ? sessionStorage : localStorage).setItem(key, JSON.stringify(data));
- }
-};
-
-export const getItem = (key, session) => {
- if (typeof window !== 'undefined') {
- const value = (session ? sessionStorage : localStorage).getItem(key);
-
- if (value !== 'undefined') {
- return JSON.parse(value);
- }
- }
-};
-
-export const removeItem = (key, session) => {
- if (typeof window !== 'undefined') {
- (session ? sessionStorage : localStorage).removeItem(key);
- }
-};
diff --git a/package.json b/package.json
index 12153e841e..7cbd5e5545 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,6 @@
"dependencies": {
"@fontsource/inter": "4.5.7",
"@prisma/client": "4.2.1",
- "bcryptjs": "^2.4.3",
"chalk": "^4.1.1",
"chart.js": "^2.9.4",
"classnames": "^2.3.1",
@@ -81,11 +80,11 @@
"is-docker": "^3.0.0",
"is-localhost-ip": "^1.4.0",
"isbot": "^3.4.5",
- "jose": "2.0.5",
"kafkajs": "^2.1.0",
"maxmind": "^4.3.6",
"moment-timezone": "^0.5.33",
"next": "^12.2.5",
+ "next-basics": "^0.6.0",
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
"prop-types": "^15.7.2",
@@ -115,6 +114,8 @@
"eslint": "^7.32.0",
"eslint-config-next": "^12.2.4",
"eslint-config-prettier": "^8.5.0",
+ "eslint-import-resolver-alias": "^1.1.2",
+ "eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"extract-react-intl-messages": "^4.1.1",
"husky": "^7.0.0",
diff --git a/pages/api/account/[id].js b/pages/api/account/[id].js
index 51279f4bbc..31c4b2dc2e 100644
--- a/pages/api/account/[id].js
+++ b/pages/api/account/[id].js
@@ -1,6 +1,6 @@
import { getAccountById, deleteAccount } from 'queries';
import { useAuth } from 'lib/middleware';
-import { methodNotAllowed, ok, unauthorized } from 'lib/response';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/api/account/index.js b/pages/api/account/index.js
index a7cb3795a3..fe9cafe12f 100644
--- a/pages/api/account/index.js
+++ b/pages/api/account/index.js
@@ -1,7 +1,6 @@
+import { ok, unauthorized, methodNotAllowed, badRequest, hashPassword } from 'next-basics';
import { getAccountById, getAccountByUsername, updateAccount, createAccount } from 'queries';
import { useAuth } from 'lib/middleware';
-import { hashPassword } from 'lib/crypto';
-import { ok, unauthorized, methodNotAllowed, badRequest } from 'lib/response';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/api/account/password.js b/pages/api/account/password.js
index 432ba34cda..da3a643c1c 100644
--- a/pages/api/account/password.js
+++ b/pages/api/account/password.js
@@ -1,7 +1,13 @@
import { getAccountById, updateAccount } from 'queries';
import { useAuth } from 'lib/middleware';
-import { badRequest, methodNotAllowed, ok, unauthorized } from 'lib/response';
-import { checkPassword, hashPassword } from 'lib/crypto';
+import {
+ badRequest,
+ methodNotAllowed,
+ ok,
+ unauthorized,
+ checkPassword,
+ hashPassword,
+} from 'next-basics';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/api/accounts/index.js b/pages/api/accounts/index.js
index 29edf8289d..42b96a11dc 100644
--- a/pages/api/accounts/index.js
+++ b/pages/api/accounts/index.js
@@ -1,6 +1,6 @@
import { getAccounts } from 'queries';
import { useAuth } from 'lib/middleware';
-import { ok, unauthorized, methodNotAllowed } from 'lib/response';
+import { ok, unauthorized, methodNotAllowed } from 'next-basics';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/api/auth/login.js b/pages/api/auth/login.js
index 9a4aed9e83..f1d991009d 100644
--- a/pages/api/auth/login.js
+++ b/pages/api/auth/login.js
@@ -1,6 +1,6 @@
-import { checkPassword, createSecureToken } from 'lib/crypto';
+import { ok, unauthorized, badRequest, checkPassword, createSecureToken } from 'next-basics';
import { getAccountByUsername } from 'queries/admin/account/getAccountByUsername';
-import { ok, unauthorized, badRequest } from 'lib/response';
+import { secret } from 'lib/crypto';
export default async (req, res) => {
const { username, password } = req.body;
@@ -11,10 +11,10 @@ export default async (req, res) => {
const account = await getAccountByUsername(username);
- if (account && (await checkPassword(password, account.password))) {
+ if (account && checkPassword(password, account.password)) {
const { user_id, username, is_admin } = account;
const user = { user_id, username, is_admin };
- const token = await createSecureToken(user);
+ const token = createSecureToken(user, secret());
return ok(res, { token, user });
}
diff --git a/pages/api/auth/verify.js b/pages/api/auth/verify.js
index d9a2bb0121..303a38ecdc 100644
--- a/pages/api/auth/verify.js
+++ b/pages/api/auth/verify.js
@@ -1,5 +1,5 @@
import { useAuth } from 'lib/middleware';
-import { ok, unauthorized } from 'lib/response';
+import { ok, unauthorized } from 'next-basics';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/api/collect.js b/pages/api/collect.js
index c17dd76126..dec2f28d90 100644
--- a/pages/api/collect.js
+++ b/pages/api/collect.js
@@ -1,12 +1,10 @@
const { Resolver } = require('dns').promises;
import isbot from 'isbot';
import ipaddr from 'ipaddr.js';
+import { createToken, unauthorized, send, badRequest, forbidden } from 'next-basics';
import { savePageView, saveEvent } from 'queries';
import { useCors, useSession } from 'lib/middleware';
import { getJsonBody, getIpAddress } from 'lib/request';
-import { unauthorized, send, badRequest, forbidden } from 'lib/response';
-import { createToken } from 'lib/crypto';
-import { removeTrailingSlash } from 'lib/url';
import { uuid } from 'lib/crypto';
export default async (req, res) => {
@@ -69,7 +67,7 @@ export default async (req, res) => {
let { url, referrer, event_name, event_data } = payload;
if (process.env.REMOVE_TRAILING_SLASH) {
- url = removeTrailingSlash(url);
+ url = url.replace(/\/$/, '');
}
const event_uuid = uuid();
@@ -89,7 +87,7 @@ export default async (req, res) => {
return badRequest(res);
}
- const token = await createToken({ website_id, session_id, session_uuid });
+ const token = createToken({ website_id, session_id, session_uuid });
return send(res, token);
};
diff --git a/pages/api/config.js b/pages/api/config.js
index a72eb0ba14..27a04eaab6 100644
--- a/pages/api/config.js
+++ b/pages/api/config.js
@@ -1,4 +1,4 @@
-import { ok, methodNotAllowed } from 'lib/response';
+import { ok, methodNotAllowed } from 'next-basics';
export default async (req, res) => {
if (req.method === 'GET') {
diff --git a/pages/api/heartbeat.js b/pages/api/heartbeat.js
index a53213ada5..dd1be1eb63 100644
--- a/pages/api/heartbeat.js
+++ b/pages/api/heartbeat.js
@@ -1,4 +1,4 @@
-import { ok } from 'lib/response';
+import { ok } from 'next-basics';
export default async (req, res) => {
return ok(res, 'nice');
diff --git a/pages/api/realtime/init.js b/pages/api/realtime/init.js
index 69c70a8299..ee8f4ee253 100644
--- a/pages/api/realtime/init.js
+++ b/pages/api/realtime/init.js
@@ -1,8 +1,8 @@
import { subMinutes } from 'date-fns';
+import { ok, methodNotAllowed, createToken } from 'next-basics';
import { useAuth } from 'lib/middleware';
-import { ok, methodNotAllowed } from 'lib/response';
import { getUserWebsites, getRealtimeData } from 'queries';
-import { createToken } from 'lib/crypto';
+import { secret } from 'lib/crypto';
export default async (req, res) => {
await useAuth(req, res);
@@ -12,7 +12,7 @@ export default async (req, res) => {
const websites = await getUserWebsites(user_id);
const ids = websites.map(({ website_id }) => website_id);
- const token = await createToken({ websites: ids });
+ const token = createToken({ websites: ids }, secret());
const data = await getRealtimeData(ids, subMinutes(new Date(), 30));
return ok(res, {
diff --git a/pages/api/realtime/update.js b/pages/api/realtime/update.js
index 4fa0ea3b70..9b91663d81 100644
--- a/pages/api/realtime/update.js
+++ b/pages/api/realtime/update.js
@@ -1,8 +1,8 @@
+import { ok, methodNotAllowed, badRequest, parseToken } from 'next-basics';
import { useAuth } from 'lib/middleware';
-import { ok, methodNotAllowed, badRequest } from 'lib/response';
import { getRealtimeData } from 'queries';
-import { parseToken } from 'lib/crypto';
import { SHARE_TOKEN_HEADER } from 'lib/constants';
+import { secret } from 'lib/crypto';
export default async (req, res) => {
await useAuth(req, res);
@@ -16,7 +16,7 @@ export default async (req, res) => {
return badRequest(res);
}
- const { websites } = await parseToken(token);
+ const { websites } = parseToken(token, secret());
const data = await getRealtimeData(websites, new Date(+start_at));
diff --git a/pages/api/share/[id].js b/pages/api/share/[id].js
index 698f1ba097..e6dcb4a314 100644
--- a/pages/api/share/[id].js
+++ b/pages/api/share/[id].js
@@ -1,6 +1,6 @@
import { getWebsiteByShareId } from 'queries';
-import { ok, notFound, methodNotAllowed } from 'lib/response';
-import { createToken } from 'lib/crypto';
+import { ok, notFound, methodNotAllowed, createToken } from 'next-basics';
+import { secret } from 'lib/crypto';
export default async (req, res) => {
const { id } = req.query;
@@ -10,7 +10,7 @@ export default async (req, res) => {
if (website) {
const websiteId = website.website_id;
- const token = await createToken({ website_id: websiteId });
+ const token = createToken({ website_id: websiteId }, secret());
return ok(res, { websiteId, token });
}
diff --git a/pages/api/website/[id]/active.js b/pages/api/website/[id]/active.js
index 4acc97f1df..20550427d1 100644
--- a/pages/api/website/[id]/active.js
+++ b/pages/api/website/[id]/active.js
@@ -1,4 +1,4 @@
-import { methodNotAllowed, ok, unauthorized } from 'lib/response';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { allowQuery } from 'lib/auth';
import { useCors } from 'lib/middleware';
import { getActiveVisitors } from 'queries';
diff --git a/pages/api/website/[id]/events.js b/pages/api/website/[id]/events.js
index ab4ddc7d03..c633a58583 100644
--- a/pages/api/website/[id]/events.js
+++ b/pages/api/website/[id]/events.js
@@ -1,6 +1,6 @@
import moment from 'moment-timezone';
import { getEventMetrics } from 'queries';
-import { ok, badRequest, methodNotAllowed, unauthorized } from 'lib/response';
+import { ok, badRequest, methodNotAllowed, unauthorized } from 'next-basics';
import { allowQuery } from 'lib/auth';
import { useCors } from 'lib/middleware';
diff --git a/pages/api/website/[id]/index.js b/pages/api/website/[id]/index.js
index 048b5ff1e9..aaebb3d410 100644
--- a/pages/api/website/[id]/index.js
+++ b/pages/api/website/[id]/index.js
@@ -1,5 +1,5 @@
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteWebsite, getWebsiteById } from 'queries';
-import { methodNotAllowed, ok, unauthorized } from 'lib/response';
import { allowQuery } from 'lib/auth';
import { useCors } from 'lib/middleware';
diff --git a/pages/api/website/[id]/metrics.js b/pages/api/website/[id]/metrics.js
index b6e701e7c1..378d8c7330 100644
--- a/pages/api/website/[id]/metrics.js
+++ b/pages/api/website/[id]/metrics.js
@@ -1,5 +1,5 @@
import { getPageviewMetrics, getSessionMetrics, getWebsiteById } from 'queries';
-import { ok, methodNotAllowed, unauthorized, badRequest } from 'lib/response';
+import { ok, methodNotAllowed, unauthorized, badRequest } from 'next-basics';
import { allowQuery } from 'lib/auth';
import { useCors } from 'lib/middleware';
import { FILTER_IGNORED } from 'lib/constants';
diff --git a/pages/api/website/[id]/pageviews.js b/pages/api/website/[id]/pageviews.js
index 3bd572d93c..9a713761bf 100644
--- a/pages/api/website/[id]/pageviews.js
+++ b/pages/api/website/[id]/pageviews.js
@@ -1,6 +1,6 @@
import moment from 'moment-timezone';
import { getPageviewStats } from 'queries';
-import { ok, badRequest, methodNotAllowed, unauthorized } from 'lib/response';
+import { ok, badRequest, methodNotAllowed, unauthorized } from 'next-basics';
import { allowQuery } from 'lib/auth';
import { useCors } from 'lib/middleware';
diff --git a/pages/api/website/[id]/reset.js b/pages/api/website/[id]/reset.js
index 10fc5cb7c7..2f5d05b47b 100644
--- a/pages/api/website/[id]/reset.js
+++ b/pages/api/website/[id]/reset.js
@@ -1,5 +1,5 @@
import { resetWebsite } from 'queries';
-import { methodNotAllowed, ok, unauthorized } from 'lib/response';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { allowQuery } from 'lib/auth';
export default async (req, res) => {
diff --git a/pages/api/website/[id]/stats.js b/pages/api/website/[id]/stats.js
index bd32c79d01..d652c04859 100644
--- a/pages/api/website/[id]/stats.js
+++ b/pages/api/website/[id]/stats.js
@@ -1,5 +1,5 @@
import { getWebsiteStats } from 'queries';
-import { methodNotAllowed, ok, unauthorized } from 'lib/response';
+import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { allowQuery } from 'lib/auth';
import { useCors } from 'lib/middleware';
diff --git a/pages/api/website/index.js b/pages/api/website/index.js
index 59d0a5f1af..ee67a6b01e 100644
--- a/pages/api/website/index.js
+++ b/pages/api/website/index.js
@@ -1,7 +1,7 @@
+import { ok, unauthorized, methodNotAllowed, getRandomChars } from 'next-basics';
import { updateWebsite, createWebsite, getWebsiteById } from 'queries';
import { useAuth } from 'lib/middleware';
-import { uuid, getRandomChars } from 'lib/crypto';
-import { ok, unauthorized, methodNotAllowed } from 'lib/response';
+import { uuid } from 'lib/crypto';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/api/websites/index.js b/pages/api/websites/index.js
index b70272d8d6..8b03a9e984 100644
--- a/pages/api/websites/index.js
+++ b/pages/api/websites/index.js
@@ -1,6 +1,6 @@
import { getAllWebsites, getUserWebsites } from 'queries';
import { useAuth } from 'lib/middleware';
-import { ok, methodNotAllowed, unauthorized } from 'lib/response';
+import { ok, methodNotAllowed, unauthorized } from 'next-basics';
export default async (req, res) => {
await useAuth(req, res);
diff --git a/pages/logout.js b/pages/logout.js
index 7692fb03b8..bcc99a10be 100644
--- a/pages/logout.js
+++ b/pages/logout.js
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useRouter } from 'next/router';
-import { removeItem } from 'lib/web';
+import { removeItem } from 'next-basics';
import { AUTH_TOKEN } from 'lib/constants';
import { setUser } from 'store/app';
diff --git a/scripts/change-password.js b/scripts/change-password.js
index 5e3de42f69..a9b63c12d8 100644
--- a/scripts/change-password.js
+++ b/scripts/change-password.js
@@ -1,11 +1,11 @@
+/* eslint-disable no-console */
require('dotenv').config();
-const bcrypt = require('bcryptjs');
+const { hashPassword } = require('next-basics');
const chalk = require('chalk');
const prompts = require('prompts');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
-const SALT_ROUNDS = 10;
const runQuery = async query => {
return query.catch(e => {
@@ -24,10 +24,6 @@ const updateAccountByUsername = (username, data) => {
);
};
-const hashPassword = password => {
- return bcrypt.hashSync(password, SALT_ROUNDS);
-};
-
const changePassword = async (username, newPassword) => {
const password = hashPassword(newPassword);
return updateAccountByUsername(username, { password });
diff --git a/store/app.js b/store/app.js
index dc5f527de4..f03eb57c8a 100644
--- a/store/app.js
+++ b/store/app.js
@@ -1,6 +1,6 @@
import create from 'zustand';
import { DEFAULT_LOCALE, DEFAULT_THEME, LOCALE_CONFIG, THEME_CONFIG } from 'lib/constants';
-import { getItem } from 'lib/web';
+import { getItem } from 'next-basics';
const initialState = {
locale: getItem(LOCALE_CONFIG) || DEFAULT_LOCALE,
diff --git a/store/dashboard.js b/store/dashboard.js
index 35f4f2ff45..7c512228a5 100644
--- a/store/dashboard.js
+++ b/store/dashboard.js
@@ -1,6 +1,6 @@
import create from 'zustand';
import { DASHBOARD_CONFIG, DEFAULT_WEBSITE_LIMIT } from 'lib/constants';
-import { getItem, setItem } from 'lib/web';
+import { getItem, setItem } from 'next-basics';
export const initialState = {
showCharts: true,
diff --git a/store/version.js b/store/version.js
index ef8f3e48fc..cb5208fa73 100644
--- a/store/version.js
+++ b/store/version.js
@@ -2,7 +2,7 @@ import create from 'zustand';
import produce from 'immer';
import semver from 'semver';
import { CURRENT_VERSION, VERSION_CHECK, UPDATES_URL } from 'lib/constants';
-import { getItem } from 'lib/web';
+import { getItem } from 'next-basics';
const initialState = {
current: CURRENT_VERSION,
diff --git a/yarn.lock b/yarn.lock
index 53bf04e866..5da68afe77 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1440,11 +1440,6 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
-"@panva/asn1.js@^1.0.0":
- version "1.0.0"
- resolved "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz"
- integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==
-
"@prisma/client@4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.2.1.tgz#b384587f6066070381ea4c90228a14697a0c271b"
@@ -2373,6 +2368,11 @@ buble@^0.20.0:
minimist "^1.2.5"
regexpu-core "4.5.4"
+buffer-equal-constant-time@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
+ integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
+
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
@@ -3020,6 +3020,13 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
+ecdsa-sig-formatter@1.0.11:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
+ integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
+ dependencies:
+ safe-buffer "^5.0.1"
+
electron-to-chromium@^1.4.118:
version "1.4.143"
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.143.tgz"
@@ -3144,6 +3151,11 @@ eslint-config-prettier@^8.5.0:
resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz"
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
+eslint-import-resolver-alias@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz#297062890e31e4d6651eb5eba9534e1f6e68fc97"
+ integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==
+
eslint-import-resolver-node@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
@@ -4146,13 +4158,6 @@ jest-worker@^26.2.1:
merge-stream "^2.0.0"
supports-color "^7.0.0"
-jose@2.0.5:
- version "2.0.5"
- resolved "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz"
- integrity sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==
- dependencies:
- "@panva/asn1.js" "^1.0.0"
-
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -4254,6 +4259,22 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
+jsonwebtoken@^8.5.1:
+ version "8.5.1"
+ resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
+ integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
+ dependencies:
+ jws "^3.2.2"
+ lodash.includes "^4.3.0"
+ lodash.isboolean "^3.0.3"
+ lodash.isinteger "^4.0.4"
+ lodash.isnumber "^3.0.3"
+ lodash.isplainobject "^4.0.6"
+ lodash.isstring "^4.0.1"
+ lodash.once "^4.0.0"
+ ms "^2.1.1"
+ semver "^5.6.0"
+
jsprim@^1.2.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
@@ -4272,6 +4293,23 @@ jsprim@^1.2.2:
array-includes "^3.1.5"
object.assign "^4.1.3"
+jwa@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
+ integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
+ dependencies:
+ buffer-equal-constant-time "1.0.1"
+ ecdsa-sig-formatter "1.0.11"
+ safe-buffer "^5.0.1"
+
+jws@^3.2.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
+ integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
+ dependencies:
+ jwa "^1.4.1"
+ safe-buffer "^5.0.1"
+
kafkajs@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.2.0.tgz#43b2d13c82395acee4500f09d6c7d503db8c77ea"
@@ -4395,6 +4433,36 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+lodash.includes@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
+ integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==
+
+lodash.isboolean@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
+ integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
+
+lodash.isinteger@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
+ integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==
+
+lodash.isnumber@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
+ integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
+
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+ integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+
+lodash.isstring@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+ integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
+
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
@@ -4405,6 +4473,11 @@ lodash.mergewith@^4.6.2:
resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz"
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
+lodash.once@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+ integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
+
lodash.pick@^4.4.0:
version "4.4.0"
resolved "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz"
@@ -4662,6 +4735,14 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+next-basics@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/next-basics/-/next-basics-0.6.0.tgz#bbb3b2dafa69931c3b7aad0cd456332ddcf019c7"
+ integrity sha512-S9deRGhQPj9tN9WSroK8UAcxFuoV38YNFO9B5qEQpt7ZUNCkAUITccW98LGlJ5WfNzkp7dnXVgmL3+yvRWlH4w==
+ dependencies:
+ bcryptjs "^2.4.3"
+ jsonwebtoken "^8.5.1"
+
next@^12.2.5:
version "12.2.5"
resolved "https://registry.yarnpkg.com/next/-/next-12.2.5.tgz#14fb5975e8841fad09553b8ef41fe1393602b717"
@@ -5866,7 +5947,7 @@ semver-compare@^1.0.0:
resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
-"semver@2 || 3 || 4 || 5", semver@^5.5.0:
+"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0:
version "5.7.1"
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==