From e28ee6597ac3f9e248d4c26fb73aef2168ae2f3a Mon Sep 17 00:00:00 2001 From: Brian Cao Date: Fri, 18 Nov 2022 18:49:58 -0800 Subject: [PATCH] Fix Website auth. --- interface/api/models.d.ts | 7 ---- lib/{auth.js => auth.ts} | 35 ++++++++++++------- lib/{cache.js => cache.ts} | 11 ++++-- lib/{constants.js => constants.ts} | 12 +++++++ lib/enum.ts | 7 ---- pages/api/auth/login.ts | 3 +- pages/api/users/[id]/password.ts | 17 +++++---- pages/api/websites/[id]/active.ts | 12 +++---- pages/api/websites/[id]/eventdata.ts | 25 +++++-------- pages/api/websites/[id]/events.ts | 4 +-- pages/api/websites/[id]/index.ts | 14 ++++---- pages/api/websites/[id]/metrics.ts | 4 +-- pages/api/websites/[id]/pageviews.ts | 4 +-- pages/api/websites/[id]/reset.ts | 12 +++---- pages/api/websites/[id]/stats.ts | 12 +++---- queries/admin/user.ts | 2 +- queries/admin/userWebsite.ts | 6 ++-- queries/analytics/event/getEventData.ts | 16 ++++----- queries/analytics/event/getEventMetrics.ts | 2 +- queries/analytics/event/saveEvent.ts | 2 +- .../analytics/pageview/getPageviewMetrics.ts | 2 +- .../analytics/pageview/getPageviewStats.ts | 2 +- queries/analytics/pageview/savePageView.ts | 2 +- 23 files changed, 108 insertions(+), 105 deletions(-) rename lib/{auth.js => auth.ts} (67%) rename lib/{cache.js => cache.ts} (80%) rename lib/{constants.js => constants.ts} (97%) delete mode 100644 lib/enum.ts diff --git a/interface/api/models.d.ts b/interface/api/models.d.ts index 6251a149..3359a651 100644 --- a/interface/api/models.d.ts +++ b/interface/api/models.d.ts @@ -1,10 +1,3 @@ -export interface User { - id: string; - username: string; - isAdmin: boolean; - createdAt: string; -} - export interface Website { id: string; userId: string; diff --git a/lib/auth.js b/lib/auth.ts similarity index 67% rename from lib/auth.js rename to lib/auth.ts index 09193e89..050e1c20 100644 --- a/lib/auth.js +++ b/lib/auth.ts @@ -1,8 +1,9 @@ -import { parseSecureToken, parseToken } from 'next-basics'; -import { getUser, getWebsite } from 'queries'; import debug from 'debug'; -import { SHARE_TOKEN_HEADER, TYPE_USER, TYPE_WEBSITE } from 'lib/constants'; +import { NextApiRequestAuth } from 'interface/api/nextApi'; +import { SHARE_TOKEN_HEADER, UmamiApi } from 'lib/constants'; import { secret } from 'lib/crypto'; +import { parseSecureToken, parseToken } from 'next-basics'; +import { getUser, getUserWebsite } from 'queries'; const log = debug('umami:auth'); @@ -47,30 +48,38 @@ export function isValidToken(token, validation) { return false; } -export async function allowQuery(req, type) { - const { id } = req.query; +export async function allowQuery( + req: NextApiRequestAuth, + type: UmamiApi.AuthType, + typeId?: string, +) { + const { id } = req.query as { id: string }; const { user, shareToken } = req.auth; - if (user?.isAdmin) { - return true; - } - if (shareToken) { return isValidToken(shareToken, { id }); } if (user?.id) { - if (type === TYPE_WEBSITE) { - const website = await getWebsite({ id }); + if (type === UmamiApi.AuthType.Website) { + const userWebsite = await getUserWebsite({ + userId: user.id, + websiteId: typeId ?? id, + isDeleted: false, + }); - return website && website.userId === user.id; - } else if (type === TYPE_USER) { + return userWebsite; + } else if (type === UmamiApi.AuthType.User) { const user = await getUser({ id }); return user && user.id === id; } } + if (user?.isAdmin) { + return true; + } + return false; } diff --git a/lib/cache.js b/lib/cache.ts similarity index 80% rename from lib/cache.js rename to lib/cache.ts index bc065c14..6ffbb5fd 100644 --- a/lib/cache.js +++ b/lib/cache.ts @@ -1,5 +1,6 @@ import { getWebsite, getUser, getSession } from '../queries'; import redis, { DELETED } from 'lib/redis'; +import { Role, Team, TeamUser, User, UserRole, UserWebsite, Website } from '@prisma/client'; async function fetchObject(key, query) { const obj = await redis.get(key); @@ -40,8 +41,14 @@ async function deleteWebsite(id) { return deleteObject(`website:${id}`); } -async function fetchUser(id) { - return fetchObject(`user:${id}`, () => getUser({ id })); +async function fetchUser(id): Promise< + User & { + userRole?: (UserRole & { role: Role })[]; + teamUser?: (TeamUser & { team: Team })[]; + userWebsite?: (UserWebsite & { website: Website })[]; + } +> { + return fetchObject(`user:${id}`, () => getUser({ id }, true)); } async function storeUser(data) { diff --git a/lib/constants.js b/lib/constants.ts similarity index 97% rename from lib/constants.js rename to lib/constants.ts index 0e4eabec..cf56c39f 100644 --- a/lib/constants.js +++ b/lib/constants.ts @@ -1,3 +1,15 @@ +/* eslint-disable no-unused-vars */ +export namespace UmamiApi { + export enum EventType { + Pageview = 1, + Event = 2, + } + + export enum AuthType { + Website, + User, + } +} export const CURRENT_VERSION = process.env.currentVersion; export const AUTH_TOKEN = 'umami.auth'; export const LOCALE_CONFIG = 'umami.locale'; diff --git a/lib/enum.ts b/lib/enum.ts deleted file mode 100644 index d8270333..00000000 --- a/lib/enum.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable no-unused-vars */ -export namespace UmamiApi { - export enum EventType { - Pageview = 1, - Event = 2, - } -} diff --git a/pages/api/auth/login.ts b/pages/api/auth/login.ts index 321fb3ab..b7458a7a 100644 --- a/pages/api/auth/login.ts +++ b/pages/api/auth/login.ts @@ -7,12 +7,11 @@ import { methodNotAllowed, getRandomChars, } from 'next-basics'; -import { getUser } from 'queries'; +import { getUser, User } from 'queries'; import { secret } from 'lib/crypto'; import redis from 'lib/redis'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; import { NextApiResponse } from 'next'; -import { User } from 'interface/api/models'; export interface LoginRequestBody { username: string; diff --git a/pages/api/users/[id]/password.ts b/pages/api/users/[id]/password.ts index 4b00d7d5..e0024eda 100644 --- a/pages/api/users/[id]/password.ts +++ b/pages/api/users/[id]/password.ts @@ -1,18 +1,17 @@ -import { getUser, updateUser } from 'queries'; +import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; import { useAuth } from 'lib/middleware'; +import { NextApiResponse } from 'next'; import { badRequest, + checkPassword, + hashPassword, methodNotAllowed, ok, unauthorized, - checkPassword, - hashPassword, } from 'next-basics'; -import { allowQuery } from 'lib/auth'; -import { TYPE_USER } from 'lib/constants'; -import { NextApiRequestQueryBody } from 'interface/api/nextApi'; -import { NextApiResponse } from 'next'; -import { User } from 'interface/api/models'; +import { getUser, updateUser, User } from 'queries'; export interface UserPasswordRequestQuery { id: string; @@ -32,7 +31,7 @@ export default async ( const { current_password, new_password } = req.body; const { id } = req.query; - if (!(await allowQuery(req, TYPE_USER))) { + if (!(await allowQuery(req, UmamiApi.AuthType.User))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/active.ts b/pages/api/websites/[id]/active.ts index b50c29e7..3323ad7b 100644 --- a/pages/api/websites/[id]/active.ts +++ b/pages/api/websites/[id]/active.ts @@ -1,11 +1,11 @@ -import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { allowQuery } from 'lib/auth'; -import { useAuth, useCors } from 'lib/middleware'; -import { getActiveVisitors } from 'queries'; -import { TYPE_WEBSITE } from 'lib/constants'; import { WebsiteActive } from 'interface/api/models'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; +import { useAuth, useCors } from 'lib/middleware'; import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getActiveVisitors } from 'queries'; export interface WebsiteActiveRequestQuery { id: string; @@ -19,7 +19,7 @@ export default async ( await useAuth(req, res); if (req.method === 'GET') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/eventdata.ts b/pages/api/websites/[id]/eventdata.ts index 646b2920..120f61cc 100644 --- a/pages/api/websites/[id]/eventdata.ts +++ b/pages/api/websites/[id]/eventdata.ts @@ -1,12 +1,11 @@ -import moment from 'moment-timezone'; -import { getEventData } from 'queries'; -import { ok, badRequest, methodNotAllowed, unauthorized } from 'next-basics'; -import { allowQuery } from 'lib/auth'; -import { useAuth, useCors } from 'lib/middleware'; -import { TYPE_WEBSITE } from 'lib/constants'; -import { NextApiRequestQueryBody } from 'interface/api/nextApi'; -import { NextApiResponse } from 'next'; import { WebsiteMetric } from 'interface/api/models'; +import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; +import { useAuth, useCors } from 'lib/middleware'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getEventData } from 'queries'; export interface WebsiteEventDataRequestQuery { id: string; @@ -15,7 +14,6 @@ export interface WebsiteEventDataRequestQuery { export interface WebsiteEventDataRequestBody { start_at: string; end_at: string; - timezone: string; event_name: string; columns: { [key: string]: 'count' | 'max' | 'min' | 'avg' | 'sum' }; filters?: { [key: string]: any }; @@ -29,17 +27,13 @@ export default async ( await useAuth(req, res); if (req.method === 'POST') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } const { id: websiteId } = req.query; - const { start_at, end_at, timezone, event_name: eventName, columns, filters } = req.body; - - if (!moment.tz.zone(timezone)) { - return badRequest(res); - } + const { start_at, end_at, event_name: eventName, columns, filters } = req.body; const startDate = new Date(+start_at); const endDate = new Date(+end_at); @@ -47,7 +41,6 @@ export default async ( const events = await getEventData(websiteId, { startDate, endDate, - timezone, eventName, columns, filters, diff --git a/pages/api/websites/[id]/events.ts b/pages/api/websites/[id]/events.ts index efbf5a39..832cb727 100644 --- a/pages/api/websites/[id]/events.ts +++ b/pages/api/websites/[id]/events.ts @@ -1,7 +1,7 @@ import { WebsiteMetric } from 'interface/api/models'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; import { allowQuery } from 'lib/auth'; -import { TYPE_WEBSITE } from 'lib/constants'; +import { UmamiApi } from 'lib/constants'; import { useAuth, useCors } from 'lib/middleware'; import moment from 'moment-timezone'; import { NextApiResponse } from 'next'; @@ -28,7 +28,7 @@ export default async ( await useAuth(req, res); if (req.method === 'GET') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/index.ts b/pages/api/websites/[id]/index.ts index 2ec1ee14..5cf6b474 100644 --- a/pages/api/websites/[id]/index.ts +++ b/pages/api/websites/[id]/index.ts @@ -1,11 +1,11 @@ +import { Website } from 'interface/api/models'; +import { NextApiRequestQueryBody } from 'interface/api/nextApi'; import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; import { useAuth, useCors } from 'lib/middleware'; +import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics'; import { deleteWebsite, getWebsite, updateWebsite } from 'queries'; -import { TYPE_WEBSITE } from 'lib/constants'; -import { NextApiRequestQueryBody } from 'interface/api/nextApi'; -import { NextApiResponse } from 'next'; -import { Website } from 'interface/api/models'; export interface WebsiteRequestQuery { id: string; @@ -26,7 +26,7 @@ export default async ( const { id: websiteId } = req.query; - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } @@ -45,7 +45,7 @@ export default async ( domain, shareId, }); - } catch (e) { + } catch (e: any) { if (e.message.includes('Unique constraint') && e.message.includes('share_id')) { return serverError(res, 'That share ID is already taken.'); } @@ -55,7 +55,7 @@ export default async ( } if (req.method === 'DELETE') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/metrics.ts b/pages/api/websites/[id]/metrics.ts index 69c3c79d..d3cdbf07 100644 --- a/pages/api/websites/[id]/metrics.ts +++ b/pages/api/websites/[id]/metrics.ts @@ -1,7 +1,7 @@ import { WebsiteMetric } from 'interface/api/models'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; import { allowQuery } from 'lib/auth'; -import { FILTER_IGNORED, TYPE_WEBSITE } from 'lib/constants'; +import { FILTER_IGNORED, UmamiApi } from 'lib/constants'; import { useAuth, useCors } from 'lib/middleware'; import { NextApiResponse } from 'next'; import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics'; @@ -57,7 +57,7 @@ export default async ( await useAuth(req, res); if (req.method === 'GET') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/pageviews.ts b/pages/api/websites/[id]/pageviews.ts index 5bbf067b..208a052a 100644 --- a/pages/api/websites/[id]/pageviews.ts +++ b/pages/api/websites/[id]/pageviews.ts @@ -1,7 +1,7 @@ import { WebsitePageviews } from 'interface/api/models'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; import { allowQuery } from 'lib/auth'; -import { TYPE_WEBSITE } from 'lib/constants'; +import { UmamiApi } from 'lib/constants'; import { useAuth, useCors } from 'lib/middleware'; import moment from 'moment-timezone'; import { NextApiResponse } from 'next'; @@ -33,7 +33,7 @@ export default async ( await useAuth(req, res); if (req.method === 'GET') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/reset.ts b/pages/api/websites/[id]/reset.ts index ff141398..a5473ee6 100644 --- a/pages/api/websites/[id]/reset.ts +++ b/pages/api/websites/[id]/reset.ts @@ -1,10 +1,10 @@ -import { resetWebsite } from 'queries'; -import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { allowQuery } from 'lib/auth'; -import { useAuth, useCors } from 'lib/middleware'; -import { TYPE_WEBSITE } from 'lib/constants'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; +import { useAuth, useCors } from 'lib/middleware'; import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { resetWebsite } from 'queries'; export interface WebsiteResetRequestQuery { id: string; @@ -20,7 +20,7 @@ export default async ( const { id: websiteId } = req.query; if (req.method === 'POST') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/stats.ts b/pages/api/websites/[id]/stats.ts index 497c3ff2..2122a2da 100644 --- a/pages/api/websites/[id]/stats.ts +++ b/pages/api/websites/[id]/stats.ts @@ -1,11 +1,11 @@ -import { getWebsiteStats } from 'queries'; -import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { allowQuery } from 'lib/auth'; -import { useAuth, useCors } from 'lib/middleware'; -import { TYPE_WEBSITE } from 'lib/constants'; import { WebsiteStats } from 'interface/api/models'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; +import { useAuth, useCors } from 'lib/middleware'; import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getWebsiteStats } from 'queries'; export interface WebsiteStatsRequestQuery { id: string; @@ -28,7 +28,7 @@ export default async ( await useAuth(req, res); if (req.method === 'GET') { - if (!(await allowQuery(req, TYPE_WEBSITE))) { + if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { return unauthorized(res); } diff --git a/queries/admin/user.ts b/queries/admin/user.ts index e03974f2..75a98194 100644 --- a/queries/admin/user.ts +++ b/queries/admin/user.ts @@ -1,5 +1,5 @@ import { Prisma } from '@prisma/client'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; import cache from 'lib/cache'; import prisma from 'lib/prisma'; diff --git a/queries/admin/userWebsite.ts b/queries/admin/userWebsite.ts index 90039b8f..76f924ec 100644 --- a/queries/admin/userWebsite.ts +++ b/queries/admin/userWebsite.ts @@ -9,10 +9,8 @@ export async function createUserWebsite( }); } -export async function getUserWebsite( - where: Prisma.UserWebsiteWhereUniqueInput, -): Promise { - return prisma.client.userWebsite.findUnique({ +export async function getUserWebsite(where: Prisma.UserWebsiteWhereInput): Promise { + return prisma.client.userWebsite.findFirst({ where, }); } diff --git a/queries/analytics/event/getEventData.ts b/queries/analytics/event/getEventData.ts index 3c6bd460..39002cf3 100644 --- a/queries/analytics/event/getEventData.ts +++ b/queries/analytics/event/getEventData.ts @@ -3,7 +3,7 @@ import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import cache from 'lib/cache'; import { WebsiteMetric } from 'interface/api/models'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; export async function getEventData( ...args: [ @@ -11,7 +11,7 @@ export async function getEventData( data: { startDate: Date; endDate: Date; - event_name: string; + eventName: string; columns: any; filters: object; }, @@ -32,12 +32,12 @@ async function relationalQuery( data: { startDate: Date; endDate: Date; - event_name: string; + eventName: string; columns: any; filters: object; }, ) { - const { startDate, endDate, event_name, columns, filters } = data; + const { startDate, endDate, eventName, columns, filters } = data; const { rawQuery, getEventDataColumnsQuery, getEventDataFilterQuery } = prisma; const params = [startDate, endDate]; @@ -48,7 +48,7 @@ async function relationalQuery( where website_id ='${websiteId}' and created_at between $1 and $2 and event_type = ${UmamiApi.EventType.Event} - ${event_name ? `and event_name = ${event_name}` : ''} + ${eventName ? `and eventName = ${eventName}` : ''} ${ Object.keys(filters).length > 0 ? `and ${getEventDataFilterQuery('event_data', filters)}` @@ -63,12 +63,12 @@ async function clickhouseQuery( data: { startDate: Date; endDate: Date; - event_name: string; + eventName: string; columns: any; filters: object; }, ) { - const { startDate, endDate, event_name, columns, filters } = data; + const { startDate, endDate, eventName, columns, filters } = data; const { rawQuery, getBetweenDates, getEventDataColumnsQuery, getEventDataFilterQuery } = clickhouse; const website = await cache.fetchWebsite(websiteId); @@ -81,7 +81,7 @@ async function clickhouseQuery( where website_id = $1 and rev_id = $2 and event_type = ${UmamiApi.EventType.Event} - ${event_name ? `and event_name = ${event_name}` : ''} + ${eventName ? `and eventName = ${eventName}` : ''} and ${getBetweenDates('created_at', startDate, endDate)} ${ Object.keys(filters).length > 0 diff --git a/queries/analytics/event/getEventMetrics.ts b/queries/analytics/event/getEventMetrics.ts index 96843dce..ebf855f0 100644 --- a/queries/analytics/event/getEventMetrics.ts +++ b/queries/analytics/event/getEventMetrics.ts @@ -3,7 +3,7 @@ import clickhouse from 'lib/clickhouse'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; import cache from 'lib/cache'; import { WebsiteEventMetric } from 'interface/api/models'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; export async function getEventMetrics( ...args: [ diff --git a/queries/analytics/event/saveEvent.ts b/queries/analytics/event/saveEvent.ts index 02a349e3..521f6c28 100644 --- a/queries/analytics/event/saveEvent.ts +++ b/queries/analytics/event/saveEvent.ts @@ -4,7 +4,7 @@ import kafka from 'lib/kafka'; import prisma from 'lib/prisma'; import { uuid } from 'lib/crypto'; import cache from 'lib/cache'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; export async function saveEvent(args: { id: string; diff --git a/queries/analytics/pageview/getPageviewMetrics.ts b/queries/analytics/pageview/getPageviewMetrics.ts index de8fe448..f61e25bd 100644 --- a/queries/analytics/pageview/getPageviewMetrics.ts +++ b/queries/analytics/pageview/getPageviewMetrics.ts @@ -3,7 +3,7 @@ import clickhouse from 'lib/clickhouse'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; import cache from 'lib/cache'; import { Prisma } from '@prisma/client'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; export async function getPageviewMetrics( ...args: [ diff --git a/queries/analytics/pageview/getPageviewStats.ts b/queries/analytics/pageview/getPageviewStats.ts index f9cda5fe..212a07de 100644 --- a/queries/analytics/pageview/getPageviewStats.ts +++ b/queries/analytics/pageview/getPageviewStats.ts @@ -2,7 +2,7 @@ import cache from 'lib/cache'; import clickhouse from 'lib/clickhouse'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; export async function getPageviewStats( ...args: [ diff --git a/queries/analytics/pageview/savePageView.ts b/queries/analytics/pageview/savePageView.ts index 793a8cd4..fd3f7e81 100644 --- a/queries/analytics/pageview/savePageView.ts +++ b/queries/analytics/pageview/savePageView.ts @@ -4,7 +4,7 @@ import kafka from 'lib/kafka'; import prisma from 'lib/prisma'; import cache from 'lib/cache'; import { uuid } from 'lib/crypto'; -import { UmamiApi } from 'lib/enum'; +import { UmamiApi } from 'lib/constants'; export async function savePageView(args: { id: string;