diff --git a/lib/auth.ts b/lib/auth.ts index 2195ad8f..4d11a289 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -15,6 +15,7 @@ import { getTeamWebsite, getTeamWebsiteByTeamMemberId } from 'queries/admin/team import { validate } from 'uuid'; import { Auth } from './types'; import { loadWebsite } from './query'; +import { UserReport } from '@prisma/client'; const log = debug('umami:auth'); @@ -135,7 +136,34 @@ export async function canDeleteWebsite({ user }: Auth, websiteId: string) { return false; } -// To-do: Implement when payments are setup. +export async function canViewUserReport(auth: Auth, userReport: UserReport) { + if (auth.user.isAdmin) { + return true; + } + + if ((auth.user.id = userReport.userId)) { + return true; + } + + if (await canViewWebsite(auth, userReport.websiteId)) { + return true; + } + + return false; +} + +export async function canUpdateUserReport(auth: Auth, userReport: UserReport) { + if (auth.user.isAdmin) { + return true; + } + + if ((auth.user.id = userReport.userId)) { + return true; + } + + return false; +} + export async function canCreateTeam({ user }: Auth) { if (user.isAdmin) { return true; @@ -144,7 +172,6 @@ export async function canCreateTeam({ user }: Auth) { return !!user; } -// To-do: Implement when payments are setup. export async function canViewTeam({ user }: Auth, teamId: string) { if (user.isAdmin) { return true; diff --git a/pages/api/reports/[id].ts b/pages/api/reports/[id].ts new file mode 100644 index 00000000..42002d18 --- /dev/null +++ b/pages/api/reports/[id].ts @@ -0,0 +1,60 @@ +import { canUpdateUserReport, canViewUserReport } from 'lib/auth'; +import { useAuth, useCors } from 'lib/middleware'; +import { NextApiRequestQueryBody } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getUserReportById, updateUserReport } from 'queries'; + +export interface UserReportRequestQuery { + id: string; +} + +export interface UserReportRequestBody { + websiteId: string; + reportName: string; + templateName: string; + parameters: string; +} + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + + if (req.method === 'GET') { + const { id: userReportId } = req.query; + + const data = await getUserReportById(userReportId); + + if (!(await canViewUserReport(req.auth, data))) { + return unauthorized(res); + } + + return ok(res, data); + } + + if (req.method === 'POST') { + const { id: userReportId } = req.query; + + const data = await getUserReportById(userReportId); + + if (!(await canUpdateUserReport(req.auth, data))) { + return unauthorized(res); + } + + const updated = await updateUserReport( + { + ...req.body, + }, + { + id: userReportId, + }, + ); + + return ok(res, updated); + } + + return methodNotAllowed(res); +}; diff --git a/pages/api/reports/index.ts b/pages/api/reports/index.ts new file mode 100644 index 00000000..e5855355 --- /dev/null +++ b/pages/api/reports/index.ts @@ -0,0 +1,43 @@ +import { uuid } from 'lib/crypto'; +import { useAuth, useCors } from 'lib/middleware'; +import { NextApiRequestQueryBody } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok } from 'next-basics'; +import { createUserReport, getUserReports } from 'queries'; + +export interface UserReportRequestBody { + websiteId: string; + reportName: string; + templateName: string; + parameters: string; +} + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + + const { + user: { id: userId }, + } = req.auth; + + if (req.method === 'GET') { + const data = await getUserReports(userId); + + return ok(res, data); + } + + if (req.method === 'POST') { + const data = await createUserReport({ + id: uuid(), + userId, + ...req.body, + }); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/queries/admin/user.ts b/queries/admin/user.ts index a81a76ef..b7f452c7 100644 --- a/queries/admin/user.ts +++ b/queries/admin/user.ts @@ -210,6 +210,20 @@ export async function deleteUser( }, }, }), + client.userReport.deleteMany({ + where: { + OR: [ + { + websiteId: { + in: websiteIds, + }, + }, + { + userId, + }, + ], + }, + }), cloudMode ? client.website.updateMany({ data: { diff --git a/queries/admin/userReport.ts b/queries/admin/userReport.ts new file mode 100644 index 00000000..d31b512e --- /dev/null +++ b/queries/admin/userReport.ts @@ -0,0 +1,37 @@ +import { Prisma, UserReport } from '@prisma/client'; +import prisma from 'lib/prisma'; + +export async function createUserReport( + data: Prisma.UserReportUncheckedCreateInput, +): Promise { + return prisma.client.userReport.create({ data }); +} + +export async function getUserReportById(userReportId: string): Promise { + return prisma.client.userReport.findUnique({ + where: { + id: userReportId, + }, + }); +} + +export async function getUserReports(userId: string): Promise { + return prisma.client.userReport.findMany({ + where: { + userId, + }, + }); +} + +export async function updateUserReport( + data: Prisma.UserReportUpdateInput, + where: Prisma.UserReportWhereUniqueInput, +): Promise { + return prisma.client.userReport.update({ data, where }); +} + +export async function deleteUserReport( + where: Prisma.UserReportWhereUniqueInput, +): Promise { + return prisma.client.userReport.delete({ where }); +} diff --git a/queries/admin/website.ts b/queries/admin/website.ts index f5ce5739..e6d53fce 100644 --- a/queries/admin/website.ts +++ b/queries/admin/website.ts @@ -92,6 +92,11 @@ export async function deleteWebsite( websiteId, }, }), + client.userReport.deleteMany({ + where: { + websiteId, + }, + }), cloudMode ? prisma.client.website.update({ data: { diff --git a/queries/index.js b/queries/index.js index 5c295fff..e565df25 100644 --- a/queries/index.js +++ b/queries/index.js @@ -1,6 +1,7 @@ export * from './admin/team'; export * from './admin/teamUser'; export * from './admin/user'; +export * from './admin/userReport'; export * from './admin/website'; export * from './analytics/event/getEventMetrics'; export * from './analytics/event/getEventUsage';