From f85393f8dfabec5ffb8cb053f66e5e19f6139619 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 25 Jan 2024 23:20:53 -0800 Subject: [PATCH] Teams refactor: removed team websites. --- package.json | 2 +- src/app/(main)/settings/teams/TeamsTable.tsx | 6 + .../settings/teams/[id]/TeamMembers.tsx | 10 +- .../settings/teams/[id]/TeamSettings.tsx | 1 - .../teams/[id]/TeamWebsiteAddForm.tsx | 79 ----------- .../settings/teams/[id]/TeamWebsites.tsx | 6 +- src/app/(main)/settings/teams/page.tsx | 4 +- .../(main)/settings/users/UsersDataTable.tsx | 10 +- src/app/(main)/settings/users/UsersTable.tsx | 46 ++++--- src/app/(main)/settings/users/page.tsx | 10 +- src/components/messages.ts | 1 + src/index.ts | 1 - src/lib/auth.ts | 18 +-- src/lib/constants.ts | 1 + src/pages/api/auth/login.ts | 7 +- src/pages/api/teams/[id]/websites/index.ts | 13 -- src/queries/admin/team.ts | 43 ++++-- src/queries/admin/teamUser.ts | 8 -- src/queries/admin/teamWebsite.ts | 127 ------------------ src/queries/admin/user.ts | 81 +++++------ src/queries/admin/website.ts | 2 +- src/queries/index.js | 1 - yarn.lock | 64 ++++++++- 23 files changed, 190 insertions(+), 351 deletions(-) delete mode 100644 src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx delete mode 100644 src/queries/admin/teamWebsite.ts diff --git a/package.json b/package.json index 6001ee9c..367627e6 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.12.2", - "@umami/prisma-client": "^0.8.0", + "@umami/prisma-client": "^0.13.0", "@umami/redis-client": "^0.18.0", "chalk": "^4.1.1", "chart.js": "^4.2.1", diff --git a/src/app/(main)/settings/teams/TeamsTable.tsx b/src/app/(main)/settings/teams/TeamsTable.tsx index 10866b4f..852b4ff4 100644 --- a/src/app/(main)/settings/teams/TeamsTable.tsx +++ b/src/app/(main)/settings/teams/TeamsTable.tsx @@ -17,6 +17,12 @@ export function TeamsTable({ data = [] }: { data: any[] }) { {row => row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username} + + {row => row._count.website} + + + {row => row._count.teamUser} + {row => { const { id, name, teamUser } = row; diff --git a/src/app/(main)/settings/teams/[id]/TeamMembers.tsx b/src/app/(main)/settings/teams/[id]/TeamMembers.tsx index 588a5a52..a84fad10 100644 --- a/src/app/(main)/settings/teams/[id]/TeamMembers.tsx +++ b/src/app/(main)/settings/teams/[id]/TeamMembers.tsx @@ -1,8 +1,8 @@ import useApi from 'components/hooks/useApi'; -import TeamMembersTable from './TeamMembersTable'; import useFilterQuery from 'components/hooks/useFilterQuery'; import DataTable from 'components/common/DataTable'; import useCache from 'store/cache'; +import TeamMembersTable from './TeamMembersTable'; export function TeamMembers({ teamId, readOnly }: { teamId: string; readOnly: boolean }) { const { get } = useApi(); @@ -18,11 +18,9 @@ export function TeamMembers({ teamId, readOnly }: { teamId: string; readOnly: bo }); return ( - <> - - {({ data }) => } - - + + {({ data }) => } + ); } diff --git a/src/app/(main)/settings/teams/[id]/TeamSettings.tsx b/src/app/(main)/settings/teams/[id]/TeamSettings.tsx index 1f92b095..c6285774 100644 --- a/src/app/(main)/settings/teams/[id]/TeamSettings.tsx +++ b/src/app/(main)/settings/teams/[id]/TeamSettings.tsx @@ -25,7 +25,6 @@ export function TeamSettings({ teamId }: { teamId: string }) { return get(`/teams/${teamId}`); } }, - gcTime: 0, }); const canEdit = data?.teamUser?.find( ({ userId, role }) => role === ROLES.teamOwner && userId === user.id, diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx deleted file mode 100644 index ee169886..00000000 --- a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import useApi from 'components/hooks/useApi'; -import { useState } from 'react'; -import { Button, Form, FormButtons, GridColumn, Loading, SubmitButton, Toggle } from 'react-basics'; -import useMessages from 'components/hooks/useMessages'; -import WebsitesDataTable from 'app/(main)/settings/websites/WebsitesDataTable'; -import Empty from 'components/common/Empty'; -import { setValue } from 'store/cache'; -import { useUser } from 'components/hooks'; - -export function TeamWebsiteAddForm({ - teamId, - onSave, - onClose, -}: { - teamId: string; - onSave: () => void; - onClose: () => void; -}) { - const { user } = useUser(); - const { formatMessage, labels } = useMessages(); - const { get, post, useQuery, useMutation } = useApi(); - const { mutate, error } = useMutation({ - mutationFn: (data: any) => post(`/teams/${teamId}/websites`, data), - }); - const { data: websites, isLoading } = useQuery({ - queryKey: ['websites'], - queryFn: () => get('/websites'), - }); - const [selected, setSelected] = useState([]); - const hasData = websites && websites.data.length > 0; - - const handleSubmit = () => { - mutate( - { websiteIds: selected }, - { - onSuccess: async () => { - setValue('team:websites', Date.now()); - onSave?.(); - onClose?.(); - }, - }, - ); - }; - - const handleSelect = id => { - setSelected(state => (state.includes(id) ? state.filter(n => n !== id) : state.concat(id))); - }; - - return ( - <> - {isLoading && !hasData && } - {!isLoading && !hasData && } - {hasData && ( -
- - - {row => ( - - )} - - - - - {formatMessage(labels.addWebsite)} - - - -
- )} - - ); -} - -export default TeamWebsiteAddForm; diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsites.tsx b/src/app/(main)/settings/teams/[id]/TeamWebsites.tsx index 93b0a662..d71ebd99 100644 --- a/src/app/(main)/settings/teams/[id]/TeamWebsites.tsx +++ b/src/app/(main)/settings/teams/[id]/TeamWebsites.tsx @@ -1,13 +1,13 @@ -import useFilterQuery from 'components/hooks/useFilterQuery'; +import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable'; import DataTable from 'components/common/DataTable'; +import useFilterQuery from 'components/hooks/useFilterQuery'; import useApi from 'components/hooks/useApi'; import useUser from 'components/hooks/useUser'; import useCache from 'store/cache'; -import WebsitesTable from '../../websites/WebsitesTable'; export function TeamWebsites({ teamId }: { teamId: string; readOnly: boolean }) { - const { user } = useUser(); const { get } = useApi(); + const { user } = useUser(); const modified = useCache(state => state?.['team:websites']); const queryResult = useFilterQuery({ queryKey: ['team:websites', { teamId, modified }], diff --git a/src/app/(main)/settings/teams/page.tsx b/src/app/(main)/settings/teams/page.tsx index 0cdb6f7d..08e96595 100644 --- a/src/app/(main)/settings/teams/page.tsx +++ b/src/app/(main)/settings/teams/page.tsx @@ -1,6 +1,6 @@ +import { Metadata } from 'next'; import TeamsDataTable from './TeamsDataTable'; import TeamsHeader from './TeamsHeader'; -import { Metadata } from 'next'; export default function () { if (process.env.cloudMode) { @@ -16,5 +16,5 @@ export default function () { } export const metadata: Metadata = { - title: 'Teams Settings | umami', + title: 'Teams Settings - Umami', }; diff --git a/src/app/(main)/settings/users/UsersDataTable.tsx b/src/app/(main)/settings/users/UsersDataTable.tsx index 2495d023..77243f60 100644 --- a/src/app/(main)/settings/users/UsersDataTable.tsx +++ b/src/app/(main)/settings/users/UsersDataTable.tsx @@ -3,10 +3,9 @@ import useApi from 'components/hooks/useApi'; import useFilterQuery from 'components/hooks/useFilterQuery'; import DataTable from 'components/common/DataTable'; import UsersTable from './UsersTable'; -import UsersHeader from './UsersHeader'; import useCache from 'store/cache'; -export function UsersDataTable() { +export function UsersDataTable({ showActions }: { showActions: boolean }) { const { get } = useApi(); const modified = useCache((state: any) => state?.users); const queryResult = useFilterQuery({ @@ -15,10 +14,9 @@ export function UsersDataTable() { }); return ( - <> - - {({ data }) => } - + + {({ data }) => } + ); } diff --git a/src/app/(main)/settings/users/UsersTable.tsx b/src/app/(main)/settings/users/UsersTable.tsx index 4eb7fbb6..13f3b6f8 100644 --- a/src/app/(main)/settings/users/UsersTable.tsx +++ b/src/app/(main)/settings/users/UsersTable.tsx @@ -6,7 +6,13 @@ import useMessages from 'components/hooks/useMessages'; import useLocale from 'components/hooks/useLocale'; import UserDeleteButton from './UserDeleteButton'; -export function UsersTable({ data = [] }: { data: any[] }) { +export function UsersTable({ + data = [], + showActions = true, +}: { + data: any[]; + showActions?: boolean; +}) { const { formatMessage, labels } = useMessages(); const { dateLocale } = useLocale(); const breakpoint = useBreakpoint(); @@ -29,24 +35,26 @@ export function UsersTable({ data = [] }: { data: any[] }) { }) }
- - {row => { - const { id, username } = row; - return ( - <> - - - - - - ); - }} - + {showActions && ( + + {row => { + const { id, username } = row; + return ( + <> + + + + + + ); + }} + + )} ); } diff --git a/src/app/(main)/settings/users/page.tsx b/src/app/(main)/settings/users/page.tsx index 00ebe98c..06bb5f1e 100644 --- a/src/app/(main)/settings/users/page.tsx +++ b/src/app/(main)/settings/users/page.tsx @@ -1,8 +1,14 @@ -import UsersDataTable from './UsersDataTable'; import { Metadata } from 'next'; +import UsersDataTable from './UsersDataTable'; +import UsersHeader from './UsersHeader'; export default function () { - return ; + return ( + <> + + + + ); } export const metadata: Metadata = { title: 'Users | umami', diff --git a/src/components/messages.ts b/src/components/messages.ts index 66ffea9c..00942b87 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -52,6 +52,7 @@ export const labels = defineMessages({ deleteWebsite: { id: 'label.delete-website', defaultMessage: 'Delete website' }, reset: { id: 'label.reset', defaultMessage: 'Reset' }, addWebsite: { id: 'label.add-website', defaultMessage: 'Add website' }, + addMember: { id: 'label.add-member', defaultMessage: 'Add member' }, addDescription: { id: 'label.add-description', defaultMessage: 'Add description' }, changePassword: { id: 'label.change-password', defaultMessage: 'Change password' }, currentPassword: { id: 'label.current-password', defaultMessage: 'Current password' }, diff --git a/src/index.ts b/src/index.ts index 7b192054..cbcef988 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,6 @@ export * from 'components/hooks/useTimezone'; export * from 'components/hooks/useUser'; export * from 'components/hooks/useWebsite'; -export * from 'app/(main)/settings/teams/[id]/TeamWebsiteAddForm'; export * from 'app/(main)/settings/teams/[id]/TeamEditForm'; export * from 'app/(main)/settings/teams/[id]/TeamMemberRemoveButton'; export * from 'app/(main)/settings/teams/[id]/TeamMembers'; diff --git a/src/lib/auth.ts b/src/lib/auth.ts index cb3d3cc7..6ec76abe 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,10 +1,10 @@ import { Report } from '@prisma/client'; import debug from 'debug'; import redis from '@umami/redis-client'; -import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants'; +import { PERMISSIONS, ROLE_PERMISSIONS, ROLES, SHARE_TOKEN_HEADER } from 'lib/constants'; import { secret } from 'lib/crypto'; import { createSecureToken, ensureArray, getRandomChars, parseToken } from 'next-basics'; -import { findTeamWebsiteByUserId, getTeamUser, getTeamWebsite } from 'queries'; +import { getTeamUser, getWebsiteById } from 'queries'; import { loadWebsite } from './load'; import { Auth } from './types'; import { NextApiRequest } from 'next'; @@ -55,8 +55,6 @@ export async function canViewWebsite({ user, shareToken }: Auth, websiteId: stri if (user.id === website?.userId) { return true; } - - return !!(await findTeamWebsiteByUserId(websiteId, user.id)); } export async function canViewAllWebsites({ user }: Auth) { @@ -178,16 +176,12 @@ export async function canDeleteTeamWebsite({ user }: Auth, teamId: string, websi return true; } - const teamWebsite = await getTeamWebsite(teamId, websiteId); + const teamWebsite = await getWebsiteById(websiteId); - if (teamWebsite?.website?.userId === user.id) { - return true; - } + if (teamWebsite && teamWebsite.teamId === teamId) { + const teamUser = await getTeamUser(teamId, user.id); - if (teamWebsite) { - const teamUser = await getTeamUser(teamWebsite.teamId, user.id); - - return hasPermission(teamUser.role, PERMISSIONS.teamUpdate); + return teamUser.role === ROLES.teamOwner || teamUser.role === ROLES.teamMember; } return false; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 0c894634..7f7a8ea9 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -125,6 +125,7 @@ export const ROLES = { viewOnly: 'view-only', teamOwner: 'team-owner', teamMember: 'team-member', + teamGuest: 'team-guest', } as const; export const PERMISSIONS = { diff --git a/src/pages/api/auth/login.ts b/src/pages/api/auth/login.ts index 00cb09d1..23bdb15f 100644 --- a/src/pages/api/auth/login.ts +++ b/src/pages/api/auth/login.ts @@ -68,7 +68,12 @@ export default async ( }); } - log(`Login from ip ${getIpAddress(req)} with username "${username.replace(/["\r\n]/g, '')}" failed.`); + log( + `Login from ip ${getIpAddress(req)} with username "${username.replace( + /["\r\n]/g, + '', + )}" failed.`, + ); return unauthorized(res, 'message.incorrect-username-password'); } diff --git a/src/pages/api/teams/[id]/websites/index.ts b/src/pages/api/teams/[id]/websites/index.ts index 4e754d20..4fe11fb7 100644 --- a/src/pages/api/teams/[id]/websites/index.ts +++ b/src/pages/api/teams/[id]/websites/index.ts @@ -6,7 +6,6 @@ import { pageInfo } from 'lib/schema'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { getWebsitesByTeamId } from 'queries'; -import { createTeamWebsites } from 'queries/admin/teamWebsite'; export interface TeamWebsiteRequestQuery extends SearchFilter { id: string; @@ -52,17 +51,5 @@ export default async ( return ok(res, websites); } - if (req.method === 'POST') { - if (!(await canViewTeam(req.auth, teamId))) { - return unauthorized(res); - } - - const { websiteIds } = req.body; - - const websites = await createTeamWebsites(teamId, websiteIds); - - return ok(res, websites); - } - return methodNotAllowed(res); }; diff --git a/src/queries/admin/team.ts b/src/queries/admin/team.ts index 7bfbfd21..4af1dc97 100644 --- a/src/queries/admin/team.ts +++ b/src/queries/admin/team.ts @@ -10,8 +10,9 @@ export interface GetTeamOptions { async function getTeam(where: Prisma.TeamWhereInput, options: GetTeamOptions = {}): Promise { const { includeTeamUser = false } = options; + const { client } = prisma; - return prisma.client.team.findFirst({ + return client.team.findFirst({ where, include: { teamUser: includeTeamUser, @@ -27,14 +28,15 @@ export function getTeamByAccessCode(accessCode: string, options: GetTeamOptions return getTeam({ accessCode }, options); } -export async function createTeam(data: Prisma.TeamCreateInput, userId: string): Promise { +export async function createTeam(data: Prisma.TeamCreateInput, userId: string): Promise { const { id } = data; + const { client, transaction } = prisma; - return prisma.transaction([ - prisma.client.team.create({ + return transaction([ + client.team.create({ data, }), - prisma.client.teamUser.create({ + client.teamUser.create({ data: { id: uuid(), teamId: id, @@ -46,7 +48,9 @@ export async function createTeam(data: Prisma.TeamCreateInput, userId: string): } export async function updateTeam(teamId: string, data: Prisma.TeamUpdateInput): Promise { - return prisma.client.team.update({ + const { client } = prisma; + + return client.team.update({ where: { id: teamId, }, @@ -61,13 +65,22 @@ export async function deleteTeam( teamId: string, ): Promise> { const { client, transaction } = prisma; + const cloudMode = process.env.CLOUD_MODE; + + if (cloudMode) { + return transaction([ + client.team.update({ + data: { + deletedAt: new Date(), + }, + where: { + id: teamId, + }, + }), + ]); + } return transaction([ - client.teamWebsite.deleteMany({ - where: { - teamId, - }, - }), client.teamUser.deleteMany({ where: { teamId, @@ -87,6 +100,7 @@ export async function getTeams( ): Promise> { const { userId, query } = filters; const mode = prisma.getQueryMode(); + const { client } = prisma; const where: Prisma.TeamWhereInput = { ...(userId && { @@ -123,7 +137,7 @@ export async function getTeams( ...filters, }); - const teams = await prisma.client.team.findMany({ + const teams = await client.team.findMany({ where: { ...where, }, @@ -131,7 +145,7 @@ export async function getTeams( ...(options?.include && { include: options?.include }), }); - const count = await prisma.client.team.count({ where }); + const count = await client.team.count({ where }); return { data: teams, count, ...getParameters }; } @@ -154,6 +168,9 @@ export async function getTeamsByUserId( }, }, }, + _count: { + select: { website: true, teamUser: true }, + }, }, }, ); diff --git a/src/queries/admin/teamUser.ts b/src/queries/admin/teamUser.ts index c5b27a02..de29ce21 100644 --- a/src/queries/admin/teamUser.ts +++ b/src/queries/admin/teamUser.ts @@ -68,14 +68,6 @@ export async function deleteTeamUser(teamId: string, userId: string): Promise { - return prisma.client.teamWebsite.findFirst({ - where: { - teamId, - websiteId, - }, - include: { - website: true, - }, - }); -} - -export async function findTeamWebsiteByUserId( - websiteId: string, - userId: string, -): Promise { - return prisma.client.teamWebsite.findFirst({ - where: { - websiteId, - team: { - teamUser: { - some: { - userId, - }, - }, - }, - }, - }); -} - -export async function getTeamWebsites(teamId: string): Promise< - (TeamWebsite & { - team: Team & { teamUser: TeamUser[] }; - website: Website & { - user: { id: string; username: string }; - }; - })[] -> { - return prisma.client.teamWebsite.findMany({ - where: { - teamId, - }, - include: { - team: { - include: { - teamUser: { - where: { - role: ROLES.teamOwner, - }, - }, - }, - }, - website: { - include: { - user: { - select: { - id: true, - username: true, - }, - }, - }, - }, - }, - orderBy: [ - { - team: { - name: 'asc', - }, - }, - ], - }); -} - -export async function createTeamWebsite(teamId: string, websiteId: string): Promise { - return prisma.client.teamWebsite.create({ - data: { - id: uuid(), - teamId, - websiteId, - }, - }); -} - -export async function createTeamWebsites(teamId: string, websiteIds: string[]) { - const currentTeamWebsites = await getTeamWebsites(teamId); - - // filter out websites that already exists on the team - const addWebsites = websiteIds.filter( - websiteId => !currentTeamWebsites.some(a => a.websiteId === websiteId), - ); - - const teamWebsites: Prisma.TeamWebsiteCreateManyInput[] = addWebsites.map(a => { - return { - id: uuid(), - teamId, - websiteId: a, - }; - }); - - return prisma.client.teamWebsite.createMany({ - data: teamWebsites, - }); -} - -export async function deleteTeamWebsite( - teamId: string, - websiteId: string, -): Promise { - return prisma.client.teamWebsite.deleteMany({ - where: { - teamId, - websiteId, - }, - }); -} diff --git a/src/queries/admin/user.ts b/src/queries/admin/user.ts index 338b9192..29d94ec8 100644 --- a/src/queries/admin/user.ts +++ b/src/queries/admin/user.ts @@ -105,6 +105,7 @@ export async function getUsersByTeamId(teamId: string, filter?: UserSearchFilter include: { teamUser: { select: { + teamId: true, role: true, }, }, @@ -188,7 +189,27 @@ export async function deleteUser( const teamIds = teams.map(a => a.id); - return prisma + if (cloudMode) { + return client.transaction([ + client.website.updateMany({ + data: { + deletedAt: new Date(), + }, + where: { id: { in: websiteIds } }, + }), + client.user.update({ + data: { + username: getRandomChars(32), + deletedAt: new Date(), + }, + where: { + id: userId, + }, + }), + ]); + } + + return client .transaction([ client.eventData.deleteMany({ where: { websiteId: { in: websiteIds } }, @@ -199,29 +220,6 @@ export async function deleteUser( client.session.deleteMany({ where: { websiteId: { in: websiteIds } }, }), - client.teamWebsite.deleteMany({ - where: { - OR: [ - { - websiteId: { - in: websiteIds, - }, - }, - { - teamId: { - in: teamIds, - }, - }, - ], - }, - }), - client.teamWebsite.deleteMany({ - where: { - teamId: { - in: teamIds, - }, - }, - }), client.teamUser.deleteMany({ where: { OR: [ @@ -257,33 +255,16 @@ export async function deleteUser( ], }, }), - cloudMode - ? client.website.updateMany({ - data: { - deletedAt: new Date(), - }, - where: { id: { in: websiteIds } }, - }) - : client.website.deleteMany({ - where: { id: { in: websiteIds } }, - }), - cloudMode - ? client.user.update({ - data: { - username: getRandomChars(32), - deletedAt: new Date(), - }, - where: { - id: userId, - }, - }) - : client.user.delete({ - where: { - id: userId, - }, - }), + client.website.deleteMany({ + where: { id: { in: websiteIds } }, + }), + client.user.delete({ + where: { + id: userId, + }, + }), ]) - .then(async data => { + .then(async (data: any) => { if (cache.enabled) { const ids = websites.map(a => a.id); diff --git a/src/queries/admin/website.ts b/src/queries/admin/website.ts index ed166371..7789a6ec 100644 --- a/src/queries/admin/website.ts +++ b/src/queries/admin/website.ts @@ -260,7 +260,7 @@ export async function deleteWebsite( }, }), cloudMode - ? prisma.client.website.update({ + ? client.website.update({ data: { deletedAt: new Date(), }, diff --git a/src/queries/index.js b/src/queries/index.js index 452d85a6..afec8e4e 100644 --- a/src/queries/index.js +++ b/src/queries/index.js @@ -1,7 +1,6 @@ export * from './admin/report'; export * from './admin/team'; export * from './admin/teamUser'; -export * from './admin/teamWebsite'; export * from './admin/user'; export * from './admin/website'; export * from './analytics/events/getEventMetrics'; diff --git a/yarn.lock b/yarn.lock index 193806f3..a49c8003 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2597,13 +2597,15 @@ "@typescript-eslint/types" "6.14.0" eslint-visitor-keys "^3.4.1" -"@umami/prisma-client@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@umami/prisma-client/-/prisma-client-0.8.0.tgz#9f866c813b15b7ab0e7632506316bf1e5d2e74cc" - integrity sha512-ix3/75CO3eVlf1Rg0cUIjoHDFJV7nxx5sSh1NnvbjyGn8EsTpZ7fVYF874w8+ENQsaKFIMftUSGGiwvgxaZN3g== +"@umami/prisma-client@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@umami/prisma-client/-/prisma-client-0.13.0.tgz#00ed90adf36e8e8cb7301ab557ad617dd946188f" + integrity sha512-HkhwTH7cxkbqlxw6LK8QGbEaIl0aYZeVf5bUwncv68C/RaQrQ0/g9x6TzHo6uKlSgk2LtavnGwxYmjxD1fdGQw== dependencies: + "@prisma/extension-read-replicas" "^0.3.0" chalk "^4.1.2" debug "^4.3.4" + sql-formatter "^15.1.2" "@umami/redis-client@^0.18.0": version "0.18.0" @@ -3377,7 +3379,7 @@ commander@11.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== -commander@2, commander@^2.20.0, commander@^2.20.3: +commander@2, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3933,6 +3935,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -4793,6 +4800,11 @@ get-port-please@^3.1.1: resolved "https://registry.yarnpkg.com/get-port-please/-/get-port-please-3.1.1.tgz#2556623cddb4801d823c0a6a15eec038abb483be" integrity sha512-3UBAyM3u4ZBVYDsxOQfJDxEa6XTbpBDrOjp4mf7ExFRt5BKs/QywQQiJsh2B+hxcZLSapWqCRvElUe8DnKcFHA== +get-stdin@=8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -6308,6 +6320,11 @@ moment-timezone@^0.5.35: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moo@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + mri@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -6353,6 +6370,16 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +nearley@^2.20.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" + integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== + dependencies: + commander "^2.19.0" + moo "^0.5.0" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + next-basics@^0.39.0: version "0.39.0" resolved "https://registry.yarnpkg.com/next-basics/-/next-basics-0.39.0.tgz#1ec448a1c12966a82067445bfb9319b7e883dd6a" @@ -7507,6 +7534,19 @@ raf-schd@^4.0.2: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -7883,6 +7923,11 @@ restore-cursor@^4.0.0: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -8280,6 +8325,15 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sql-formatter@^15.1.2: + version "15.1.2" + resolved "https://registry.yarnpkg.com/sql-formatter/-/sql-formatter-15.1.2.tgz#86df2592eedf6d422244e10e00a74380c22791b7" + integrity sha512-zBrLBclCNurCsQaO6yMvkXzHvv7eJPjiF8LIEQ5HdBV/x6UuWIZwqss3mlZ/6HLj+VYhFKeHpQnyLuZWG2agKQ== + dependencies: + argparse "^2.0.1" + get-stdin "=8.0.0" + nearley "^2.20.1" + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"