Refactored queries.

This commit is contained in:
Mike Cao 2023-07-29 22:03:34 -07:00
parent f36a689817
commit 8904b7b4ed
27 changed files with 137 additions and 144 deletions

View File

@ -10,8 +10,7 @@ import {
parseSecureToken,
parseToken,
} from 'next-basics';
import { getTeamUser } from 'queries';
import { getTeamWebsite, getTeamWebsiteByTeamMemberId } from 'queries/admin/teamWebsite';
import { getTeamUser, getTeamWebsite, findTeamWebsiteByUserId } from 'queries';
import { loadWebsite } from './load';
import { Auth } from './types';
@ -79,19 +78,13 @@ export async function canViewWebsite({ user, shareToken }: Auth, websiteId: stri
return true;
}
const teamWebsite = await getTeamWebsiteByTeamMemberId(websiteId, user.id);
if (teamWebsite) {
return true;
}
const website = await loadWebsite(websiteId);
if (website.userId) {
return user.id === website.userId;
}
return false;
return !!(await findTeamWebsiteByUserId(websiteId, user.id));
}
export async function canCreateWebsite({ user }: Auth) {
@ -139,15 +132,11 @@ export async function canViewReport(auth: Auth, report: Report) {
return true;
}
if ((auth.user.id = report.userId)) {
if (auth.user.id == report.userId) {
return true;
}
if (await canViewWebsite(auth, report.websiteId)) {
return true;
}
return false;
return !!(await canViewWebsite(auth, report.websiteId));
}
export async function canUpdateReport(auth: Auth, report: Report) {
@ -155,11 +144,7 @@ export async function canUpdateReport(auth: Auth, report: Report) {
return true;
}
if ((auth.user.id = report.userId)) {
return true;
}
return false;
return auth.user.id == report.userId;
}
export async function canCreateTeam({ user }: Auth) {

View File

@ -1,11 +1,11 @@
import { User, Website } from '@prisma/client';
import redis from '@umami/redis-client';
import { getSession, getUser, getWebsite } from '../queries';
import { getSession, getUserById, getWebsiteById } from '../queries';
const { fetchObject, storeObject, deleteObject } = redis;
async function fetchWebsite(id): Promise<Website> {
return fetchObject(`website:${id}`, () => getWebsite({ id }));
return fetchObject(`website:${id}`, () => getWebsiteById(id));
}
async function storeWebsite(data) {
@ -20,7 +20,7 @@ async function deleteWebsite(id) {
}
async function fetchUser(id): Promise<User> {
return fetchObject(`user:${id}`, () => getUser({ id }, { includePassword: true }));
return fetchObject(`user:${id}`, () => getUserById(id, { includePassword: true }));
}
async function storeUser(data) {
@ -35,7 +35,7 @@ async function deleteUser(id) {
}
async function fetchSession(id) {
return fetchObject(`session:${id}`, () => getSession({ id }));
return fetchObject(`session:${id}`, () => getSession(id));
}
async function storeSession(data) {

View File

@ -1,5 +1,5 @@
import cache from 'lib/cache';
import { getWebsite, getSession, getUser } from 'queries';
import { getSession, getUserById, getWebsiteById } from 'queries';
import { User, Website, Session } from '@prisma/client';
export async function loadWebsite(websiteId: string): Promise<Website> {
@ -8,7 +8,7 @@ export async function loadWebsite(websiteId: string): Promise<Website> {
if (cache.enabled) {
website = await cache.fetchWebsite(websiteId);
} else {
website = await getWebsite({ id: websiteId });
website = await getWebsiteById(websiteId);
}
if (!website || website.deletedAt) {
@ -24,7 +24,7 @@ export async function loadSession(sessionId: string): Promise<Session> {
if (cache.enabled) {
session = await cache.fetchSession(sessionId);
} else {
session = await getSession({ id: sessionId });
session = await getSession(sessionId);
}
if (!session) {
@ -40,7 +40,7 @@ export async function loadUser(userId: string): Promise<User> {
if (cache.enabled) {
user = await cache.fetchUser(userId);
} else {
user = await getUser({ id: userId });
user = await getUserById(userId);
}
if (!user || user.deletedAt) {

View File

@ -12,7 +12,7 @@ import { findSession } from 'lib/session';
import { getAuthToken, parseShareToken } from 'lib/auth';
import { secret, isUuid } from 'lib/crypto';
import { ROLES } from 'lib/constants';
import { getUser } from '../queries';
import { getUserById } from '../queries';
import { NextApiRequestCollect } from 'pages/api/send';
const log = debug('umami:middleware');
@ -53,7 +53,7 @@ export const useAuth = createMiddleware(async (req, res, next) => {
const { userId, authKey } = payload || {};
if (isUuid(userId)) {
user = await getUser({ id: userId });
user = await getUserById(userId);
} else if (redis.enabled && authKey) {
user = await redis.get(authKey);
}

View File

@ -9,7 +9,7 @@ import {
methodNotAllowed,
} from 'next-basics';
import redis from '@umami/redis-client';
import { getUser } from 'queries';
import { getUserByUsername } from 'queries';
import { secret } from 'lib/crypto';
import { NextApiRequestQueryBody, User } from 'lib/types';
import { setAuthKey } from 'lib/auth';
@ -37,7 +37,7 @@ export default async (
return badRequest(res);
}
const user = await getUser({ username }, { includePassword: true });
const user = await getUserByUsername(username, { includePassword: true });
if (user && checkPassword(password, user.password)) {
if (redis.enabled) {

View File

@ -9,7 +9,7 @@ import {
forbidden,
ok,
} from 'next-basics';
import { getUser, updateUser } from 'queries';
import { getUserById, updateUser } from 'queries';
export interface UserPasswordRequestQuery {
id: string;
@ -34,7 +34,7 @@ export default async (
const { id } = req.auth.user;
if (req.method === 'POST') {
const user = await getUser({ id }, { includePassword: true });
const user = await getUserById(id, { includePassword: true });
if (!checkPassword(currentPassword, user.password)) {
return badRequest(res, 'Current password is incorrect');

View File

@ -52,19 +52,14 @@ export default async (
return unauthorized(res);
}
const result = await updateReport(
{
websiteId,
userId,
type,
name,
description,
parameters: JSON.stringify(parameters),
} as any,
{
id: reportId,
},
);
const result = await updateReport(reportId, {
websiteId,
userId,
type,
name,
description,
parameters: JSON.stringify(parameters),
} as any);
return ok(res, result);
}

View File

@ -2,7 +2,7 @@ import { useAuth, useCors } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createReport, getReports } from 'queries';
import { createReport, getWebsiteReports } from 'queries';
import { canViewWebsite } from 'lib/auth';
import { uuid } from 'lib/crypto';
@ -35,7 +35,7 @@ export default async (
return unauthorized(res);
}
const data = await getReports({ websiteId });
const data = await getWebsiteReports(websiteId);
return ok(res, data);
}

View File

@ -2,7 +2,7 @@ import { NextApiRequestQueryBody } from 'lib/types';
import { secret } from 'lib/crypto';
import { NextApiResponse } from 'next';
import { createToken, methodNotAllowed, notFound, ok } from 'next-basics';
import { getWebsite } from 'queries';
import { getWebsiteByShareId } from 'queries';
export interface ShareRequestQuery {
id: string;
@ -20,7 +20,7 @@ export default async (
const { id: shareId } = req.query;
if (req.method === 'GET') {
const website = await getWebsite({ shareId });
const website = await getWebsiteByShareId(shareId);
if (website) {
const data = { websiteId: website.id };

View File

@ -4,7 +4,7 @@ import { canDeleteTeam, canUpdateTeam, canViewTeam } from 'lib/auth';
import { useAuth } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteTeam, getTeam, updateTeam } from 'queries';
import { deleteTeam, getTeamById, updateTeam } from 'queries';
export interface TeamRequestQuery {
id: string;
@ -28,7 +28,7 @@ export default async (
return unauthorized(res);
}
const user = await getTeam({ id: teamId });
const user = await getTeamById(teamId, { includeTeamUser: true });
return ok(res, user);
}
@ -41,7 +41,7 @@ export default async (
const { name, accessCode } = req.body;
const data = { name, accessCode };
const updated = await updateTeam(data, { id: teamId });
const updated = await updateTeam(teamId, data);
return ok(res, updated);
}

View File

@ -3,7 +3,7 @@ import { useAuth } from 'lib/middleware';
import { NextApiRequestQueryBody } from 'lib/types';
import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createTeamUser, getTeamUsers, getUser } from 'queries';
import { createTeamUser, getTeamUsers, getUserByUsername } from 'queries';
export interface TeamUserRequestQuery {
id: string;
@ -40,7 +40,7 @@ export default async (
const { email, roleId: roleId } = req.body;
// Check for User
const user = await getUser({ username: email });
const user = await getUserByUsername(email);
if (!user) {
return badRequest(res, 'The User does not exists.');

View File

@ -3,7 +3,7 @@ import { NextApiRequestQueryBody } from 'lib/types';
import { useAuth } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, notFound } from 'next-basics';
import { createTeamUser, getTeam, getTeamUser } from 'queries';
import { createTeamUser, getTeamByAccessCode, getTeamUser } from 'queries';
import { ROLES } from 'lib/constants';
export interface TeamsJoinRequestBody {
@ -19,7 +19,7 @@ export default async (
if (req.method === 'POST') {
const { accessCode } = req.body;
const team = await getTeam({ accessCode });
const team = await getTeamByAccessCode(accessCode);
if (!team) {
return notFound(res, 'message.team-not-found');

View File

@ -3,7 +3,7 @@ import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth';
import { useAuth } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { deleteUser, getUser, updateUser } from 'queries';
import { deleteUser, getUserById, getUserByUsername, updateUser } from 'queries';
export interface UserRequestQuery {
id: string;
@ -31,7 +31,7 @@ export default async (
return unauthorized(res);
}
const user = await getUser({ id });
const user = await getUserById(id);
return ok(res, user);
}
@ -43,7 +43,7 @@ export default async (
const { username, password, role } = req.body;
const user = await getUser({ id });
const user = await getUserById(id);
const data: any = {};
@ -62,9 +62,9 @@ export default async (
// Check when username changes
if (data.username && user.username !== data.username) {
const userByUsername = await getUser({ username });
const user = await getUserByUsername(username);
if (userByUsername) {
if (user) {
return badRequest(res, 'User already exists');
}
}

View File

@ -5,7 +5,7 @@ import { useAuth } from 'lib/middleware';
import { NextApiRequestQueryBody, Role, User } from 'lib/types';
import { NextApiResponse } from 'next';
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createUser, getUser, getUsers } from 'queries';
import { createUser, getUserByUsername, getUsers } from 'queries';
export interface UsersRequestBody {
username: string;
@ -37,7 +37,7 @@ export default async (
const { username, password, role, id } = req.body;
const existingUser = await getUser({ username }, { showDeleted: true });
const existingUser = await getUserByUsername(username, { showDeleted: true });
if (existingUser) {
return badRequest(res, 'User already exists');

View File

@ -3,7 +3,7 @@ import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
import { Website, NextApiRequestQueryBody } from 'lib/types';
import { canViewWebsite, canUpdateWebsite, canDeleteWebsite } from 'lib/auth';
import { useAuth, useCors } from 'lib/middleware';
import { deleteWebsite, getWebsite, updateWebsite } from 'queries';
import { deleteWebsite, getWebsiteById, updateWebsite } from 'queries';
import { SHARE_ID_REGEX } from 'lib/constants';
export interface WebsiteRequestQuery {
@ -30,7 +30,7 @@ export default async (
return unauthorized(res);
}
const website = await getWebsite({ id: websiteId });
const website = await getWebsiteById(websiteId);
return ok(res, website);
}

View File

@ -13,19 +13,29 @@ export async function getReportById(reportId: string): Promise<Report> {
});
}
export async function getReports(where: Prisma.ReportWhereInput): Promise<Report[]> {
export async function getUserReports(userId: string): Promise<Report[]> {
return prisma.client.report.findMany({
where,
where: {
userId,
},
});
}
export async function getWebsiteReports(websiteId: string): Promise<Report[]> {
return prisma.client.report.findMany({
where: {
websiteId,
},
});
}
export async function updateReport(
reportId: string,
data: Prisma.ReportUpdateInput,
where: Prisma.ReportWhereUniqueInput,
): Promise<Report> {
return prisma.client.report.update({ data, where });
return prisma.client.report.update({ where: { id: reportId }, data });
}
export async function deleteReport(where: Prisma.ReportWhereUniqueInput): Promise<Report> {
return prisma.client.report.delete({ where });
export async function deleteReport(reportId: string): Promise<Report> {
return prisma.client.report.delete({ where: { id: reportId } });
}

View File

@ -3,15 +3,29 @@ import prisma from 'lib/prisma';
import { ROLES } from 'lib/constants';
import { uuid } from 'lib/crypto';
export async function getTeam(where: Prisma.TeamWhereInput): Promise<Team> {
export interface GetTeamOptions {
includeTeamUser?: boolean;
}
async function getTeam(where: Prisma.TeamWhereInput, options: GetTeamOptions = {}): Promise<Team> {
const { includeTeamUser = false } = options;
return prisma.client.team.findFirst({
where,
include: {
teamUser: true,
teamUser: includeTeamUser,
},
});
}
export function getTeamById(teamId: string, options: GetTeamOptions = {}) {
return getTeam({ id: teamId }, options);
}
export function getTeamByAccessCode(accessCode: string, options: GetTeamOptions = {}) {
return getTeam({ accessCode }, options);
}
export async function getTeams(where: Prisma.TeamWhereInput): Promise<Team[]> {
return prisma.client.team.findMany({
where,
@ -36,16 +50,15 @@ export async function createTeam(data: Prisma.TeamCreateInput, userId: string):
]);
}
export async function updateTeam(
data: Prisma.TeamUpdateInput,
where: Prisma.TeamWhereUniqueInput,
): Promise<Team> {
export async function updateTeam(teamId: string, data: Prisma.TeamUpdateInput): Promise<Team> {
return prisma.client.team.update({
where: {
id: teamId,
},
data: {
...data,
updatedAt: new Date(),
},
where,
});
}

View File

@ -53,12 +53,14 @@ export async function createTeamUser(
}
export async function updateTeamUser(
teamUserId: string,
data: Prisma.TeamUserUpdateInput,
where: Prisma.TeamUserWhereUniqueInput,
): Promise<TeamUser> {
return prisma.client.teamUser.update({
where: {
id: teamUserId,
},
data,
where,
});
}

View File

@ -22,7 +22,7 @@ export async function getTeamWebsite(
});
}
export async function getTeamWebsiteByTeamMemberId(
export async function findTeamWebsiteByUserId(
websiteId: string,
userId: string,
): Promise<TeamWebsite> {

View File

@ -5,9 +5,14 @@ import { ROLES } from 'lib/constants';
import prisma from 'lib/prisma';
import { Website, User, Role } from 'lib/types';
export async function getUser(
export interface GetUserOptions {
includePassword?: boolean;
showDeleted?: boolean;
}
async function getUser(
where: Prisma.UserWhereInput | Prisma.UserWhereUniqueInput,
options: { includePassword?: boolean; showDeleted?: boolean } = {},
options: GetUserOptions = {},
): Promise<User> {
const { includePassword = false, showDeleted = false } = options;
@ -23,6 +28,14 @@ export async function getUser(
});
}
export async function getUserById(userId: string, options: GetUserOptions = {}) {
return getUser({ id: userId }, options);
}
export async function getUserByUsername(username: string, options: GetUserOptions = {}) {
return getUser({ username }, options);
}
export async function getUsers(): Promise<User[]> {
return prisma.client.user.findMany({
take: 100,

View File

@ -2,12 +2,20 @@ import { Prisma, Website } from '@prisma/client';
import cache from 'lib/cache';
import prisma from 'lib/prisma';
export async function getWebsite(where: Prisma.WebsiteWhereUniqueInput): Promise<Website> {
async function getWebsite(where: Prisma.WebsiteWhereUniqueInput): Promise<Website> {
return prisma.client.website.findUnique({
where,
});
}
export async function getWebsiteById(id: string) {
return getWebsite({ id });
}
export async function getWebsiteByShareId(shareId: string) {
return getWebsite({ shareId });
}
export async function getWebsites(): Promise<Website[]> {
return prisma.client.website.findMany({
orderBy: {

View File

@ -2,7 +2,7 @@ import { Prisma } from '@prisma/client';
import { DATA_TYPE } from 'lib/constants';
import { uuid } from 'lib/crypto';
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
import { flattenJSON } from 'lib/dynamicData';
import { flattenJSON } from 'lib/data';
import kafka from 'lib/kafka';
import prisma from 'lib/prisma';
import { DynamicData } from 'lib/types';

View File

@ -2,24 +2,23 @@ import prisma from 'lib/prisma';
import clickhouse from 'lib/clickhouse';
import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db';
import { WebsiteEventMetric } from 'lib/types';
import { DEFAULT_RESET_DATE, EVENT_TYPE } from 'lib/constants';
import { EVENT_TYPE } from 'lib/constants';
import { loadWebsite } from 'lib/load';
import { maxDate } from 'lib/date';
export interface GetEventMetricsCriteria {
startDate: Date;
endDate: Date;
timezone: string;
unit: string;
filters: {
url: string;
eventName: string;
};
}
export async function getEventMetrics(
...args: [
websiteId: string,
data: {
startDate: Date;
endDate: Date;
timezone: string;
unit: string;
filters: {
url: string;
eventName: string;
};
},
]
...args: [websiteId: string, criteria: GetEventMetricsCriteria]
): Promise<WebsiteEventMetric[]> {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
@ -27,25 +26,8 @@ export async function getEventMetrics(
});
}
async function relationalQuery(
websiteId: string,
{
startDate,
endDate,
timezone = 'utc',
unit = 'day',
filters,
}: {
startDate: Date;
endDate: Date;
timezone: string;
unit: string;
filters: {
url: string;
eventName: string;
};
},
) {
async function relationalQuery(websiteId: string, criteria: GetEventMetricsCriteria) {
const { startDate, endDate, timezone = 'utc', unit = 'day', filters } = criteria;
const { rawQuery, getDateQuery, getFilterQuery } = prisma;
const website = await loadWebsite(websiteId);
const filterQuery = getFilterQuery(filters);
@ -74,25 +56,8 @@ async function relationalQuery(
);
}
async function clickhouseQuery(
websiteId: string,
{
startDate,
endDate,
timezone = 'utc',
unit = 'day',
filters,
}: {
startDate: Date;
endDate: Date;
timezone: string;
unit: string;
filters: {
url: string;
eventName: string;
};
},
) {
async function clickhouseQuery(websiteId: string, criteria: GetEventMetricsCriteria) {
const { startDate, endDate, timezone = 'utc', unit = 'day', filters } = criteria;
const { rawQuery, getDateQuery, getFilterQuery } = clickhouse;
const website = await loadWebsite(websiteId);
const filterQuery = getFilterQuery(filters);

View File

@ -1,8 +1,9 @@
import { Prisma } from '@prisma/client';
import prisma from 'lib/prisma';
export async function getSession(where: Prisma.SessionWhereUniqueInput) {
export async function getSession(id: string) {
return prisma.client.session.findUnique({
where,
where: {
id,
},
});
}

View File

@ -1,6 +1,6 @@
import { DATA_TYPE } from 'lib/constants';
import { uuid } from 'lib/crypto';
import { flattenJSON } from 'lib/dynamicData';
import { flattenJSON } from 'lib/data';
import prisma from 'lib/prisma';
import { DynamicData } from 'lib/types';

View File

@ -1,7 +1,8 @@
export * from './admin/report';
export * from './admin/team';
export * from './admin/teamUser';
export * from './admin/teamWebsite';
export * from './admin/user';
export * from './admin/report';
export * from './admin/website';
export * from './analytics/events/getEventMetrics';
export * from './analytics/events/getEventUsage';