From 78225691df03db27d7706beb873d8f7c6b786b2d Mon Sep 17 00:00:00 2001 From: Brian Cao Date: Sun, 20 Nov 2022 00:48:13 -0800 Subject: [PATCH] Add permission checks. --- db/postgresql/schema.prisma | 55 ++++++------------- lib/auth.ts | 51 ++++++++++++++---- lib/cache.ts | 14 ++--- lib/constants.ts | 10 ++++ pages/api/teams/[id]/index.ts | 35 +++++------- pages/api/teams/[id]/user.ts | 16 +++++- pages/api/teams/[id]/website.ts | 27 +++------- pages/api/teams/index.ts | 8 +-- pages/api/users/[id]/index.ts | 19 +++---- pages/api/users/index.ts | 17 +++--- pages/api/websites/[id]/index.ts | 20 ++++--- pages/api/websites/index.ts | 26 ++++++--- queries/admin/permission.ts | 21 ++++++++ queries/admin/team.ts | 47 +++++++++-------- queries/admin/teamUser.ts | 4 +- queries/admin/teamWebsite.ts | 45 ---------------- queries/admin/user.ts | 5 +- queries/admin/userRole.ts | 4 +- queries/admin/userWebsite.ts | 43 --------------- queries/admin/website.ts | 91 ++++++-------------------------- 20 files changed, 225 insertions(+), 333 deletions(-) delete mode 100644 queries/admin/teamWebsite.ts delete mode 100644 queries/admin/userWebsite.ts diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma index 538a1fa8..d47b9d55 100644 --- a/db/postgresql/schema.prisma +++ b/db/postgresql/schema.prisma @@ -14,12 +14,12 @@ model User { createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) isDeleted Boolean @default(false) @map("is_deleted") - groupRole GroupRole[] - groupUser GroupUser[] - userRole UserRole[] - teamWebsite TeamWebsite[] - teamUser TeamUser[] - userWebsite UserWebsite[] + groupRole GroupRole[] + groupUser GroupUser[] + userRole UserRole[] + teamUser TeamUser[] + Website Website? @relation(fields: [websiteId], references: [id]) + websiteId String? @db.Uuid @@map("user") } @@ -47,11 +47,13 @@ model Website { domain String? @db.VarChar(500) shareId String? @unique @map("share_id") @db.VarChar(64) revId Int @default(0) @map("rev_id") @db.Integer + userId String? @map("user_id") @db.Uuid + teamId String? @map("team_id") @db.Uuid createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) isDeleted Boolean @default(false) @map("is_deleted") - teamWebsite TeamWebsite[] - userWebsite UserWebsite[] + team Team[] + user User[] @@index([createdAt]) @@index([shareId]) @@ -153,6 +155,7 @@ model RolePermission { role Role @relation(fields: [roleId], references: [id]) permission Permission @relation(fields: [permissionId], references: [id]) + @@unique([roleId, permissionId]) @@map("role_permission") } @@ -168,6 +171,7 @@ model UserRole { user User @relation(fields: [userId], references: [id]) team Team? @relation(fields: [teamId], references: [id]) + @@unique([roleId, userId, teamId]) @@map("user_role") } @@ -177,28 +181,14 @@ model Team { createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) isDeleted Boolean @default(false) @map("is_deleted") - teamWebsites TeamWebsite[] - teamUsers TeamUser[] - UserRole UserRole[] + teamUsers TeamUser[] + UserRole UserRole[] + Website Website? @relation(fields: [websiteId], references: [id]) + websiteId String? @db.Uuid @@map("team") } -model TeamWebsite { - id String @id() @unique() @map("team_website_id") @db.Uuid - teamId String @map("team_id") @db.Uuid - websiteId String @map("website_id") @db.Uuid - createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) - isDeleted Boolean @default(false) @map("is_deleted") - - website Website @relation(fields: [websiteId], references: [id]) - team Team @relation(fields: [teamId], references: [id]) - user User? @relation(fields: [userId], references: [id]) - userId String? @db.Uuid - - @@map("team_website") -} - model TeamUser { id String @id() @unique() @map("team_user_id") @db.Uuid teamId String @map("team_id") @db.Uuid @@ -212,16 +202,3 @@ model TeamUser { @@map("team_user") } - -model UserWebsite { - id String @id() @unique() @map("user_website_id") @db.Uuid - userId String @map("user_id") @db.Uuid - websiteId String @map("website_id") @db.Uuid - createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) - isDeleted Boolean @default(false) @map("is_deleted") - - website Website @relation(fields: [websiteId], references: [id]) - user User @relation(fields: [userId], references: [id]) - - @@map("user_website") -} diff --git a/lib/auth.ts b/lib/auth.ts index 050e1c20..7035df69 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,9 +1,10 @@ import debug from 'debug'; import { NextApiRequestAuth } from 'interface/api/nextApi'; +import cache from 'lib/cache'; import { SHARE_TOKEN_HEADER, UmamiApi } from 'lib/constants'; import { secret } from 'lib/crypto'; import { parseSecureToken, parseToken } from 'next-basics'; -import { getUser, getUserWebsite } from 'queries'; +import { getPermissionsByUserId, getTeamUser, getUser } from 'queries'; const log = debug('umami:auth'); @@ -63,23 +64,51 @@ export async function allowQuery( if (user?.id) { if (type === UmamiApi.AuthType.Website) { - const userWebsite = await getUserWebsite({ - userId: user.id, - websiteId: typeId ?? id, - isDeleted: false, - }); + const website = await cache.fetchWebsite(typeId ?? id); - return userWebsite; + if (website && website.userId === user.id) { + return true; + } + + if (website.teamId) { + const teamUser = getTeamUser({ userId: user.id, teamId: website.teamId, isDeleted: false }); + + return teamUser; + } + + return false; } else if (type === UmamiApi.AuthType.User) { const user = await getUser({ id }); return user && user.id === id; - } - } + } else if (type === UmamiApi.AuthType.Team) { + const teamUser = await getTeamUser({ + userId: user.id, + teamId: typeId ?? id, + isDeleted: false, + }); - if (user?.isAdmin) { - return true; + return teamUser; + } else if (type === UmamiApi.AuthType.TeamOwner) { + const teamUser = await getTeamUser({ + userId: user.id, + teamId: typeId ?? id, + isDeleted: false, + }); + + return teamUser && teamUser.isOwner; + } } return false; } + +export async function checkPermission(req: NextApiRequestAuth, type: UmamiApi.Permission) { + const { + user: { id: userId }, + } = req.auth; + + const userRole = await getPermissionsByUserId(userId, type); + + return userRole.length > 0; +} diff --git a/lib/cache.ts b/lib/cache.ts index 6ffbb5fd..0cf6e2c8 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,6 +1,6 @@ -import { getWebsite, getUser, getSession } from '../queries'; +import { User, Website } from '@prisma/client'; import redis, { DELETED } from 'lib/redis'; -import { Role, Team, TeamUser, User, UserRole, UserWebsite, Website } from '@prisma/client'; +import { getSession, getUser, getWebsite } from '../queries'; async function fetchObject(key, query) { const obj = await redis.get(key); @@ -26,7 +26,7 @@ async function deleteObject(key) { return redis.set(key, DELETED); } -async function fetchWebsite(id) { +async function fetchWebsite(id): Promise { return fetchObject(`website:${id}`, () => getWebsite({ id })); } @@ -41,13 +41,7 @@ async function deleteWebsite(id) { return deleteObject(`website:${id}`); } -async function fetchUser(id): Promise< - User & { - userRole?: (UserRole & { role: Role })[]; - teamUser?: (TeamUser & { team: Team })[]; - userWebsite?: (UserWebsite & { website: Website })[]; - } -> { +async function fetchUser(id): Promise { return fetchObject(`user:${id}`, () => getUser({ id }, true)); } diff --git a/lib/constants.ts b/lib/constants.ts index cf56c39f..dbdf4e38 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -8,6 +8,16 @@ export namespace UmamiApi { export enum AuthType { Website, User, + Team, + TeamOwner, + } + + export enum Permission { + Admin = 'Admin', + } + + export enum Role { + Admin = 'Admin', } } export const CURRENT_VERSION = process.env.currentVersion; diff --git a/pages/api/teams/[id]/index.ts b/pages/api/teams/[id]/index.ts index 14710a54..e93621eb 100644 --- a/pages/api/teams/[id]/index.ts +++ b/pages/api/teams/[id]/index.ts @@ -1,8 +1,10 @@ import { Team } from '@prisma/client'; 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, methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { deleteTeam, getTeam, updateTeam } from 'queries'; export interface TeamRequestQuery { @@ -10,8 +12,7 @@ export interface TeamRequestQuery { } export interface TeamRequestBody { - name?: string; - is_deleted?: boolean; + name: string; } export default async ( @@ -20,43 +21,35 @@ export default async ( ) => { await useAuth(req, res); - const { - user: { id: userId, isAdmin }, - } = req.auth; - const { id } = req.query; + const { id: teamId } = req.query; if (req.method === 'GET') { - if (id !== userId && !isAdmin) { + if (!(await allowQuery(req, UmamiApi.AuthType.Team))) { return unauthorized(res); } - - const user = await getTeam({ id }); + const user = await getTeam({ id: teamId }); return ok(res, user); } if (req.method === 'POST') { - const { name, is_deleted: isDeleted } = req.body; + const { name } = req.body; - if (id !== userId && !isAdmin) { - return unauthorized(res); + if (!(await allowQuery(req, UmamiApi.AuthType.TeamOwner))) { + return unauthorized(res, 'You must be the owner of this team.'); } - const updated = await updateTeam({ name, isDeleted }, { id }); + const updated = await updateTeam({ name }, { id: teamId }); return ok(res, updated); } if (req.method === 'DELETE') { - if (id === userId) { - return badRequest(res, 'You cannot delete your own user.'); + if (!(await allowQuery(req, UmamiApi.AuthType.TeamOwner))) { + return unauthorized(res, 'You must be the owner of this team.'); } - if (!isAdmin) { - return unauthorized(res); - } - - await deleteTeam(id); + await deleteTeam(teamId); return ok(res); } diff --git a/pages/api/teams/[id]/user.ts b/pages/api/teams/[id]/user.ts index 9f1290e1..529f0195 100644 --- a/pages/api/teams/[id]/user.ts +++ b/pages/api/teams/[id]/user.ts @@ -1,8 +1,10 @@ import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; import { uuid } from 'lib/crypto'; import { useAuth } from 'lib/middleware'; import { NextApiResponse } from 'next'; -import { methodNotAllowed, ok } from 'next-basics'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { createTeamUser, deleteTeamUser, getUsersByTeamId } from 'queries'; export interface TeamUserRequestQuery { @@ -23,12 +25,20 @@ export default async ( const { id: teamId } = req.query; if (req.method === 'GET') { + if (!(await allowQuery(req, UmamiApi.AuthType.Team))) { + return unauthorized(res); + } + const user = await getUsersByTeamId({ teamId }); return ok(res, user); } if (req.method === 'POST') { + if (!(await allowQuery(req, UmamiApi.AuthType.TeamOwner))) { + return unauthorized(res, 'You must be the owner of this team.'); + } + const { user_id: userId } = req.body; const updated = await createTeamUser({ id: uuid(), userId, teamId }); @@ -37,6 +47,10 @@ export default async ( } if (req.method === 'DELETE') { + if (!(await allowQuery(req, UmamiApi.AuthType.TeamOwner))) { + return unauthorized(res, 'You must be the owner of this team.'); + } + const { team_user_id } = req.body; await deleteTeamUser(team_user_id); diff --git a/pages/api/teams/[id]/website.ts b/pages/api/teams/[id]/website.ts index 35c2e36e..364fc5da 100644 --- a/pages/api/teams/[id]/website.ts +++ b/pages/api/teams/[id]/website.ts @@ -1,9 +1,10 @@ import { NextApiRequestQueryBody } from 'interface/api/nextApi'; -import { uuid } from 'lib/crypto'; +import { allowQuery } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; import { useAuth } from 'lib/middleware'; import { NextApiResponse } from 'next'; -import { methodNotAllowed, ok } from 'next-basics'; -import { createTeamWebsite, deleteTeamWebsite, getWebsitesByTeamId } from 'queries'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getWebsitesByTeamId } from 'queries'; export interface TeamWebsiteRequestQuery { id: string; @@ -23,26 +24,14 @@ export default async ( const { id: teamId } = req.query; if (req.method === 'GET') { + if (!(await allowQuery(req, UmamiApi.AuthType.Team))) { + return unauthorized(res); + } + const website = await getWebsitesByTeamId({ teamId }); return ok(res, website); } - if (req.method === 'POST') { - const { website_id: websiteId } = req.body; - - const updated = await createTeamWebsite({ id: uuid(), websiteId, teamId }); - - return ok(res, updated); - } - - if (req.method === 'DELETE') { - const { team_website_id } = req.body; - - await deleteTeamWebsite(team_website_id); - - return ok(res); - } - return methodNotAllowed(res); }; diff --git a/pages/api/teams/index.ts b/pages/api/teams/index.ts index 8173c3b2..5e8f6f4f 100644 --- a/pages/api/teams/index.ts +++ b/pages/api/teams/index.ts @@ -21,17 +21,17 @@ export default async ( } = req.auth; if (req.method === 'GET') { - const users = await getTeamsByUserId(id); + const teams = await getTeamsByUserId(id); - return ok(res, users); + return ok(res, teams); } if (req.method === 'POST') { const { name } = req.body; - const user = await getTeam({ name }); + const team = await getTeam({ name }); - if (user) { + if (team) { return badRequest(res, 'Team already exists'); } diff --git a/pages/api/users/[id]/index.ts b/pages/api/users/[id]/index.ts index 80b6f8b2..8eef949b 100644 --- a/pages/api/users/[id]/index.ts +++ b/pages/api/users/[id]/index.ts @@ -1,9 +1,10 @@ -import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { getUser, deleteUser, updateUser } from 'queries'; +import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { checkPermission } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; import { useAuth } from 'lib/middleware'; import { NextApiResponse } from 'next'; -import { NextApiRequestQueryBody } from 'interface/api/nextApi'; -import { User } from 'interface/api/models'; +import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { deleteUser, getUser, updateUser, User } from 'queries'; export interface UserRequestQuery { id: string; @@ -21,12 +22,12 @@ export default async ( await useAuth(req, res); const { - user: { id: userId, isAdmin }, + user: { id: userId }, } = req.auth; const { id } = req.query; if (req.method === 'GET') { - if (id !== userId && !isAdmin) { + if (id !== userId) { return unauthorized(res); } @@ -38,7 +39,7 @@ export default async ( if (req.method === 'POST') { const { username, password } = req.body; - if (id !== userId && !isAdmin) { + if (id !== userId) { return unauthorized(res); } @@ -51,7 +52,7 @@ export default async ( } // Only admin can change these fields - if (isAdmin) { + if (!(await checkPermission(req, UmamiApi.Permission.Admin))) { data.username = username; } @@ -74,7 +75,7 @@ export default async ( return badRequest(res, 'You cannot delete your own user.'); } - if (!isAdmin) { + if (!(await checkPermission(req, UmamiApi.Permission.Admin))) { return unauthorized(res); } diff --git a/pages/api/users/index.ts b/pages/api/users/index.ts index 07007546..5f15c44a 100644 --- a/pages/api/users/index.ts +++ b/pages/api/users/index.ts @@ -1,10 +1,11 @@ -import { ok, unauthorized, methodNotAllowed, badRequest, hashPassword } from 'next-basics'; -import { useAuth } from 'lib/middleware'; -import { uuid } from 'lib/crypto'; -import { createUser, getUser, getUsers } from 'queries'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; +import { checkPermission } from 'lib/auth'; +import { UmamiApi } from 'lib/constants'; +import { uuid } from 'lib/crypto'; +import { useAuth } from 'lib/middleware'; import { NextApiResponse } from 'next'; -import { User } from 'interface/api/models'; +import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { createUser, getUser, getUsers, User } from 'queries'; export interface UsersRequestBody { username: string; @@ -18,11 +19,7 @@ export default async ( ) => { await useAuth(req, res); - const { - user: { isAdmin }, - } = req.auth; - - if (!isAdmin) { + if (!(await checkPermission(req, UmamiApi.Permission.Admin))) { return unauthorized(res); } diff --git a/pages/api/websites/[id]/index.ts b/pages/api/websites/[id]/index.ts index 5cf6b474..f26313c4 100644 --- a/pages/api/websites/[id]/index.ts +++ b/pages/api/websites/[id]/index.ts @@ -4,7 +4,7 @@ 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 { methodNotAllowed, ok, serverError, unauthorized, badRequest } from 'next-basics'; import { deleteWebsite, getWebsite, updateWebsite } from 'queries'; export interface WebsiteRequestQuery { @@ -15,6 +15,8 @@ export interface WebsiteRequestBody { name: string; domain: string; shareId: string; + userId?: string; + teamId?: string; } export default async ( @@ -37,14 +39,14 @@ export default async ( } if (req.method === 'POST') { - const { name, domain, shareId } = req.body; + const { ...data } = req.body; + + if (!data.userId && !data.teamId) { + badRequest(res, 'A website must be assigned to a User or Team.'); + } try { - await updateWebsite(websiteId, { - name, - domain, - shareId, - }); + await updateWebsite(websiteId, data); } catch (e: any) { if (e.message.includes('Unique constraint') && e.message.includes('share_id')) { return serverError(res, 'That share ID is already taken.'); @@ -55,10 +57,6 @@ export default async ( } if (req.method === 'DELETE') { - if (!(await allowQuery(req, UmamiApi.AuthType.Website))) { - return unauthorized(res); - } - await deleteWebsite(websiteId); return ok(res); diff --git a/pages/api/websites/index.ts b/pages/api/websites/index.ts index 4c5ad07c..50469efb 100644 --- a/pages/api/websites/index.ts +++ b/pages/api/websites/index.ts @@ -1,9 +1,10 @@ +import { Prisma } from '@prisma/client'; import { NextApiRequestQueryBody } from 'interface/api/nextApi'; import { uuid } from 'lib/crypto'; import { useAuth, useCors } from 'lib/middleware'; import { NextApiResponse } from 'next'; -import { getRandomChars, methodNotAllowed, ok } from 'next-basics'; -import { createWebsiteByUser, getAllWebsites, getWebsitesByUserId } from 'queries'; +import { methodNotAllowed, ok } from 'next-basics'; +import { createWebsite, getAllWebsites, getWebsitesByUserId } from 'queries'; export interface WebsitesRequestQuery { include_all?: boolean; @@ -12,7 +13,8 @@ export interface WebsitesRequestQuery { export interface WebsitesRequestBody { name: string; domain: string; - enableShareUrl: boolean; + shareId: string; + teamId?: string; } export default async ( @@ -36,10 +38,22 @@ export default async ( } if (req.method === 'POST') { - const { name, domain, enableShareUrl } = req.body; + const { name, domain, shareId, teamId } = req.body; - const shareId = enableShareUrl ? getRandomChars(8) : null; - const website = await createWebsiteByUser(userId, { id: uuid(), name, domain, shareId }); + const data: Prisma.WebsiteCreateInput = { + id: uuid(), + name, + domain, + shareId, + }; + + if (teamId) { + data.teamId = teamId; + } else { + data.userId = userId; + } + + const website = await createWebsite(data); return ok(res, website); } diff --git a/queries/admin/permission.ts b/queries/admin/permission.ts index 37d1647e..9c98065c 100644 --- a/queries/admin/permission.ts +++ b/queries/admin/permission.ts @@ -19,6 +19,27 @@ export async function getPermissions(where: Prisma.PermissionWhereInput): Promis }); } +export async function getPermissionsByUserId(userId, name?: string): Promise { + return prisma.client.permission.findMany({ + where: { + ...(name ? { name } : {}), + RolePermission: { + every: { + role: { + is: { + userRoles: { + every: { + userId, + }, + }, + }, + }, + }, + }, + }, + }); +} + export async function updatePermission( data: Prisma.PermissionUpdateInput, where: Prisma.PermissionWhereUniqueInput, diff --git a/queries/admin/team.ts b/queries/admin/team.ts index 8687fb64..2ea81e0a 100644 --- a/queries/admin/team.ts +++ b/queries/admin/team.ts @@ -1,51 +1,54 @@ -import { Prisma, Team, TeamUser } from '@prisma/client'; +import { Prisma, Team } from '@prisma/client'; import prisma from 'lib/prisma'; -export async function createTeam(data: Prisma.TeamCreateInput): Promise { - return prisma.client.role.create({ - data, +export async function createTeam( + data: Prisma.TeamCreateInput, + searchDeleted = false, +): Promise { + return prisma.client.team.create({ + data: { ...data, isDeleted: searchDeleted ? null : false }, }); } -export async function getTeam(where: Prisma.TeamWhereUniqueInput): Promise { - return prisma.client.role.findUnique({ +export async function getTeam(where: Prisma.TeamWhereInput): Promise { + return prisma.client.team.findFirst({ where, }); } export async function getTeams(where: Prisma.TeamWhereInput): Promise { - return prisma.client.role.findMany({ + return prisma.client.team.findMany({ where, }); } -export async function getTeamsByUserId(userId: string): Promise< - (TeamUser & { - team: Team; - })[] -> { - return prisma.client.teamUser.findMany({ - where: { - userId, - }, - include: { - team: true, - }, - }); +export async function getTeamsByUserId(userId: string): Promise { + return prisma.client.teamUser + .findMany({ + where: { + userId, + }, + include: { + team: true, + }, + }) + .then(data => { + return data.map(a => a.team); + }); } export async function updateTeam( data: Prisma.TeamUpdateInput, where: Prisma.TeamWhereUniqueInput, ): Promise { - return prisma.client.role.update({ + return prisma.client.team.update({ data, where, }); } export async function deleteTeam(teamId: string): Promise { - return prisma.client.role.update({ + return prisma.client.team.update({ data: { isDeleted: true, }, diff --git a/queries/admin/teamUser.ts b/queries/admin/teamUser.ts index b2f7bbf2..3bbe9a76 100644 --- a/queries/admin/teamUser.ts +++ b/queries/admin/teamUser.ts @@ -9,8 +9,8 @@ export async function createTeamUser( }); } -export async function getTeamUser(where: Prisma.TeamUserWhereUniqueInput): Promise { - return prisma.client.teamUser.findUnique({ +export async function getTeamUser(where: Prisma.TeamUserWhereInput): Promise { + return prisma.client.teamUser.findFirst({ where, }); } diff --git a/queries/admin/teamWebsite.ts b/queries/admin/teamWebsite.ts deleted file mode 100644 index 6b485da0..00000000 --- a/queries/admin/teamWebsite.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Prisma, TeamWebsite } from '@prisma/client'; -import prisma from 'lib/prisma'; - -export async function createTeamWebsite( - data: Prisma.TeamWebsiteCreateInput | Prisma.TeamWebsiteUncheckedCreateInput, -): Promise { - return prisma.client.teamWebsite.create({ - data, - }); -} - -export async function getTeamWebsite( - where: Prisma.TeamWebsiteWhereUniqueInput, -): Promise { - return prisma.client.teamWebsite.findUnique({ - where, - }); -} - -export async function getTeamWebsites(where: Prisma.TeamWebsiteWhereInput): Promise { - return prisma.client.teamWebsite.findMany({ - where, - }); -} - -export async function updateTeamWebsite( - data: Prisma.TeamWebsiteUpdateInput, - where: Prisma.TeamWebsiteWhereUniqueInput, -): Promise { - return prisma.client.teamWebsite.update({ - data, - where, - }); -} - -export async function deleteTeamWebsite(teamWebsiteId: string): Promise { - return prisma.client.teamWebsite.update({ - data: { - isDeleted: true, - }, - where: { - id: teamWebsiteId, - }, - }); -} diff --git a/queries/admin/user.ts b/queries/admin/user.ts index 75a98194..7122323b 100644 --- a/queries/admin/user.ts +++ b/queries/admin/user.ts @@ -1,5 +1,4 @@ import { Prisma } from '@prisma/client'; -import { UmamiApi } from 'lib/constants'; import cache from 'lib/cache'; import prisma from 'lib/prisma'; @@ -99,14 +98,14 @@ export async function deleteUser( ): Promise<[Prisma.BatchPayload, Prisma.BatchPayload, Prisma.BatchPayload, User]> { const { client } = prisma; - const websites = await client.userWebsite.findMany({ + const websites = await client.website.findMany({ where: { userId }, }); let websiteIds = []; if (websites.length > 0) { - websiteIds = websites.map(a => a.websiteId); + websiteIds = websites.map(a => a.id); } return client diff --git a/queries/admin/userRole.ts b/queries/admin/userRole.ts index 22893412..ee2a1a2f 100644 --- a/queries/admin/userRole.ts +++ b/queries/admin/userRole.ts @@ -9,8 +9,8 @@ export async function createUserRole( }); } -export async function getUserRole(where: Prisma.UserRoleWhereUniqueInput): Promise { - return prisma.client.userRole.findUnique({ +export async function getUserRole(where: Prisma.UserRoleWhereInput): Promise { + return prisma.client.userRole.findFirst({ where, }); } diff --git a/queries/admin/userWebsite.ts b/queries/admin/userWebsite.ts deleted file mode 100644 index 76f924ec..00000000 --- a/queries/admin/userWebsite.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Prisma, UserWebsite } from '@prisma/client'; -import prisma from 'lib/prisma'; - -export async function createUserWebsite( - data: Prisma.UserWebsiteCreateInput | Prisma.UserWebsiteUncheckedCreateInput, -): Promise { - return prisma.client.userWebsite.create({ - data, - }); -} - -export async function getUserWebsite(where: Prisma.UserWebsiteWhereInput): Promise { - return prisma.client.userWebsite.findFirst({ - where, - }); -} - -export async function getUserWebsites(where: Prisma.UserWebsiteWhereInput): Promise { - return prisma.client.userWebsite.findMany({ - where, - }); -} - -export async function updateUserWebsite( - data: Prisma.UserWebsiteUpdateInput, - where: Prisma.UserWebsiteWhereUniqueInput, -): Promise { - return prisma.client.userWebsite.update({ - data, - where, - }); -} - -export async function deleteUserWebsite(userWebsiteId: string): Promise { - return prisma.client.userWebsite.update({ - data: { - isDeleted: true, - }, - where: { - id: userWebsiteId, - }, - }); -} diff --git a/queries/admin/website.ts b/queries/admin/website.ts index c88fb2db..7ee64a35 100644 --- a/queries/admin/website.ts +++ b/queries/admin/website.ts @@ -3,54 +3,12 @@ import cache from 'lib/cache'; import prisma from 'lib/prisma'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; -export async function createWebsiteByUser( - userId: string, - data: { - id: string; - name: string; - domain: string; - shareId?: string; - }, +export async function createWebsite( + data: Prisma.WebsiteCreateInput | Prisma.WebsiteUncheckedCreateInput, ): Promise { return prisma.client.website .create({ - data: { - userWebsite: { - connect: { - id: userId, - }, - }, - ...data, - }, - }) - .then(async data => { - if (cache.enabled) { - await cache.storeWebsite(data); - } - - return data; - }); -} - -export async function createWebsiteByTeam( - teamId: string, - data: { - id: string; - name: string; - domain: string; - shareId?: string; - }, -): Promise { - return prisma.client.website - .create({ - data: { - teamWebsite: { - connect: { - id: teamId, - }, - }, - ...data, - }, + data, }) .then(async data => { if (cache.enabled) { @@ -103,11 +61,7 @@ export async function getWebsite(where: Prisma.WebsiteWhereUniqueInput): Promise export async function getWebsitesByUserId(userId): Promise { return prisma.client.website.findMany({ where: { - userWebsite: { - every: { - userId, - }, - }, + userId, }, orderBy: { name: 'asc', @@ -118,11 +72,7 @@ export async function getWebsitesByUserId(userId): Promise { export async function getWebsitesByTeamId(teamId): Promise { return prisma.client.website.findMany({ where: { - teamWebsite: { - every: { - teamId, - }, - }, + teamId, }, orderBy: { name: 'asc', @@ -130,35 +80,26 @@ export async function getWebsitesByTeamId(teamId): Promise { }); } -export async function getAllWebsites(): Promise<(Website & { user: string })[]> { - return await prisma.client.website - .findMany({ - orderBy: [ - { - name: 'asc', - }, - ], - include: { - userWebsite: { - include: { - user: true, - }, - }, +export async function getAllWebsites(): Promise { + return await prisma.client.website.findMany({ + orderBy: [ + { + name: 'asc', }, - }) - .then(data => data.map(i => ({ ...i, user: i.userWebsite[0]?.userId }))); + ], + }); } -export async function deleteWebsite( - websiteId: string, -) { +export async function deleteWebsite(websiteId: string) { return runQuery({ [PRISMA]: () => deleteWebsiteRelationalQuery(websiteId), [CLICKHOUSE]: () => deleteWebsiteClickhouseQuery(websiteId), }); } -async function deleteWebsiteRelationalQuery(websiteId): Promise<[Prisma.BatchPayload, Prisma.BatchPayload, Website]> { +async function deleteWebsiteRelationalQuery( + websiteId, +): Promise<[Prisma.BatchPayload, Prisma.BatchPayload, Website]> { const { client, transaction } = prisma; return transaction([