From d20eeb0617f2788de8fd5d77d3402e697b92302b Mon Sep 17 00:00:00 2001 From: m5r Date: Fri, 6 Aug 2021 01:07:15 +0800 Subject: [PATCH] multi tenancy stuff --- app/auth/mutations/login.ts | 10 +- app/auth/mutations/signup.ts | 29 +- app/core/hooks/use-current-customer.ts | 13 - app/core/hooks/use-current-phone-number.ts | 11 + app/core/hooks/use-current-user.ts | 15 + app/core/hooks/use-customer-phone-number.ts | 12 - app/core/hooks/use-notifications.ts | 9 +- app/core/hooks/use-require-onboarding.ts | 12 +- app/core/layouts/layout/index.tsx | 2 + .../set-notification-subscription.ts | 51 +- app/core/utils.ts | 35 + app/customers/queries/get-current-customer.ts | 21 - app/messages/api/queue/fetch-messages.ts | 29 +- .../api/queue/insert-incoming-message.ts | 41 +- app/messages/api/queue/insert-messages.ts | 48 +- .../api/queue/notify-incoming-message.ts | 17 +- app/messages/api/queue/send-message.ts | 30 +- .../api/webhook/incoming-message.test.ts | 46 +- app/messages/api/webhook/incoming-message.ts | 54 +- app/messages/components/new-message-area.tsx | 25 +- app/messages/mutations/send-message.ts | 35 +- app/messages/queries/get-conversation.ts | 7 +- app/messages/queries/get-conversations.ts | 81 +- .../api/queue/set-twilio-webhooks.ts | 42 +- app/onboarding/mutations/set-phone-number.ts | 36 +- .../mutations/set-twilio-api-fields.ts | 15 +- app/onboarding/pages/welcome/step-one.tsx | 6 +- app/onboarding/pages/welcome/step-three.tsx | 13 +- app/onboarding/pages/welcome/step-two.tsx | 18 +- app/pages/index.test.tsx | 23 +- app/pages/index.tsx | 10 +- app/phone-calls/api/queue/fetch-calls.ts | 33 +- app/phone-calls/api/queue/insert-calls.ts | 18 +- .../components/phone-calls-list.tsx | 4 +- app/phone-calls/hooks/use-phone-calls.ts | 10 +- app/phone-calls/queries/get-phone-calls.ts | 41 +- .../get-current-customer-phone-number.ts | 20 - .../queries/get-current-phone-number.ts | 21 + .../queries/get-customer-phone-number.ts | 19 - app/settings/components/danger-zone.tsx | 2 - .../components/profile-informations.tsx | 12 +- app/settings/components/update-password.tsx | 2 - app/users/queries/get-current-user.ts | 32 + .../20210804092849_add_orgs/migration.sql | 148 ++ .../migration.sql | 16 + .../20210805153229_org_id_wesh/migration.sql | 36 + db/schema.prisma | 136 +- docker-compose.yml | 21 + package-lock.json | 2067 +---------------- package.json | 8 +- types.ts | 7 +- 51 files changed, 907 insertions(+), 2542 deletions(-) delete mode 100644 app/core/hooks/use-current-customer.ts create mode 100644 app/core/hooks/use-current-phone-number.ts create mode 100644 app/core/hooks/use-current-user.ts delete mode 100644 app/core/hooks/use-customer-phone-number.ts create mode 100644 app/core/utils.ts delete mode 100644 app/customers/queries/get-current-customer.ts delete mode 100644 app/phone-numbers/queries/get-current-customer-phone-number.ts create mode 100644 app/phone-numbers/queries/get-current-phone-number.ts delete mode 100644 app/phone-numbers/queries/get-customer-phone-number.ts create mode 100644 app/users/queries/get-current-user.ts create mode 100644 db/migrations/20210804092849_add_orgs/migration.sql create mode 100644 db/migrations/20210805150436_delete_twiliocredentials_entity/migration.sql create mode 100644 db/migrations/20210805153229_org_id_wesh/migration.sql create mode 100644 docker-compose.yml diff --git a/app/auth/mutations/login.ts b/app/auth/mutations/login.ts index 59e1679..e4bf10f 100644 --- a/app/auth/mutations/login.ts +++ b/app/auth/mutations/login.ts @@ -1,6 +1,6 @@ import { resolver, SecurePassword, AuthenticationError } from "blitz"; -import db, { Role } from "../../../db"; +import db, { GlobalRole } from "../../../db"; import { Login } from "../validations"; export const authenticateUser = async (rawEmail: string, rawPassword: string) => { @@ -25,7 +25,13 @@ export default resolver.pipe(resolver.zod(Login), async ({ email, password }, ct // This throws an error if credentials are invalid const user = await authenticateUser(email, password); - await ctx.session.$create({ userId: user.id, role: user.role as Role }); + const hasCompletedOnboarding = undefined; // TODO + await ctx.session.$create({ + userId: user.id, + roles: [user.role], + hasCompletedOnboarding, + orgId: "user.memberships[0].organizationId", + }); return user; }); diff --git a/app/auth/mutations/signup.ts b/app/auth/mutations/signup.ts index 2753db1..5b6e297 100644 --- a/app/auth/mutations/signup.ts +++ b/app/auth/mutations/signup.ts @@ -1,18 +1,35 @@ import { resolver, SecurePassword } from "blitz"; -import db, { Role } from "../../../db"; +import db, { GlobalRole, MembershipRole } from "../../../db"; import { Signup } from "../validations"; import { computeEncryptionKey } from "../../../db/_encryption"; export default resolver.pipe(resolver.zod(Signup), async ({ email, password }, ctx) => { const hashedPassword = await SecurePassword.hash(password.trim()); + const encryptionKey = computeEncryptionKey(email.toLowerCase().trim()).toString("hex"); const user = await db.user.create({ - data: { email: email.toLowerCase().trim(), hashedPassword, role: Role.USER }, - select: { id: true, name: true, email: true, role: true }, + data: { + email: email.toLowerCase().trim(), + hashedPassword, + role: GlobalRole.CUSTOMER, + memberships: { + create: { + role: MembershipRole.OWNER, + organization: { + create: { + encryptionKey, + }, + }, + }, + }, + }, + include: { memberships: true }, }); - const encryptionKey = computeEncryptionKey(user.id).toString("hex"); - await db.customer.create({ data: { id: user.id, encryptionKey } }); - await ctx.session.$create({ userId: user.id, role: user.role }); + await ctx.session.$create({ + userId: user.id, + roles: [user.role, user.memberships[0]!.role], + orgId: user.memberships[0]!.organizationId, + }); return user; }); diff --git a/app/core/hooks/use-current-customer.ts b/app/core/hooks/use-current-customer.ts deleted file mode 100644 index 3f00caa..0000000 --- a/app/core/hooks/use-current-customer.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useSession, useQuery } from "blitz"; - -import getCurrentCustomer from "../../customers/queries/get-current-customer"; - -export default function useCurrentCustomer() { - const session = useSession(); - const [customer] = useQuery(getCurrentCustomer, null, { enabled: Boolean(session.userId) }); - return { - customer, - hasFilledTwilioCredentials: Boolean(customer && customer.accountSid && customer.authToken), - hasCompletedOnboarding: session.hasCompletedOnboarding, - }; -} diff --git a/app/core/hooks/use-current-phone-number.ts b/app/core/hooks/use-current-phone-number.ts new file mode 100644 index 0000000..a139c46 --- /dev/null +++ b/app/core/hooks/use-current-phone-number.ts @@ -0,0 +1,11 @@ +import { useQuery } from "blitz"; + +import getCurrentPhoneNumber from "../../phone-numbers/queries/get-current-phone-number"; +import useCurrentUser from "./use-current-user"; + +export default function useUserPhoneNumber() { + const { hasFilledTwilioCredentials } = useCurrentUser(); + const [phoneNumber] = useQuery(getCurrentPhoneNumber, {}, { enabled: hasFilledTwilioCredentials }); + + return phoneNumber; +} diff --git a/app/core/hooks/use-current-user.ts b/app/core/hooks/use-current-user.ts new file mode 100644 index 0000000..e480f21 --- /dev/null +++ b/app/core/hooks/use-current-user.ts @@ -0,0 +1,15 @@ +import { useSession, useQuery } from "blitz"; + +import getCurrentUser from "../../users/queries/get-current-user"; + +export default function useCurrentUser() { + const session = useSession(); + const [user] = useQuery(getCurrentUser, null, { enabled: Boolean(session.userId) }); + const organization = user?.memberships[0]!.organization; + return { + user, + organization, + hasFilledTwilioCredentials: Boolean(user && organization?.twilioAccountSid && organization?.twilioAuthToken), + hasCompletedOnboarding: session.hasCompletedOnboarding, + }; +} diff --git a/app/core/hooks/use-customer-phone-number.ts b/app/core/hooks/use-customer-phone-number.ts deleted file mode 100644 index 6ded656..0000000 --- a/app/core/hooks/use-customer-phone-number.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from "blitz"; - -import getCurrentCustomerPhoneNumber from "../../phone-numbers/queries/get-current-customer-phone-number"; -import useCurrentCustomer from "./use-current-customer"; - -export default function useCustomerPhoneNumber() { - const { customer } = useCurrentCustomer(); - const hasFilledTwilioCredentials = Boolean(customer && customer.accountSid && customer.authToken); - const [customerPhoneNumber] = useQuery(getCurrentCustomerPhoneNumber, {}, { enabled: hasFilledTwilioCredentials }); - - return customerPhoneNumber; -} diff --git a/app/core/hooks/use-notifications.ts b/app/core/hooks/use-notifications.ts index 28f9853..a676dd3 100644 --- a/app/core/hooks/use-notifications.ts +++ b/app/core/hooks/use-notifications.ts @@ -2,6 +2,7 @@ import { getConfig, useMutation } from "blitz"; import { useEffect, useState } from "react"; import setNotificationSubscription from "../mutations/set-notification-subscription"; +import useCurrentPhoneNumber from "./use-current-phone-number"; const { publicRuntimeConfig } = getConfig(); @@ -9,6 +10,7 @@ export default function useNotifications() { const isServiceWorkerSupported = "serviceWorker" in navigator; const [subscription, setSubscription] = useState(null); const [setNotificationSubscriptionMutation] = useMutation(setNotificationSubscription); + const phoneNumber = useCurrentPhoneNumber(); useEffect(() => { (async () => { @@ -23,7 +25,7 @@ export default function useNotifications() { }, [isServiceWorkerSupported]); async function subscribe() { - if (!isServiceWorkerSupported) { + if (!isServiceWorkerSupported || !phoneNumber) { return; } @@ -33,7 +35,10 @@ export default function useNotifications() { applicationServerKey: urlBase64ToUint8Array(publicRuntimeConfig.webPush.publicKey), }); setSubscription(subscription); - await setNotificationSubscriptionMutation({ subscription: subscription.toJSON() as any }); // TODO remove as any + await setNotificationSubscriptionMutation({ + phoneNumberId: phoneNumber.id, + subscription: subscription.toJSON() as any, + }); // TODO remove as any } async function unsubscribe() { diff --git a/app/core/hooks/use-require-onboarding.ts b/app/core/hooks/use-require-onboarding.ts index dcd6792..6718326 100644 --- a/app/core/hooks/use-require-onboarding.ts +++ b/app/core/hooks/use-require-onboarding.ts @@ -1,12 +1,12 @@ import { Routes, useRouter } from "blitz"; -import useCurrentCustomer from "./use-current-customer"; -import useCustomerPhoneNumber from "./use-customer-phone-number"; +import useCurrentUser from "./use-current-user"; +import useCurrentPhoneNumber from "./use-current-phone-number"; export default function useRequireOnboarding() { const router = useRouter(); - const { hasFilledTwilioCredentials, hasCompletedOnboarding } = useCurrentCustomer(); - const customerPhoneNumber = useCustomerPhoneNumber(); + const { hasFilledTwilioCredentials, hasCompletedOnboarding } = useCurrentUser(); + const phoneNumber = useCurrentPhoneNumber(); if (hasCompletedOnboarding) { return; @@ -16,12 +16,12 @@ export default function useRequireOnboarding() { throw router.push(Routes.StepTwo()); } - /*if (!customer.paddleCustomerId || !customer.paddleSubscriptionId) { + /*if (!user.paddleCustomerId || !user.paddleSubscriptionId) { throw router.push(Routes.StepTwo()); return; }*/ - if (!customerPhoneNumber) { + if (!phoneNumber) { throw router.push(Routes.StepThree()); } } diff --git a/app/core/layouts/layout/index.tsx b/app/core/layouts/layout/index.tsx index d692eb5..7cc6e4b 100644 --- a/app/core/layouts/layout/index.tsx +++ b/app/core/layouts/layout/index.tsx @@ -77,6 +77,8 @@ const ErrorBoundary = withRouter( // let Blitz ErrorBoundary handle this one throw error; } + + // if network error and connection lost, display the auto-reload page with countdown } public render() { diff --git a/app/core/mutations/set-notification-subscription.ts b/app/core/mutations/set-notification-subscription.ts index 9af98b3..ae6c209 100644 --- a/app/core/mutations/set-notification-subscription.ts +++ b/app/core/mutations/set-notification-subscription.ts @@ -3,10 +3,13 @@ import { z } from "zod"; import db from "../../../db"; import appLogger from "../../../integrations/logger"; +import { enforceSuperAdminIfNotCurrentOrganization, setDefaultOrganizationId } from "../utils"; const logger = appLogger.child({ mutation: "set-notification-subscription" }); const Body = z.object({ + organizationId: z.string().optional(), + phoneNumberId: z.string(), subscription: z.object({ endpoint: z.string(), expirationTime: z.number().nullable(), @@ -17,22 +20,36 @@ const Body = z.object({ }), }); -export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ subscription }, context) => { - const customerId = context.session.userId; - try { - await db.notificationSubscription.create({ - data: { - customerId, - endpoint: subscription.endpoint, - expirationTime: subscription.expirationTime, - keys_p256dh: subscription.keys.p256dh, - keys_auth: subscription.keys.auth, - }, +export default resolver.pipe( + resolver.zod(Body), + resolver.authorize(), + setDefaultOrganizationId, + enforceSuperAdminIfNotCurrentOrganization, + async ({ organizationId, phoneNumberId, subscription }) => { + const phoneNumber = await db.phoneNumber.findFirst({ + where: { id: phoneNumberId, organizationId }, + include: { organization: true }, }); - } catch (error) { - if (error.code !== "P2002") { - logger.error(error); - // we might want to `throw error`; + if (!phoneNumber) { + return; } - } -}); + + try { + await db.notificationSubscription.create({ + data: { + organizationId, + phoneNumberId, + endpoint: subscription.endpoint, + expirationTime: subscription.expirationTime, + keys_p256dh: subscription.keys.p256dh, + keys_auth: subscription.keys.auth, + }, + }); + } catch (error) { + if (error.code !== "P2002") { + logger.error(error); + // we might want to `throw error`; + } + } + }, +); diff --git a/app/core/utils.ts b/app/core/utils.ts new file mode 100644 index 0000000..a704753 --- /dev/null +++ b/app/core/utils.ts @@ -0,0 +1,35 @@ +import type { Ctx } from "blitz"; + +import type { Prisma } from "../../db"; +import { GlobalRole } from "../../db"; + +function assert(condition: any, message: string): asserts condition { + if (!condition) throw new Error(message); +} + +export function setDefaultOrganizationId>( + input: T, + { session }: Ctx, +): T & { organizationId: Prisma.StringNullableFilter | string } { + assert(session.orgId, "Missing session.orgId in setDefaultOrganizationId"); + if (input.organizationId) { + // Pass through the input + return input as T & { organizationId: string }; + } else if (session.roles?.includes(GlobalRole.SUPERADMIN)) { + // Allow viewing any organization + return { ...input, organizationId: { not: "" } }; + } else { + // Set organizationId to session.orgId + return { ...input, organizationId: session.orgId }; + } +} + +export function enforceSuperAdminIfNotCurrentOrganization>(input: T, ctx: Ctx): T { + assert(ctx.session.orgId, "missing session.orgId"); + assert(input.organizationId, "missing input.organizationId"); + + if (input.organizationId !== ctx.session.orgId) { + ctx.session.$authorize(GlobalRole.SUPERADMIN); + } + return input; +} diff --git a/app/customers/queries/get-current-customer.ts b/app/customers/queries/get-current-customer.ts deleted file mode 100644 index c885bc7..0000000 --- a/app/customers/queries/get-current-customer.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { resolver } from "blitz"; - -import db from "../../../db"; - -export default resolver.pipe(resolver.authorize(), async (_ = null, { session }) => { - if (!session.userId) return null; - - return db.customer.findFirst({ - where: { id: session.userId }, - select: { - id: true, - encryptionKey: true, - accountSid: true, - authToken: true, - twimlAppSid: true, - paddleCustomerId: true, - paddleSubscriptionId: true, - user: true, - }, - }); -}); diff --git a/app/messages/api/queue/fetch-messages.ts b/app/messages/api/queue/fetch-messages.ts index da9da75..f19a3c7 100644 --- a/app/messages/api/queue/fetch-messages.ts +++ b/app/messages/api/queue/fetch-messages.ts @@ -5,21 +5,27 @@ import db from "../../../../db"; import insertMessagesQueue from "./insert-messages"; type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; }; -const fetchMessagesQueue = Queue("api/queue/fetch-messages", async ({ customerId }) => { - const [customer, phoneNumber] = await Promise.all([ - db.customer.findFirst({ where: { id: customerId } }), - db.phoneNumber.findFirst({ where: { customerId } }), - ]); - if (!customer || !customer.accountSid || !customer.authToken || !phoneNumber) { +const fetchMessagesQueue = Queue("api/queue/fetch-messages", async ({ organizationId, phoneNumberId }) => { + const phoneNumber = await db.phoneNumber.findFirst({ + where: { id: phoneNumberId, organizationId }, + include: { organization: true }, + }); + if (!phoneNumber) { + return; + } + + const organization = phoneNumber.organization; + if (!organization.twilioAccountSid || !organization.twilioAuthToken) { return; } const [sent, received] = await Promise.all([ - twilio(customer.accountSid, customer.authToken).messages.list({ from: phoneNumber.phoneNumber }), - twilio(customer.accountSid, customer.authToken).messages.list({ to: phoneNumber.phoneNumber }), + twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.list({ from: phoneNumber.number }), + twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.list({ to: phoneNumber.number }), ]); const messagesSent = sent.filter((message) => message.direction.startsWith("outbound")); const messagesReceived = received.filter((message) => message.direction === "inbound"); @@ -29,11 +35,12 @@ const fetchMessagesQueue = Queue("api/queue/fetch-messages", async ({ c await insertMessagesQueue.enqueue( { - customerId, + organizationId, + phoneNumberId, messages, }, { - id: `insert-messages-${customerId}`, + id: `insert-messages-${organizationId}-${phoneNumberId}`, }, ); }); diff --git a/app/messages/api/queue/insert-incoming-message.ts b/app/messages/api/queue/insert-incoming-message.ts index 98d5c54..f75bf1a 100644 --- a/app/messages/api/queue/insert-incoming-message.ts +++ b/app/messages/api/queue/insert-incoming-message.ts @@ -4,46 +4,49 @@ import twilio from "twilio"; import db, { Direction, MessageStatus } from "../../../../db"; import { encrypt } from "../../../../db/_encryption"; +import notifyIncomingMessageQueue from "./notify-incoming-message"; type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; messageSid: MessageInstance["sid"]; }; const insertIncomingMessageQueue = Queue( "api/queue/insert-incoming-message", - async ({ messageSid, customerId }) => { - const customer = await db.customer.findFirst({ where: { id: customerId } }); - if (!customer || !customer.accountSid || !customer.authToken) { + async ({ messageSid, organizationId, phoneNumberId }) => { + const organization = await db.organization.findFirst({ + where: { id: organizationId }, + }); + if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { return; } - const encryptionKey = customer.encryptionKey; - const message = await twilio(customer.accountSid, customer.authToken).messages.get(messageSid).fetch(); + const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken) + .messages.get(messageSid) + .fetch(); await db.message.create({ data: { - customerId, + organizationId, + phoneNumberId, + id: messageSid, to: message.to, from: message.from, status: translateStatus(message.status), direction: translateDirection(message.direction), sentAt: message.dateCreated, - content: encrypt(message.body, customer.encryptionKey), + content: encrypt(message.body, organization.encryptionKey), }, }); - await db.message.createMany({ - data: { - customerId, - content: encrypt(message.body, encryptionKey), - from: message.from, - to: message.to, - status: translateStatus(message.status), - direction: translateDirection(message.direction), - twilioSid: message.sid, - sentAt: new Date(message.dateCreated), + await notifyIncomingMessageQueue.enqueue( + { + messageSid, + organizationId, + phoneNumberId, }, - }); + { id: `notify-${messageSid}-${organizationId}-${phoneNumberId}` }, + ); }, ); diff --git a/app/messages/api/queue/insert-messages.ts b/app/messages/api/queue/insert-messages.ts index 129f847..c36577b 100644 --- a/app/messages/api/queue/insert-messages.ts +++ b/app/messages/api/queue/insert-messages.ts @@ -5,31 +5,39 @@ import db, { Direction, Message, MessageStatus } from "../../../../db"; import { encrypt } from "../../../../db/_encryption"; type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; messages: MessageInstance[]; }; -const insertMessagesQueue = Queue("api/queue/insert-messages", async ({ messages, customerId }) => { - const customer = await db.customer.findFirst({ where: { id: customerId } }); - if (!customer) { - return; - } +const insertMessagesQueue = Queue( + "api/queue/insert-messages", + async ({ messages, organizationId, phoneNumberId }) => { + const phoneNumber = await db.phoneNumber.findFirst({ + where: { id: phoneNumberId, organizationId }, + include: { organization: true }, + }); + if (!phoneNumber) { + return; + } - const sms = messages - .map>((message) => ({ - customerId, - content: encrypt(message.body, customer.encryptionKey), - from: message.from, - to: message.to, - status: translateStatus(message.status), - direction: translateDirection(message.direction), - twilioSid: message.sid, - sentAt: new Date(message.dateCreated), - })) - .sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime()); + const sms = messages + .map>((message) => ({ + organizationId, + phoneNumberId: phoneNumber.id, + content: encrypt(message.body, phoneNumber.organization.encryptionKey), + from: message.from, + to: message.to, + status: translateStatus(message.status), + direction: translateDirection(message.direction), + twilioSid: message.sid, + sentAt: new Date(message.dateCreated), + })) + .sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime()); - await db.message.createMany({ data: sms }); -}); + await db.message.createMany({ data: sms }); + }, +); export default insertMessagesQueue; diff --git a/app/messages/api/queue/notify-incoming-message.ts b/app/messages/api/queue/notify-incoming-message.ts index a365c58..830303a 100644 --- a/app/messages/api/queue/notify-incoming-message.ts +++ b/app/messages/api/queue/notify-incoming-message.ts @@ -11,27 +11,32 @@ const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); const logger = appLogger.child({ queue: "notify-incoming-message" }); type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; messageSid: MessageInstance["sid"]; }; const notifyIncomingMessageQueue = Queue( "api/queue/notify-incoming-message", - async ({ messageSid, customerId }) => { + async ({ messageSid, organizationId, phoneNumberId }) => { webpush.setVapidDetails( "mailto:mokht@rmi.al", publicRuntimeConfig.webPush.publicKey, serverRuntimeConfig.webPush.privateKey, ); - const customer = await db.customer.findFirst({ where: { id: customerId } }); - if (!customer || !customer.accountSid || !customer.authToken) { + const organization = await db.organization.findFirst({ + where: { id: organizationId }, + }); + if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { return; } - const message = await twilio(customer.accountSid, customer.authToken).messages.get(messageSid).fetch(); + const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken) + .messages.get(messageSid) + .fetch(); const notification = { message: `${message.from} - ${message.body}` }; - const subscriptions = await db.notificationSubscription.findMany({ where: { customerId: customer.id } }); + const subscriptions = await db.notificationSubscription.findMany({ where: { organizationId, phoneNumberId } }); await Promise.all( subscriptions.map(async (subscription) => { const webPushSubscription: PushSubscription = { diff --git a/app/messages/api/queue/send-message.ts b/app/messages/api/queue/send-message.ts index 5bbd763..395cd64 100644 --- a/app/messages/api/queue/send-message.ts +++ b/app/messages/api/queue/send-message.ts @@ -1,40 +1,46 @@ import { Queue } from "quirrel/blitz"; import twilio from "twilio"; -import db from "../../../../db"; +import db, { MessageStatus } from "../../../../db"; type Payload = { id: string; - customerId: string; + organizationId: string; + phoneNumberId: string; to: string; content: string; }; const sendMessageQueue = Queue( "api/queue/send-message", - async ({ id, customerId, to, content }) => { - const [customer, phoneNumber] = await Promise.all([ - db.customer.findFirst({ where: { id: customerId } }), - db.phoneNumber.findFirst({ where: { customerId } }), - ]); - if (!customer || !customer.accountSid || !customer.authToken || !phoneNumber) { + async ({ id, organizationId, phoneNumberId, to, content }) => { + const organization = await db.organization.findFirst({ + where: { id: organizationId }, + include: { phoneNumbers: true }, + }); + const phoneNumber = organization?.phoneNumbers.find((phoneNumber) => phoneNumber.id === phoneNumberId); + if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken || !phoneNumber) { return; } try { - const message = await twilio(customer.accountSid, customer.authToken).messages.create({ + const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.create({ body: content, to, - from: phoneNumber.phoneNumber, + from: phoneNumber.number, }); await db.message.update({ - where: { id }, - data: { twilioSid: message.sid }, + where: { organizationId_phoneNumberId_id: { id, organizationId, phoneNumberId } }, + data: { id: message.sid }, }); } catch (error) { // TODO: handle twilio error console.log(error.code); // 21211 console.log(error.moreInfo); // https://www.twilio.com/docs/errors/21211 + await db.message.update({ + where: { id }, + data: { status: MessageStatus.Error /*errorMessage: "Reason: failed because of"*/ }, + }); } }, { diff --git a/app/messages/api/webhook/incoming-message.test.ts b/app/messages/api/webhook/incoming-message.test.ts index 9f6493b..c0b684b 100644 --- a/app/messages/api/webhook/incoming-message.test.ts +++ b/app/messages/api/webhook/incoming-message.test.ts @@ -3,31 +3,26 @@ import twilio from "twilio"; import db from "db"; import handler from "./incoming-message"; -import notifyIncomingMessageQueue from "../queue/notify-incoming-message"; import insertIncomingMessageQueue from "../queue/insert-incoming-message"; describe("/api/webhook/incoming-message", () => { - const mockedFindFirstPhoneNumber = db.phoneNumber.findFirst as jest.Mock< - ReturnType - >; - const mockedFindFirstCustomer = db.customer.findFirst as jest.Mock>; - const mockedEnqueueNotifyIncomingMessage = notifyIncomingMessageQueue.enqueue as jest.Mock< - ReturnType - >; + const mockedFindManyPhoneNumbers = db.phoneNumber.findMany as jest.Mock>; const mockedEnqueueInsertIncomingMessage = insertIncomingMessageQueue.enqueue as jest.Mock< ReturnType >; const mockedValidateRequest = twilio.validateRequest as jest.Mock>; beforeEach(() => { - mockedFindFirstPhoneNumber.mockResolvedValue({ phoneNumber: "+33757592025" } as any); - mockedFindFirstCustomer.mockResolvedValue({ id: "9292", authToken: "twi" } as any); + mockedFindManyPhoneNumbers.mockResolvedValue([ + { + id: "9292", + organization: { id: "2929", twilioAuthToken: "twi" }, + } as any, + ]); }); afterEach(() => { - mockedFindFirstPhoneNumber.mockReset(); - mockedFindFirstCustomer.mockReset(); - mockedEnqueueNotifyIncomingMessage.mockReset(); + mockedFindManyPhoneNumbers.mockReset(); mockedEnqueueInsertIncomingMessage.mockReset(); mockedValidateRequest.mockReset(); }); @@ -50,16 +45,15 @@ describe("/api/webhook/incoming-message", () => { expect(res.status).toBe(200); expect(res.headers.get("content-type")).toBe("text/html"); - [mockedEnqueueNotifyIncomingMessage, mockedEnqueueNotifyIncomingMessage].forEach((enqueue) => { - expect(enqueue).toHaveBeenCalledTimes(1); - expect(enqueue).toHaveBeenCalledWith( - { - messageSid: "SM157246f02006b80953e8c753fb68fad6", - customerId: "9292", - }, - { id: "notify-SM157246f02006b80953e8c753fb68fad6" }, - ); - }); + expect(mockedEnqueueInsertIncomingMessage).toHaveBeenCalledTimes(1); + expect(mockedEnqueueInsertIncomingMessage).toHaveBeenCalledWith( + { + messageSid: "SM157246f02006b80953e8c753fb68fad6", + phoneNumberId: "9292", + organizationId: "2929", + }, + { id: "insert-SM157246f02006b80953e8c753fb68fad6-2929-9292" }, + ); }, }); }); @@ -107,11 +101,7 @@ describe("/api/webhook/incoming-message", () => { }); jest.mock("db", () => ({ - phoneNumber: { findFirst: jest.fn() }, - customer: { findFirst: jest.fn() }, -})); -jest.mock("../queue/notify-incoming-message", () => ({ - enqueue: jest.fn(), + phoneNumber: { findMany: jest.fn() }, })); jest.mock("../queue/insert-incoming-message", () => ({ enqueue: jest.fn(), diff --git a/app/messages/api/webhook/incoming-message.ts b/app/messages/api/webhook/incoming-message.ts index 6f01e44..3629f4c 100644 --- a/app/messages/api/webhook/incoming-message.ts +++ b/app/messages/api/webhook/incoming-message.ts @@ -40,26 +40,25 @@ export default async function incomingMessageHandler(req: BlitzApiRequest, res: const body: Body = req.body; try { - const customerPhoneNumber = await db.phoneNumber.findFirst({ - where: { phoneNumber: body.To }, + const phoneNumbers = await db.phoneNumber.findMany({ + where: { number: body.To }, + include: { organization: true }, }); - if (!customerPhoneNumber) { - // phone number is not registered by any of our customer - res.status(500).end(); - return; - } - - const customer = await db.customer.findFirst({ - where: { id: customerPhoneNumber.customerId }, - }); - if (!customer || !customer.authToken) { + if (phoneNumbers.length === 0) { + // phone number is not registered by any organization res.status(500).end(); return; } const url = `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`; - const isRequestValid = twilio.validateRequest(customer.authToken, twilioSignature, url, req.body); - if (!isRequestValid) { + const phoneNumber = phoneNumbers.find((phoneNumber) => { + // if multiple organizations have the same number + // find the organization currently using that phone number + // maybe we shouldn't let multiple organizations use the same phone number + const authToken = phoneNumber.organization.twilioAuthToken ?? ""; + return twilio.validateRequest(authToken, twilioSignature, url, req.body); + }); + if (!phoneNumber) { const statusCode = 400; const apiError: ApiError = { statusCode, @@ -72,23 +71,16 @@ export default async function incomingMessageHandler(req: BlitzApiRequest, res: } const messageSid = body.MessageSid; - const customerId = customer.id; - await Promise.all([ - notifyIncomingMessageQueue.enqueue( - { - messageSid, - customerId, - }, - { id: `notify-${messageSid}` }, - ), - insertIncomingMessageQueue.enqueue( - { - messageSid, - customerId, - }, - { id: `insert-${messageSid}` }, - ), - ]); + const organizationId = phoneNumber.organization.id; + const phoneNumberId = phoneNumber.id; + await insertIncomingMessageQueue.enqueue( + { + messageSid, + organizationId, + phoneNumberId, + }, + { id: `insert-${messageSid}-${organizationId}-${phoneNumberId}` }, + ); res.setHeader("content-type", "text/html"); res.status(200).send(""); diff --git a/app/messages/components/new-message-area.tsx b/app/messages/components/new-message-area.tsx index 451bc26..beaad06 100644 --- a/app/messages/components/new-message-area.tsx +++ b/app/messages/components/new-message-area.tsx @@ -1,14 +1,14 @@ +import type { FunctionComponent } from "react"; +import { useMutation, useQuery } from "blitz"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPaperPlane } from "@fortawesome/pro-regular-svg-icons"; import { useForm } from "react-hook-form"; -import { useMutation, useQuery } from "blitz"; import sendMessage from "../mutations/send-message"; import { Direction, Message, MessageStatus } from "../../../db"; import getConversationsQuery from "../queries/get-conversations"; -import useCurrentCustomer from "../../core/hooks/use-current-customer"; -import useCustomerPhoneNumber from "../../core/hooks/use-customer-phone-number"; -import { FunctionComponent } from "react"; +import useCurrentUser from "../../core/hooks/use-current-user"; +import useCurrentPhoneNumber from "../../core/hooks/use-current-phone-number"; type Form = { content: string; @@ -20,8 +20,8 @@ type Props = { }; const NewMessageArea: FunctionComponent = ({ recipient, onSend }) => { - const { customer } = useCurrentCustomer(); - const phoneNumber = useCustomerPhoneNumber(); + const { organization } = useCurrentUser(); + const phoneNumber = useCurrentPhoneNumber(); const sendMessageMutation = useMutation(sendMessage)[0]; const { setQueryData: setConversationsQueryData, refetch: refetchConversations } = useQuery( getConversationsQuery, @@ -45,9 +45,9 @@ const NewMessageArea: FunctionComponent = ({ recipient, onSend }) => { const id = uuidv4(); const message: Message = { id, - customerId: customer!.id, - twilioSid: id, - from: phoneNumber!.phoneNumber, + organizationId: organization!.id, + phoneNumberId: phoneNumber!.id, + from: phoneNumber!.number, to: recipient, content: content, direction: Direction.Outbound, @@ -63,7 +63,12 @@ const NewMessageArea: FunctionComponent = ({ recipient, onSend }) => { } nextConversations[recipient] = [...nextConversations[recipient]!, message]; - return nextConversations; + + return Object.fromEntries( + Object.entries(nextConversations).sort( + ([, a], [, b]) => b[b.length - 1]!.sentAt.getTime() - a[a.length - 1]!.sentAt.getTime(), + ), + ); }, { refetch: false }, ); diff --git a/app/messages/mutations/send-message.ts b/app/messages/mutations/send-message.ts index 170204d..d3f1418 100644 --- a/app/messages/mutations/send-message.ts +++ b/app/messages/mutations/send-message.ts @@ -1,10 +1,8 @@ -import { resolver } from "blitz"; +import { NotFoundError, resolver } from "blitz"; import { z } from "zod"; import twilio from "twilio"; import db, { Direction, MessageStatus } from "../../../db"; -import getCurrentCustomer from "../../customers/queries/get-current-customer"; -import getCustomerPhoneNumber from "../../phone-numbers/queries/get-customer-phone-number"; import { encrypt } from "../../../db/_encryption"; import sendMessageQueue from "../../messages/api/queue/send-message"; import appLogger from "../../../integrations/logger"; @@ -17,32 +15,40 @@ const Body = z.object({ }); export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ content, to }, context) => { - const customer = await getCurrentCustomer(null, context); - if (!customer || !customer.accountSid || !customer.authToken) { + const organizationId = context.session.orgId; + const organization = await db.organization.findFirst({ + where: { id: organizationId }, + include: { phoneNumbers: true }, + }); + if (!organization) { + throw new NotFoundError(); + } + if (!organization.twilioAccountSid || !organization.twilioAuthToken) { return; } try { - await twilio(customer.accountSid, customer.authToken).lookups.v1.phoneNumbers(to).fetch(); + await twilio(organization.twilioAccountSid, organization.twilioAuthToken).lookups.v1.phoneNumbers(to).fetch(); } catch (error) { logger.error(error); return; } - const customerId = customer.id; - const customerPhoneNumber = await getCustomerPhoneNumber({ customerId }, context); - if (!customerPhoneNumber) { + const phoneNumber = organization.phoneNumbers[0]; + if (!phoneNumber) { return; } + const phoneNumberId = phoneNumber.id; const message = await db.message.create({ data: { - customerId, + organizationId, + phoneNumberId, to, - from: customerPhoneNumber.phoneNumber, + from: phoneNumber.number, direction: Direction.Outbound, status: MessageStatus.Queued, - content: encrypt(content, customer.encryptionKey), + content: encrypt(content, organization.encryptionKey), sentAt: new Date(), }, }); @@ -50,12 +56,13 @@ export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ await sendMessageQueue.enqueue( { id: message.id, - customerId, + organizationId, + phoneNumberId, to, content, }, { - id: `insert-${message.id}`, + id: `insert-${message.id}-${organizationId}-${phoneNumberId}`, }, ); }); diff --git a/app/messages/queries/get-conversation.ts b/app/messages/queries/get-conversation.ts index 861c4fc..61687bb 100644 --- a/app/messages/queries/get-conversation.ts +++ b/app/messages/queries/get-conversation.ts @@ -3,15 +3,14 @@ import { z } from "zod"; import db, { Prisma } from "../../../db"; import { decrypt } from "../../../db/_encryption"; -import getCurrentCustomer from "../../customers/queries/get-current-customer"; const GetConversations = z.object({ recipient: z.string(), }); export default resolver.pipe(resolver.zod(GetConversations), resolver.authorize(), async ({ recipient }, context) => { - const customer = await getCurrentCustomer(null, context); - if (!customer) { + const organization = await db.organization.findFirst({ where: { id: context.session.orgId } }); + if (!organization) { throw new NotFoundError(); } @@ -23,7 +22,7 @@ export default resolver.pipe(resolver.zod(GetConversations), resolver.authorize( return conversation.map((message) => { return { ...message, - content: decrypt(message.content, customer.encryptionKey), + content: decrypt(message.content, organization.encryptionKey), }; }); }); diff --git a/app/messages/queries/get-conversations.ts b/app/messages/queries/get-conversations.ts index 996182f..cafa577 100644 --- a/app/messages/queries/get-conversations.ts +++ b/app/messages/queries/get-conversations.ts @@ -1,45 +1,56 @@ import { resolver, NotFoundError } from "blitz"; +import { z } from "zod"; import db, { Direction, Message, Prisma } from "../../../db"; -import getCurrentCustomer from "../../customers/queries/get-current-customer"; import { decrypt } from "../../../db/_encryption"; +import { enforceSuperAdminIfNotCurrentOrganization, setDefaultOrganizationId } from "../../core/utils"; -export default resolver.pipe(resolver.authorize(), async (_ = null, context) => { - const customer = await getCurrentCustomer(null, context); - if (!customer) { - throw new NotFoundError(); - } - - const messages = await db.message.findMany({ - where: { customerId: customer.id }, - orderBy: { sentAt: Prisma.SortOrder.asc }, - }); - - let conversations: Record = {}; - for (const message of messages) { - let recipient: string; - if (message.direction === Direction.Outbound) { - recipient = message.to; - } else { - recipient = message.from; +export default resolver.pipe( + resolver.zod(z.object({ organizationId: z.string().optional() })), + resolver.authorize(), + setDefaultOrganizationId, + enforceSuperAdminIfNotCurrentOrganization, + async ({ organizationId }) => { + const organization = await db.organization.findFirst({ + where: { id: organizationId }, + include: { phoneNumbers: true }, + }); + if (!organization) { + throw new NotFoundError(); } - if (!conversations[recipient]) { - conversations[recipient] = []; - } - - conversations[recipient]!.push({ - ...message, - content: decrypt(message.content, customer.encryptionKey), + const phoneNumberId = organization.phoneNumbers[0]!.id; + const messages = await db.message.findMany({ + where: { organizationId, phoneNumberId }, + orderBy: { sentAt: Prisma.SortOrder.asc }, }); - conversations[recipient]!.sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime()); - } - conversations = Object.fromEntries( - Object.entries(conversations).sort( - ([, a], [, b]) => b[b.length - 1]!.sentAt.getTime() - a[a.length - 1]!.sentAt.getTime(), - ), - ); + let conversations: Record = {}; + for (const message of messages) { + let recipient: string; + if (message.direction === Direction.Outbound) { + recipient = message.to; + } else { + recipient = message.from; + } - return conversations; -}); + if (!conversations[recipient]) { + conversations[recipient] = []; + } + + conversations[recipient]!.push({ + ...message, + content: decrypt(message.content, organization.encryptionKey), + }); + + conversations[recipient]!.sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime()); + } + conversations = Object.fromEntries( + Object.entries(conversations).sort( + ([, a], [, b]) => b[b.length - 1]!.sentAt.getTime() - a[a.length - 1]!.sentAt.getTime(), + ), + ); + + return conversations; + }, +); diff --git a/app/onboarding/api/queue/set-twilio-webhooks.ts b/app/onboarding/api/queue/set-twilio-webhooks.ts index db821d1..0e631e3 100644 --- a/app/onboarding/api/queue/set-twilio-webhooks.ts +++ b/app/onboarding/api/queue/set-twilio-webhooks.ts @@ -5,23 +5,31 @@ import twilio from "twilio"; import db from "../../../../db"; type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; }; const { serverRuntimeConfig } = getConfig(); -const setTwilioWebhooks = Queue("api/queue/set-twilio-webhooks", async ({ customerId }) => { - const [customer, phoneNumber] = await Promise.all([ - db.customer.findFirst({ where: { id: customerId } }), - db.phoneNumber.findFirst({ where: { customerId } }), - ]); - if (!customer || !customer.accountSid || !customer.authToken || !phoneNumber) { +const setTwilioWebhooks = Queue("api/queue/set-twilio-webhooks", async ({ organizationId, phoneNumberId }) => { + const phoneNumber = await db.phoneNumber.findFirst({ + where: { id: phoneNumberId, organizationId }, + include: { organization: true }, + }); + if (!phoneNumber) { return; } - const twimlApp = customer.twimlAppSid - ? await twilio(customer.accountSid, customer.authToken).applications.get(customer.twimlAppSid).fetch() - : await twilio(customer.accountSid, customer.authToken).applications.create({ + const organization = phoneNumber.organization; + if (!organization.twilioAccountSid || !organization.twilioAuthToken) { + return; + } + + const twimlApp = organization.twimlAppSid + ? await twilio(organization.twilioAccountSid, organization.twilioAuthToken) + .applications.get(organization.twimlAppSid) + .fetch() + : await twilio(organization.twilioAccountSid, organization.twilioAuthToken).applications.create({ friendlyName: "Shellphone", smsUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`, smsMethod: "POST", @@ -31,14 +39,16 @@ const setTwilioWebhooks = Queue("api/queue/set-twilio-webhooks", async const twimlAppSid = twimlApp.sid; await Promise.all([ - db.customer.update({ - where: { id: customerId }, + db.organization.update({ + where: { id: organizationId }, data: { twimlAppSid }, }), - twilio(customer.accountSid, customer.authToken).incomingPhoneNumbers.get(phoneNumber.phoneNumberSid).update({ - smsApplicationSid: twimlAppSid, - voiceApplicationSid: twimlAppSid, - }), + twilio(organization.twilioAccountSid, organization.twilioAuthToken) + .incomingPhoneNumbers.get(phoneNumber.id) + .update({ + smsApplicationSid: twimlAppSid, + voiceApplicationSid: twimlAppSid, + }), ]); }); diff --git a/app/onboarding/mutations/set-phone-number.ts b/app/onboarding/mutations/set-phone-number.ts index d7adda5..388f02e 100644 --- a/app/onboarding/mutations/set-phone-number.ts +++ b/app/onboarding/mutations/set-phone-number.ts @@ -3,7 +3,7 @@ import { z } from "zod"; import twilio from "twilio"; import db from "../../../db"; -import getCurrentCustomer from "../../customers/queries/get-current-customer"; +import getCurrentUser from "../../users/queries/get-current-user"; import fetchMessagesQueue from "../../messages/api/queue/fetch-messages"; import fetchCallsQueue from "../../phone-calls/api/queue/fetch-calls"; import setTwilioWebhooks from "../api/queue/set-twilio-webhooks"; @@ -13,26 +13,40 @@ const Body = z.object({ }); export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberSid }, context) => { - const customer = await getCurrentCustomer(null, context); - if (!customer || !customer.accountSid || !customer.authToken) { + const user = await getCurrentUser(null, context); + const organization = user?.memberships[0]!.organization; + if (!user || !organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { return; } - const customerId = customer.id; - const phoneNumbers = await twilio(customer.accountSid, customer.authToken).incomingPhoneNumbers.list(); + const phoneNumbers = await twilio( + organization.twilioAccountSid, + organization.twilioAuthToken, + ).incomingPhoneNumbers.list(); const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!; + const organizationId = organization.id; await db.phoneNumber.create({ data: { - customerId, - phoneNumberSid, - phoneNumber: phoneNumber.phoneNumber, + organizationId, + id: phoneNumberSid, + number: phoneNumber.phoneNumber, }, }); context.session.$setPrivateData({ hasCompletedOnboarding: true }); + const phoneNumberId = phoneNumberSid; await Promise.all([ - fetchMessagesQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }), - fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }), - setTwilioWebhooks.enqueue({ customerId }, { id: `set-twilio-webhooks-${customerId}` }), + fetchMessagesQueue.enqueue( + { organizationId, phoneNumberId }, + { id: `fetch-messages-${organizationId}-${phoneNumberId}` }, + ), + fetchCallsQueue.enqueue( + { organizationId, phoneNumberId }, + { id: `fetch-messages-${organizationId}-${phoneNumberId}` }, + ), + setTwilioWebhooks.enqueue( + { organizationId, phoneNumberId }, + { id: `set-twilio-webhooks-${organizationId}-${phoneNumberId}` }, + ), ]); }); diff --git a/app/onboarding/mutations/set-twilio-api-fields.ts b/app/onboarding/mutations/set-twilio-api-fields.ts index 6c10a13..6c20caa 100644 --- a/app/onboarding/mutations/set-twilio-api-fields.ts +++ b/app/onboarding/mutations/set-twilio-api-fields.ts @@ -2,7 +2,7 @@ import { resolver } from "blitz"; import { z } from "zod"; import db from "../../../db"; -import getCurrentCustomer from "../../customers/queries/get-current-customer"; +import getCurrentUser from "../../users/queries/get-current-user"; const Body = z.object({ twilioAccountSid: z.string(), @@ -13,16 +13,17 @@ export default resolver.pipe( resolver.zod(Body), resolver.authorize(), async ({ twilioAccountSid, twilioAuthToken }, context) => { - const customer = await getCurrentCustomer(null, context); - if (!customer) { + const user = await getCurrentUser(null, context); + if (!user) { return; } - await db.customer.update({ - where: { id: customer.id }, + const organizationId = user.memberships[0]!.id; + await db.organization.update({ + where: { id: organizationId }, data: { - accountSid: twilioAccountSid, - authToken: twilioAuthToken, + twilioAccountSid: twilioAccountSid, + twilioAuthToken: twilioAuthToken, }, }); }, diff --git a/app/onboarding/pages/welcome/step-one.tsx b/app/onboarding/pages/welcome/step-one.tsx index 579a4f3..3bf0fff 100644 --- a/app/onboarding/pages/welcome/step-one.tsx +++ b/app/onboarding/pages/welcome/step-one.tsx @@ -2,11 +2,11 @@ import type { BlitzPage, GetServerSideProps } from "blitz"; import { getSession, Routes } from "blitz"; import OnboardingLayout from "../../components/onboarding-layout"; -import useCurrentCustomer from "../../../core/hooks/use-current-customer"; +import useCurrentUser from "../../../core/hooks/use-current-user"; import db from "../../../../db"; const StepOne: BlitzPage = () => { - useCurrentCustomer(); // preload for step two + useCurrentUser(); // preload for step two return (
@@ -35,7 +35,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { }; } - const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } }); + const phoneNumber = await db.phoneNumber.findFirst({ where: { organizationId: session.orgId } }); if (phoneNumber) { await session.$setPublicData({ hasCompletedOnboarding: true }); return { diff --git a/app/onboarding/pages/welcome/step-three.tsx b/app/onboarding/pages/welcome/step-three.tsx index 47718fd..deacb34 100644 --- a/app/onboarding/pages/welcome/step-three.tsx +++ b/app/onboarding/pages/welcome/step-three.tsx @@ -100,7 +100,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res } }; } - const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } }); + const phoneNumber = await db.phoneNumber.findFirst({ where: { organizationId: session.orgId } }); if (phoneNumber) { await session.$setPublicData({ hasCompletedOnboarding: true }); return { @@ -111,8 +111,8 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res } }; } - const customer = await db.customer.findFirst({ where: { id: session.userId } }); - if (!customer) { + const organization = await db.organization.findFirst({ where: { id: session.orgId } }); + if (!organization) { return { redirect: { destination: Routes.StepOne().pathname, @@ -121,7 +121,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res } }; } - if (!customer.accountSid || !customer.authToken) { + if (!organization.twilioAccountSid || !organization.twilioAuthToken) { return { redirect: { destination: Routes.StepTwo().pathname, @@ -130,7 +130,10 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res } }; } - const incomingPhoneNumbers = await twilio(customer.accountSid, customer.authToken).incomingPhoneNumbers.list(); + const incomingPhoneNumbers = await twilio( + organization.twilioAccountSid, + organization.twilioAuthToken, + ).incomingPhoneNumbers.list(); const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid })); return { diff --git a/app/onboarding/pages/welcome/step-two.tsx b/app/onboarding/pages/welcome/step-two.tsx index 11bd72c..1140c62 100644 --- a/app/onboarding/pages/welcome/step-two.tsx +++ b/app/onboarding/pages/welcome/step-two.tsx @@ -11,7 +11,7 @@ import db from "db"; import setTwilioApiFields from "../../mutations/set-twilio-api-fields"; import OnboardingLayout from "../../components/onboarding-layout"; import HelpModal from "../../components/help-modal"; -import useCurrentCustomer from "../../../core/hooks/use-current-customer"; +import useCurrentUser from "../../../core/hooks/use-current-user"; type Form = { twilioAccountSid: string; @@ -26,14 +26,14 @@ const StepTwo: BlitzPage = () => { formState: { isSubmitting }, } = useForm
(); const router = useRouter(); - const { customer } = useCurrentCustomer(); + const { organization } = useCurrentUser(); const [setTwilioApiFieldsMutation] = useMutation(setTwilioApiFields); const [isHelpModalOpen, setIsHelpModalOpen] = useState(false); useEffect(() => { - setValue("twilioAuthToken", customer?.authToken ?? ""); - setValue("twilioAccountSid", customer?.accountSid ?? ""); - }, [setValue, customer?.authToken, customer?.accountSid]); + setValue("twilioAuthToken", organization?.twilioAuthToken ?? ""); + setValue("twilioAccountSid", organization?.twilioAccountSid ?? ""); + }, [setValue, organization?.twilioAuthToken, organization?.twilioAccountSid]); const onSubmit = handleSubmit(async ({ twilioAccountSid, twilioAuthToken }) => { if (isSubmitting) { @@ -105,9 +105,9 @@ StepTwo.getLayout = (page) => { }; const StepTwoLayout: FunctionComponent = ({ children }) => { - const { customer } = useCurrentCustomer(); - const initialAuthToken = customer?.authToken ?? ""; - const initialAccountSid = customer?.accountSid ?? ""; + const { organization } = useCurrentUser(); + const initialAuthToken = organization?.twilioAuthToken ?? ""; + const initialAccountSid = organization?.twilioAccountSid ?? ""; const hasTwilioCredentials = initialAccountSid.length > 0 && initialAuthToken.length > 0; return ( @@ -135,7 +135,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { }; } - const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } }); + const phoneNumber = await db.phoneNumber.findFirst({ where: { organizationId: session.orgId } }); if (phoneNumber) { await session.$setPublicData({ hasCompletedOnboarding: true }); return { diff --git a/app/pages/index.test.tsx b/app/pages/index.test.tsx index c758878..f7ebc06 100644 --- a/app/pages/index.test.tsx +++ b/app/pages/index.test.tsx @@ -1,9 +1,10 @@ +import { GlobalRole } from "db"; import { render } from "../../test/utils"; import Home from "./index"; -import useCurrentCustomer from "../core/hooks/use-current-customer"; +import useCurrentUser from "../core/hooks/use-current-user"; -jest.mock("../core/hooks/use-current-customer"); -const mockUseCurrentCustomer = useCurrentCustomer as jest.MockedFunction; +jest.mock("../core/hooks/use-current-user"); +const mockUseCurrentUser = useCurrentUser as jest.MockedFunction; test.skip("renders blitz documentation link", () => { // This is an example of how to ensure a specific item is in the document @@ -11,16 +12,14 @@ test.skip("renders blitz documentation link", () => { // when you remove the the default content from the page // This is an example on how to mock api hooks when testing - mockUseCurrentCustomer.mockReturnValue({ - customer: { + mockUseCurrentUser.mockReturnValue({ + organization: undefined, + user: { id: uuidv4(), - encryptionKey: "", - accountSid: null, - authToken: null, - twimlAppSid: null, - paddleCustomerId: null, - paddleSubscriptionId: null, - user: {} as any, + name: "name", + email: "email@test.com", + role: GlobalRole.CUSTOMER, + memberships: [], }, hasFilledTwilioCredentials: false, hasCompletedOnboarding: undefined, diff --git a/app/pages/index.tsx b/app/pages/index.tsx index ccc225b..e6af17c 100644 --- a/app/pages/index.tsx +++ b/app/pages/index.tsx @@ -4,7 +4,7 @@ import { Link, useMutation, Routes } from "blitz"; import BaseLayout from "../core/layouts/base-layout"; import logout from "../auth/mutations/logout"; -import useCurrentCustomer from "../core/hooks/use-current-customer"; +import useCurrentUser from "../core/hooks/use-current-user"; /* * This file is just for a pleasant getting started page for your new app. @@ -12,10 +12,10 @@ import useCurrentCustomer from "../core/hooks/use-current-customer"; */ const UserInfo = () => { - const { customer } = useCurrentCustomer(); + const { user } = useCurrentUser(); const [logoutMutation] = useMutation(logout); - if (customer) { + if (user) { return ( <>
- User id: {customer.id} + User id: {user.id}
- User role: {customer.encryptionKey} + User role: {user.role}
); diff --git a/app/phone-calls/api/queue/fetch-calls.ts b/app/phone-calls/api/queue/fetch-calls.ts index a340e1e..09b02d7 100644 --- a/app/phone-calls/api/queue/fetch-calls.ts +++ b/app/phone-calls/api/queue/fetch-calls.ts @@ -5,35 +5,42 @@ import db from "../../../../db"; import insertCallsQueue from "./insert-calls"; type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; }; -const fetchCallsQueue = Queue("api/queue/fetch-calls", async ({ customerId }) => { - const [customer, phoneNumber] = await Promise.all([ - db.customer.findFirst({ where: { id: customerId } }), - db.phoneNumber.findFirst({ where: { customerId } }), - ]); - if (!customer || !customer.accountSid || !customer.authToken || !phoneNumber) { +const fetchCallsQueue = Queue("api/queue/fetch-calls", async ({ organizationId, phoneNumberId }) => { + const phoneNumber = await db.phoneNumber.findFirst({ + where: { id: phoneNumberId, organizationId }, + include: { organization: true }, + }); + if (!phoneNumber) { + return; + } + + const organization = phoneNumber.organization; + if (!organization.twilioAccountSid || !organization.twilioAuthToken) { return; } const [callsSent, callsReceived] = await Promise.all([ - twilio(customer.accountSid, customer.authToken).calls.list({ - from: phoneNumber.phoneNumber, + twilio(organization.twilioAccountSid, organization.twilioAuthToken).calls.list({ + from: phoneNumber.number, }), - twilio(customer.accountSid, customer.authToken).calls.list({ - to: phoneNumber.phoneNumber, + twilio(organization.twilioAccountSid, organization.twilioAuthToken).calls.list({ + to: phoneNumber.number, }), ]); const calls = [...callsSent, ...callsReceived].sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()); await insertCallsQueue.enqueue( { - customerId, + organizationId, + phoneNumberId, calls, }, { - id: `insert-calls-${customerId}`, + id: `insert-calls-${organizationId}-${phoneNumberId}`, }, ); }); diff --git a/app/phone-calls/api/queue/insert-calls.ts b/app/phone-calls/api/queue/insert-calls.ts index 67ce0a2..71044cf 100644 --- a/app/phone-calls/api/queue/insert-calls.ts +++ b/app/phone-calls/api/queue/insert-calls.ts @@ -4,15 +4,25 @@ import type { CallInstance } from "twilio/lib/rest/api/v2010/account/call"; import db, { Direction, CallStatus } from "../../../../db"; type Payload = { - customerId: string; + organizationId: string; + phoneNumberId: string; calls: CallInstance[]; }; -const insertCallsQueue = Queue("api/queue/insert-calls", async ({ calls, customerId }) => { +const insertCallsQueue = Queue("api/queue/insert-calls", async ({ calls, organizationId, phoneNumberId }) => { + const phoneNumber = await db.phoneNumber.findFirst({ + where: { id: phoneNumberId, organizationId }, + include: { organization: true }, + }); + if (!phoneNumber) { + return; + } + const phoneCalls = calls .map((call) => ({ - customerId, - twilioSid: call.sid, + organizationId, + phoneNumberId, + id: call.sid, from: call.from, to: call.to, direction: translateDirection(call.direction), diff --git a/app/phone-calls/components/phone-calls-list.tsx b/app/phone-calls/components/phone-calls-list.tsx index 5ff9622..e5b721f 100644 --- a/app/phone-calls/components/phone-calls-list.tsx +++ b/app/phone-calls/components/phone-calls-list.tsx @@ -2,7 +2,7 @@ import { Direction } from "../../../db"; import usePhoneCalls from "../hooks/use-phone-calls"; export default function PhoneCallsList() { - const phoneCalls = usePhoneCalls(); + const phoneCalls = usePhoneCalls()[0]; if (phoneCalls.length === 0) { return
empty state
; @@ -13,7 +13,7 @@ export default function PhoneCallsList() { {phoneCalls.map((phoneCall) => { const recipient = Direction.Outbound ? phoneCall.to : phoneCall.from; return ( -
  • +
  • {recipient}
    {new Date(phoneCall.createdAt).toLocaleString("fr-FR")}
  • diff --git a/app/phone-calls/hooks/use-phone-calls.ts b/app/phone-calls/hooks/use-phone-calls.ts index 15886d9..57c3f43 100644 --- a/app/phone-calls/hooks/use-phone-calls.ts +++ b/app/phone-calls/hooks/use-phone-calls.ts @@ -1,15 +1,13 @@ import { NotFoundError, useQuery } from "blitz"; -import useCurrentCustomer from "../../core/hooks/use-current-customer"; +import useCurrentPhoneNumber from "../..//core/hooks/use-current-phone-number"; import getPhoneCalls from "../queries/get-phone-calls"; export default function usePhoneCalls() { - const { customer } = useCurrentCustomer(); - if (!customer) { + const phoneNumber = useCurrentPhoneNumber(); + if (!phoneNumber) { throw new NotFoundError(); } - const { phoneCalls } = useQuery(getPhoneCalls, { customerId: customer.id })[0]; - - return phoneCalls; + return useQuery(getPhoneCalls, { phoneNumberId: phoneNumber.id }); } diff --git a/app/phone-calls/queries/get-phone-calls.ts b/app/phone-calls/queries/get-phone-calls.ts index d1163d5..822e805 100644 --- a/app/phone-calls/queries/get-phone-calls.ts +++ b/app/phone-calls/queries/get-phone-calls.ts @@ -1,31 +1,16 @@ -import { paginate, resolver } from "blitz"; -import db, { Prisma, Customer } from "db"; +import { resolver } from "blitz"; +import { z } from "zod"; +import db, { Prisma } from "db"; -interface GetPhoneCallsInput extends Pick { - customerId: Customer["id"]; -} +const Body = z.object({ + phoneNumberId: z.string(), +}); -export default resolver.pipe( - resolver.authorize(), - async ({ where, orderBy, skip = 0, take = 100 }: GetPhoneCallsInput) => { - // TODO: in multi-tenant app, you must add validation to ensure correct tenant - const { - items: phoneCalls, - hasMore, - nextPage, - count, - } = await paginate({ - skip, - take, - count: () => db.phoneCall.count({ where }), - query: (paginateArgs) => db.phoneCall.findMany({ ...paginateArgs, where, orderBy }), - }); +export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberId }, context) => { + const organizationId = context.session.orgId; - return { - phoneCalls, - nextPage, - hasMore, - count, - }; - }, -); + return db.phoneCall.findMany({ + where: { organizationId, phoneNumberId }, + orderBy: { createdAt: Prisma.SortOrder.asc }, + }); +}); diff --git a/app/phone-numbers/queries/get-current-customer-phone-number.ts b/app/phone-numbers/queries/get-current-customer-phone-number.ts deleted file mode 100644 index d75d52b..0000000 --- a/app/phone-numbers/queries/get-current-customer-phone-number.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NotFoundError, resolver } from "blitz"; - -import db from "db"; -import getCurrentCustomer from "../../customers/queries/get-current-customer"; - -export default resolver.pipe(resolver.authorize(), async (_ = null, context) => { - const customer = await getCurrentCustomer(null, context); - if (!customer) { - throw new NotFoundError(); - } - - return db.phoneNumber.findFirst({ - where: { customerId: customer.id }, - select: { - id: true, - phoneNumber: true, - phoneNumberSid: true, - }, - }); -}); diff --git a/app/phone-numbers/queries/get-current-phone-number.ts b/app/phone-numbers/queries/get-current-phone-number.ts new file mode 100644 index 0000000..601e9ab --- /dev/null +++ b/app/phone-numbers/queries/get-current-phone-number.ts @@ -0,0 +1,21 @@ +import { resolver } from "blitz"; +import { z } from "zod"; + +import db from "db"; +import { enforceSuperAdminIfNotCurrentOrganization, setDefaultOrganizationId } from "../../core/utils"; + +export default resolver.pipe( + resolver.zod(z.object({ organizationId: z.string().optional() })), + resolver.authorize(), + setDefaultOrganizationId, + enforceSuperAdminIfNotCurrentOrganization, + async ({ organizationId }) => { + return db.phoneNumber.findFirst({ + where: { organizationId }, + select: { + id: true, + number: true, + }, + }); + }, +); diff --git a/app/phone-numbers/queries/get-customer-phone-number.ts b/app/phone-numbers/queries/get-customer-phone-number.ts deleted file mode 100644 index 5496bb7..0000000 --- a/app/phone-numbers/queries/get-customer-phone-number.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { resolver } from "blitz"; -import db from "db"; -import { z } from "zod"; - -const GetCustomerPhoneNumber = z.object({ - // This accepts type of undefined, but is required at runtime - customerId: z.string().optional().refine(Boolean, "Required"), -}); - -export default resolver.pipe(resolver.zod(GetCustomerPhoneNumber), async ({ customerId }) => - db.phoneNumber.findFirst({ - where: { customerId }, - select: { - id: true, - phoneNumber: true, - phoneNumberSid: true, - }, - }), -); diff --git a/app/settings/components/danger-zone.tsx b/app/settings/components/danger-zone.tsx index 8eef4aa..d59c59f 100644 --- a/app/settings/components/danger-zone.tsx +++ b/app/settings/components/danger-zone.tsx @@ -4,10 +4,8 @@ import clsx from "clsx"; import Button from "./button"; import SettingsSection from "./settings-section"; import Modal, { ModalTitle } from "./modal"; -import useCurrentCustomer from "../../core/hooks/use-current-customer"; export default function DangerZone() { - const customer = useCurrentCustomer(); const [isDeletingUser, setIsDeletingUser] = useState(false); const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false); const modalCancelButtonRef = useRef(null); diff --git a/app/settings/components/profile-informations.tsx b/app/settings/components/profile-informations.tsx index 777b5c7..cf99ac0 100644 --- a/app/settings/components/profile-informations.tsx +++ b/app/settings/components/profile-informations.tsx @@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"; import Alert from "./alert"; import Button from "./button"; import SettingsSection from "./settings-section"; -import useCurrentCustomer from "../../core/hooks/use-current-customer"; +import useCurrentUser from "../../core/hooks/use-current-user"; import appLogger from "../../../integrations/logger"; @@ -18,7 +18,7 @@ type Form = { const logger = appLogger.child({ module: "profile-settings" }); const ProfileInformations: FunctionComponent = () => { - const { customer } = useCurrentCustomer(); + const { user } = useCurrentUser(); const router = useRouter(); const { register, @@ -29,9 +29,9 @@ const ProfileInformations: FunctionComponent = () => { const [errorMessage, setErrorMessage] = useState(""); useEffect(() => { - setValue("name", customer?.user.name ?? ""); - setValue("email", customer?.user.email ?? ""); - }, [setValue, customer]); + setValue("name", user?.name ?? ""); + setValue("email", user?.email ?? ""); + }, [setValue, user]); const onSubmit = handleSubmit(async ({ name, email }) => { if (isSubmitting) { @@ -40,7 +40,7 @@ const ProfileInformations: FunctionComponent = () => { try { // TODO - // await customer.updateUser({ email, data: { name } }); + // await updateUser({ email, data: { name } }); } catch (error) { logger.error(error.response, "error updating user infos"); diff --git a/app/settings/components/update-password.tsx b/app/settings/components/update-password.tsx index 7dcec39..8679a8e 100644 --- a/app/settings/components/update-password.tsx +++ b/app/settings/components/update-password.tsx @@ -6,7 +6,6 @@ import { useForm } from "react-hook-form"; import Alert from "./alert"; import Button from "./button"; import SettingsSection from "./settings-section"; -import useCurrentCustomer from "../../core/hooks/use-current-customer"; import appLogger from "../../../integrations/logger"; @@ -18,7 +17,6 @@ type Form = { }; const UpdatePassword: FunctionComponent = () => { - const customer = useCurrentCustomer(); const router = useRouter(); const { register, diff --git a/app/users/queries/get-current-user.ts b/app/users/queries/get-current-user.ts new file mode 100644 index 0000000..fc5e851 --- /dev/null +++ b/app/users/queries/get-current-user.ts @@ -0,0 +1,32 @@ +import { Ctx } from "blitz"; + +import db from "db"; + +export default async function getCurrentUser(_ = null, { session }: Ctx) { + if (!session.userId) return null; + + return db.user.findFirst({ + where: { id: session.userId }, + select: { + id: true, + name: true, + email: true, + role: true, + memberships: { + include: { + organization: { + select: { + id: true, + encryptionKey: true, + paddleCustomerId: true, + paddleSubscriptionId: true, + twilioAccountSid: true, + twilioAuthToken: true, + twimlAppSid: true, + }, + }, + }, + }, + }, + }); +} diff --git a/db/migrations/20210804092849_add_orgs/migration.sql b/db/migrations/20210804092849_add_orgs/migration.sql new file mode 100644 index 0000000..fa43942 --- /dev/null +++ b/db/migrations/20210804092849_add_orgs/migration.sql @@ -0,0 +1,148 @@ +/* + Warnings: + + - You are about to drop the column `customerId` on the `Message` table. All the data in the column will be lost. + - You are about to drop the column `twilioSid` on the `Message` table. All the data in the column will be lost. + - You are about to drop the column `customerId` on the `NotificationSubscription` table. All the data in the column will be lost. + - You are about to drop the column `customerId` on the `PhoneCall` table. All the data in the column will be lost. + - You are about to drop the column `twilioSid` on the `PhoneCall` table. All the data in the column will be lost. + - You are about to drop the column `customerId` on the `PhoneNumber` table. All the data in the column will be lost. + - You are about to drop the column `phoneNumber` on the `PhoneNumber` table. All the data in the column will be lost. + - You are about to drop the column `phoneNumberSid` on the `PhoneNumber` table. All the data in the column will be lost. + - The `role` column on the `User` table would be dropped and recreated. This will lead to data loss if there is data in the column. + - You are about to drop the `Customer` table. If the table is not empty, all the data it contains will be lost. + - A unique constraint covering the columns `[phoneNumberId,id]` on the table `Message` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[phoneNumberId,id]` on the table `PhoneCall` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[organizationId,id]` on the table `PhoneNumber` will be added. If there are existing duplicate values, this will fail. + - Added the required column `phoneNumberId` to the `Message` table without a default value. This is not possible if the table is not empty. + - Added the required column `organizationId` to the `NotificationSubscription` table without a default value. This is not possible if the table is not empty. + - Added the required column `phoneNumberId` to the `NotificationSubscription` table without a default value. This is not possible if the table is not empty. + - Added the required column `phoneNumberId` to the `PhoneCall` table without a default value. This is not possible if the table is not empty. + - Added the required column `number` to the `PhoneNumber` table without a default value. This is not possible if the table is not empty. + - Added the required column `organizationId` to the `PhoneNumber` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "MembershipRole" AS ENUM ('OWNER', 'ADMIN', 'USER'); + +-- CreateEnum +CREATE TYPE "GlobalRole" AS ENUM ('SUPERADMIN', 'CUSTOMER'); + +-- AlterEnum +ALTER TYPE "MessageStatus" ADD VALUE 'Error'; + +-- DropForeignKey +ALTER TABLE "Customer" DROP CONSTRAINT "Customer_id_fkey"; + +-- DropForeignKey +ALTER TABLE "Message" DROP CONSTRAINT "Message_customerId_fkey"; + +-- DropForeignKey +ALTER TABLE "NotificationSubscription" DROP CONSTRAINT "NotificationSubscription_customerId_fkey"; + +-- DropForeignKey +ALTER TABLE "PhoneCall" DROP CONSTRAINT "PhoneCall_customerId_fkey"; + +-- DropForeignKey +ALTER TABLE "PhoneNumber" DROP CONSTRAINT "PhoneNumber_customerId_fkey"; + +-- AlterTable +ALTER TABLE "Message" DROP COLUMN "customerId", +DROP COLUMN "twilioSid", +ADD COLUMN "phoneNumberId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "NotificationSubscription" DROP COLUMN "customerId", +ADD COLUMN "organizationId" TEXT NOT NULL, +ADD COLUMN "phoneNumberId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "PhoneCall" DROP COLUMN "customerId", +DROP COLUMN "twilioSid", +ADD COLUMN "phoneNumberId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "PhoneNumber" DROP COLUMN "customerId", +DROP COLUMN "phoneNumber", +DROP COLUMN "phoneNumberSid", +ADD COLUMN "number" TEXT NOT NULL, +ADD COLUMN "organizationId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "User" DROP COLUMN "role", +ADD COLUMN "role" "GlobalRole" NOT NULL DEFAULT E'CUSTOMER'; + +-- DropTable +DROP TABLE "Customer"; + +-- CreateTable +CREATE TABLE "TwilioCredentials" ( + "accountSid" TEXT NOT NULL, + "createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMPTZ NOT NULL, + "authToken" TEXT NOT NULL, + "twimlAppSid" TEXT, + "organizationId" TEXT NOT NULL, + + PRIMARY KEY ("accountSid") +); + +-- CreateTable +CREATE TABLE "Organization" ( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMPTZ NOT NULL, + "encryptionKey" TEXT NOT NULL, + "paddleCustomerId" TEXT, + "paddleSubscriptionId" TEXT, + + PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Membership" ( + "id" TEXT NOT NULL, + "role" "MembershipRole" NOT NULL, + "organizationId" TEXT NOT NULL, + "userId" TEXT, + "invitedName" TEXT, + "invitedEmail" TEXT, + + PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Membership.organizationId_invitedEmail_unique" ON "Membership"("organizationId", "invitedEmail"); + +-- CreateIndex +CREATE UNIQUE INDEX "Message.phoneNumberId_id_unique" ON "Message"("phoneNumberId", "id"); + +-- CreateIndex +CREATE UNIQUE INDEX "PhoneCall.phoneNumberId_id_unique" ON "PhoneCall"("phoneNumberId", "id"); + +-- CreateIndex +CREATE UNIQUE INDEX "PhoneNumber.organizationId_id_unique" ON "PhoneNumber"("organizationId", "id"); + +-- AddForeignKey +ALTER TABLE "TwilioCredentials" ADD FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Membership" ADD FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Membership" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Message" ADD FOREIGN KEY ("phoneNumberId") REFERENCES "PhoneNumber"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PhoneCall" ADD FOREIGN KEY ("phoneNumberId") REFERENCES "PhoneNumber"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PhoneNumber" ADD FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "NotificationSubscription" ADD FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "NotificationSubscription" ADD FOREIGN KEY ("phoneNumberId") REFERENCES "PhoneNumber"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/db/migrations/20210805150436_delete_twiliocredentials_entity/migration.sql b/db/migrations/20210805150436_delete_twiliocredentials_entity/migration.sql new file mode 100644 index 0000000..94b03dd --- /dev/null +++ b/db/migrations/20210805150436_delete_twiliocredentials_entity/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - You are about to drop the `TwilioCredentials` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "TwilioCredentials" DROP CONSTRAINT "TwilioCredentials_organizationId_fkey"; + +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "twilioAccountSid" TEXT, +ADD COLUMN "twilioAuthToken" TEXT, +ADD COLUMN "twimlAppSid" TEXT; + +-- DropTable +DROP TABLE "TwilioCredentials"; diff --git a/db/migrations/20210805153229_org_id_wesh/migration.sql b/db/migrations/20210805153229_org_id_wesh/migration.sql new file mode 100644 index 0000000..a1e0ebf --- /dev/null +++ b/db/migrations/20210805153229_org_id_wesh/migration.sql @@ -0,0 +1,36 @@ +/* + Warnings: + + - A unique constraint covering the columns `[organizationId,phoneNumberId,id]` on the table `Message` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[id,twilioAccountSid]` on the table `Organization` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[organizationId,phoneNumberId,id]` on the table `PhoneCall` will be added. If there are existing duplicate values, this will fail. + - Added the required column `organizationId` to the `Message` table without a default value. This is not possible if the table is not empty. + - Added the required column `organizationId` to the `PhoneCall` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropIndex +DROP INDEX "Message.phoneNumberId_id_unique"; + +-- DropIndex +DROP INDEX "PhoneCall.phoneNumberId_id_unique"; + +-- AlterTable +ALTER TABLE "Message" ADD COLUMN "organizationId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "PhoneCall" ADD COLUMN "organizationId" TEXT NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "Message.organizationId_phoneNumberId_id_unique" ON "Message"("organizationId", "phoneNumberId", "id"); + +-- CreateIndex +CREATE UNIQUE INDEX "Organization.id_twilioAccountSid_unique" ON "Organization"("id", "twilioAccountSid"); + +-- CreateIndex +CREATE UNIQUE INDEX "PhoneCall.organizationId_phoneNumberId_id_unique" ON "PhoneCall"("organizationId", "phoneNumberId", "id"); + +-- AddForeignKey +ALTER TABLE "Message" ADD FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PhoneCall" ADD FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/db/schema.prisma b/db/schema.prisma index 81caffc..f2fc041 100644 --- a/db/schema.prisma +++ b/db/schema.prisma @@ -12,18 +12,68 @@ generator client { // -------------------------------------- -model User { - id String @id @default(uuid()) - createdAt DateTime @default(now()) @db.Timestamptz - updatedAt DateTime @updatedAt @db.Timestamptz - name String? - email String @unique - hashedPassword String? - role Role @default(USER) +model Organization { + id String @id @default(uuid()) + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz + encryptionKey String + paddleCustomerId String? + paddleSubscriptionId String? - tokens Token[] - sessions Session[] - customer Customer[] + twilioAccountSid String? + twilioAuthToken String? // TODO: encrypt it with encryptionKey + twimlAppSid String? + + memberships Membership[] + phoneNumbers PhoneNumber[] + notificationSubscriptions NotificationSubscription[] + messages Message[] + phoneCalls PhoneCall[] + + @@unique([id, twilioAccountSid]) +} + +model Membership { + id String @id @default(uuid()) + role MembershipRole + + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId String + + user User? @relation(fields: [userId], references: [id]) + userId String? + + // When the user joins, we will clear out the name and email and set the user. + invitedName String? + invitedEmail String? + + @@unique([organizationId, invitedEmail]) +} + +enum MembershipRole { + OWNER + ADMIN + USER +} + +// The owners of the SaaS (you) can have a SUPERADMIN role to access all data +enum GlobalRole { + SUPERADMIN + CUSTOMER +} + +model User { + id String @id @default(uuid()) + createdAt DateTime @default(now()) @db.Timestamptz + updatedAt DateTime @updatedAt @db.Timestamptz + name String? + email String @unique + hashedPassword String? + role GlobalRole @default(CUSTOMER) + + memberships Membership[] + tokens Token[] + sessions Session[] } enum Role { @@ -65,25 +115,6 @@ enum TokenType { RESET_PASSWORD } -model Customer { - id String @id - createdAt DateTime @default(now()) @db.Timestamptz - updatedAt DateTime @updatedAt @db.Timestamptz - encryptionKey String - accountSid String? - authToken String? - // TODO: encrypt it with encryptionKey - twimlAppSid String? - paddleCustomerId String? - paddleSubscriptionId String? - - user User @relation(fields: [id], references: [id]) - messages Message[] - phoneCalls PhoneCall[] - phoneNumbers PhoneNumber[] - notificationSubscriptions NotificationSubscription[] -} - model Message { id String @id @default(uuid()) sentAt DateTime @db.Timestamptz @@ -92,10 +123,13 @@ model Message { to String direction Direction status MessageStatus - twilioSid String? - customer Customer @relation(fields: [customerId], references: [id]) - customerId String + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId String + phoneNumber PhoneNumber @relation(fields: [phoneNumberId], references: [id]) + phoneNumberId String + + @@unique([organizationId, phoneNumberId, id]) } enum Direction { @@ -117,20 +151,25 @@ enum MessageStatus { Read PartiallyDelivered Canceled + + Error } model PhoneCall { - id String @id @default(uuid()) + id String @id createdAt DateTime @default(now()) @db.Timestamptz - twilioSid String from String to String status CallStatus direction Direction duration String - customer Customer @relation(fields: [customerId], references: [id]) - customerId String + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId String + phoneNumber PhoneNumber @relation(fields: [phoneNumberId], references: [id]) + phoneNumberId String + + @@unique([organizationId, phoneNumberId, id]) } enum CallStatus { @@ -145,13 +184,18 @@ enum CallStatus { } model PhoneNumber { - id String @id @default(uuid()) - createdAt DateTime @default(now()) @db.Timestamptz - phoneNumberSid String - phoneNumber String + id String @id + createdAt DateTime @default(now()) @db.Timestamptz + number String - customer Customer @relation(fields: [customerId], references: [id]) - customerId String + messages Message[] + phoneCalls PhoneCall[] + notificationSubscriptions NotificationSubscription[] + + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId String + + @@unique([organizationId, id]) } model NotificationSubscription { @@ -163,6 +207,8 @@ model NotificationSubscription { keys_p256dh String keys_auth String - customer Customer @relation(fields: [customerId], references: [id]) - customerId String + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId String + phoneNumber PhoneNumber @relation(fields: [phoneNumberId], references: [id]) + phoneNumberId String } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b5f0ae9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.7" + +services: + db: + image: postgres:13-alpine + restart: unless-stopped + volumes: + - data:/var/lib/postgresql/data + env_file: ./.env.local #Here we are using the already existing .env.local file + ports: + - "5432:5432" + admin: + image: adminer + restart: unless-stopped + depends_on: + - db + ports: + - 8080:8080 + +volumes: + data: diff --git a/package-lock.json b/package-lock.json index d9635da..3185777 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1494,16 +1494,6 @@ "minimist": "^1.2.0" } }, - "@datadog/sketches-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@datadog/sketches-js/-/sketches-js-1.0.2.tgz", - "integrity": "sha512-kX23kHXT66t/DjBItHZ+NgszceyuYuPjpiuWZIDzZ+APy6GL7aPjfNKMb9yA2GwZlKTcK/j5cO8GE/2T8KvDiQ==", - "requires": { - "math-float64-frexp": "^1.0.0", - "math-float64-ldexp": "^1.0.1", - "protobufjs": "^6.10.2" - } - }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -1544,14 +1534,6 @@ } } }, - "@fastify/ajv-compiler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-1.1.0.tgz", - "integrity": "sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg==", - "requires": { - "ajv": "^6.12.6" - } - }, "@fortawesome/fontawesome-common-types": { "version": "0.2.35", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz", @@ -1622,9 +1604,9 @@ } }, "@fortawesome/react-fontawesome": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz", - "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.15.tgz", + "integrity": "sha512-/HFHdcoLESxxMkqZAcZ6RXDJ69pVApwdwRos/B2kiMWxDSAX2dFK8Er2/+rG+RsrzWB/dsAyjefLmemgmfE18g==", "requires": { "prop-types": "^15.7.2" } @@ -2649,11 +2631,11 @@ "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" }, "@prisma/client": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.27.0.tgz", - "integrity": "sha512-Sh2b1M8MGbOHbwG1FEqdWTUCrEX3p7gt2e7gpaBWou8yTIJvP1UZ4YlHgpuUcR1q4pEIR/JTZJeQk2l4iDyRBQ==", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.28.0.tgz", + "integrity": "sha512-iwdxpy0Nz8N40MnhdlRvhZOBk8+GawpEsY5FU8Tfw1k9rvIeTAi+wBHrqhY8bXq6pneZkzrdQ1Hj3tqkrbRmoQ==", "requires": { - "@prisma/engines-version": "2.27.0-43.cdba6ec525e0213cce26f8e4bb23cf556d1479bb" + "@prisma/engines-version": "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27" } }, "@prisma/debug": { @@ -2712,9 +2694,9 @@ "integrity": "sha512-rEWpaG7wZvPuWJC5SwkBB/Iwue//oC5yv58Mse7r+ibtgkA7vGdWc1bFDQ32DT9tDL5WSC6bBwqEASGV/1Gm1Q==" }, "@prisma/engines-version": { - "version": "2.27.0-43.cdba6ec525e0213cce26f8e4bb23cf556d1479bb", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.27.0-43.cdba6ec525e0213cce26f8e4bb23cf556d1479bb.tgz", - "integrity": "sha512-pwOsYdzw8+cwKlUrCzasiRh96RhNuJ/QcKr0HwjxxlUWTmbEayDKjqRRz5fsUYIpSv5fW1B3SsbzHOqVtFZ6XQ==" + "version": "2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.28.0-17.89facabd0366f63911d089156a7a70125bfbcd27.tgz", + "integrity": "sha512-BWTvF1mGxjG8EtG215uhxdeW5Uf5aiH4xhfzcFPFC3Ux5BdEM1uEBrLIixX67mI+ZNhqNZSBPf0DSf2I1IsaZw==" }, "@prisma/fetch-engine": { "version": "2.19.0", @@ -2812,82 +2794,6 @@ } } }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" - }, - "@quirrel/ioredis-mock": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@quirrel/ioredis-mock/-/ioredis-mock-5.6.1.tgz", - "integrity": "sha512-S66lITLabNKjBA/QqCrAJDU4mbeKvwmGJuyiQlnQjdw5tTBRZLZ8uKJo3/DIDT5IGTgYJz0aTPX7CyXtq8pbEA==", - "requires": { - "fengari": "^0.1.4", - "fengari-interop": "^0.1.2", - "lodash": "^4.17.21", - "standard-as-callback": "^2.1.0" - } - }, - "@quirrel/owl": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@quirrel/owl/-/owl-0.14.0.tgz", - "integrity": "sha512-GSm4ZzPKuSpG9Pxk7f+8tI7SBR9BOK07L4G3CisEIZwhz4/I/yqIb+RmftqIZxtRp3bbFQrE7O7MRGJcEAIHdA==", - "requires": { - "ioredis": "^4.27.1", - "ioredis-mock": "^5.5.6", - "opentracing": "^0.14.5", - "pino": "^6.11.3" - } - }, "@reach/portal": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.13.2.tgz", @@ -3113,122 +3019,6 @@ } } }, - "@sentry/core": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.10.0.tgz", - "integrity": "sha512-5KlxHJlbD7AMo+b9pMGkjxUOfMILtsqCtGgI7DMvZNfEkdohO8QgUY+hPqr540kmwArFS91ipQYWhqzGaOhM3Q==", - "requires": { - "@sentry/hub": "6.10.0", - "@sentry/minimal": "6.10.0", - "@sentry/types": "6.10.0", - "@sentry/utils": "6.10.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/hub": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.10.0.tgz", - "integrity": "sha512-MV8wjhWiFAXZAhmj7Ef5QdBr2IF93u8xXiIo2J+dRZ7eVa4/ZszoUiDbhUcl/TPxczaw4oW2a6tINBNFLzXiig==", - "requires": { - "@sentry/types": "6.10.0", - "@sentry/utils": "6.10.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/minimal": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.10.0.tgz", - "integrity": "sha512-yarm046UgUFIBoxqnBan2+BEgaO9KZCrLzsIsmALiQvpfW92K1lHurSawl5W6SR7wCYBnNn7CPvPE/BHFdy4YA==", - "requires": { - "@sentry/hub": "6.10.0", - "@sentry/types": "6.10.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/node": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.10.0.tgz", - "integrity": "sha512-buGmOjsTnxebHSfa3r/rhpjDk8xmrILG4xslTgV1C2JpbUtf96QnYNNydfsfAGcZrLWO0gid/wigxsx1fdXT8A==", - "requires": { - "@sentry/core": "6.10.0", - "@sentry/hub": "6.10.0", - "@sentry/tracing": "6.10.0", - "@sentry/types": "6.10.0", - "@sentry/utils": "6.10.0", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/tracing": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.10.0.tgz", - "integrity": "sha512-jZj6Aaf8kU5wgyNXbAJHosHn8OOFdK14lgwYPb/AIDsY35g9a9ncTOqIOBp8X3KkmSR8lcBzAEyiUzCxAis2jA==", - "requires": { - "@sentry/hub": "6.10.0", - "@sentry/minimal": "6.10.0", - "@sentry/types": "6.10.0", - "@sentry/utils": "6.10.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.10.0.tgz", - "integrity": "sha512-M7s0JFgG7/6/yNVYoPUbxzaXDhnzyIQYRRJJKRaTD77YO4MHvi4Ke8alBWqD5fer0cPIfcSkBqa9BLdqRqcMWw==" - }, - "@sentry/utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.10.0.tgz", - "integrity": "sha512-F9OczOcZMFtazYVZ6LfRIe65/eOfQbiAedIKS0li4npuMz0jKYRbxrjd/U7oLiNQkPAp4/BujU4m1ZIwq6a+tg==", - "requires": { - "@sentry/types": "6.10.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, "@sindresorhus/is": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", @@ -3545,11 +3335,6 @@ "@types/node": "*" } }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, "@types/mdast": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.7.tgz", @@ -3947,11 +3732,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" }, - "abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, "acorn": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", @@ -4099,11 +3879,6 @@ "buffer-equal": "^1.0.0" } }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" - }, "archiver": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/archiver/-/archiver-4.0.2.tgz", @@ -4164,11 +3939,6 @@ } } }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, "arg": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", @@ -4439,17 +4209,6 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==" }, - "avvio": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-7.2.2.tgz", - "integrity": "sha512-XW2CMCmZaCmCCsIaJaLKxAzPwF37fXi1KGxNOvedOpeisLdmxZnblGc3hpHWYnlP+KOUxZsazh43WXNHgXpbqw==", - "requires": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1", - "queue-microtask": "^1.1.2" - } - }, "axe-core": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.2.tgz", @@ -4721,21 +4480,6 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, "better-path-resolve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", @@ -4806,79 +4550,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - } - } - }, "body-scroll-lock": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz", @@ -4889,87 +4560,6 @@ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz", "integrity": "sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw==" }, - "boxen": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.3.1.tgz", - "integrity": "sha1-p9iYJDrmIvertrtgTXQKdsalRhs=", - "requires": { - "chalk": "^1.1.1", - "filled-array": "^1.0.0", - "object-assign": "^4.0.1", - "repeating": "^2.0.0", - "string-width": "^1.0.1", - "widest-line": "^1.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "widest-line": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", - "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", - "requires": { - "string-width": "^1.0.1" - } - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5151,38 +4741,6 @@ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -5270,11 +4828,6 @@ "rsvp": "^4.8.4" } }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" - }, "cardinal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", @@ -5332,11 +4885,6 @@ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.0.tgz", "integrity": "sha512-pE3Z15lLRxDzWJy7bBHBopRwfI20sbrMVLQTC7xsPglCHf4Wv1e167OgYAFP78co2XlhojDyAqA+IAJse27//g==" }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" - }, "checkpoint-client": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/checkpoint-client/-/checkpoint-client-1.1.19.tgz", @@ -5707,21 +5255,11 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" }, - "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -5870,46 +5408,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "concurrently": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.2.0.tgz", @@ -5970,78 +5468,6 @@ } } }, - "conditional-type-checks": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/conditional-type-checks/-/conditional-type-checks-1.0.5.tgz", - "integrity": "sha512-DkfkvmjXVe4ye4llJ1JADtO3dNvqqcQM08cA9BhNt9Oe8pyRW8X1CZyBg9Qst05bDV9BJM01KLmnFh78NcJgNg==" - }, - "configstore": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", - "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=", - "requires": { - "dot-prop": "^3.0.0", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "object-assign": "^4.0.1", - "os-tmpdir": "^1.0.0", - "osenv": "^0.1.0", - "uuid": "^2.0.1", - "write-file-atomic": "^1.1.2", - "xdg-basedir": "^2.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" - }, - "write-file-atomic": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", - "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "slide": "^1.1.5" - } - } - } - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -6055,24 +5481,6 @@ "simple-wcswidth": "^1.0.1" } }, - "const-ninf-float64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/const-ninf-float64/-/const-ninf-float64-1.0.0.tgz", - "integrity": "sha1-2eTI073RNgPpi5Ory+OPem23MY0=" - }, - "const-pinf-float64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/const-pinf-float64/-/const-pinf-float64-1.0.0.tgz", - "integrity": "sha1-9u+w15+cCYbT558pI6v5twtj1yY=" - }, - "const-smallest-float64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/const-smallest-float64/-/const-smallest-float64-1.0.0.tgz", - "integrity": "sha1-twZoQYDJ2H/g8/lG1gx0fh5UlH4=", - "requires": { - "utils-define-read-only-property": "^1.0.0" - } - }, "constantinople": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", @@ -6088,26 +5496,6 @@ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -6243,14 +5631,6 @@ } } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -6281,23 +5661,6 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, - "cron-parser": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", - "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", - "requires": { - "is-nan": "^1.3.2", - "luxon": "^1.26.0" - } - }, - "cross-fetch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", - "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", - "requires": { - "node-fetch": "2.6.1" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6336,11 +5699,6 @@ } } }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" - }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -6506,66 +5864,6 @@ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz", "integrity": "sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw==" }, - "dd-trace": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/dd-trace/-/dd-trace-1.1.1.tgz", - "integrity": "sha512-zqGZv+qSHkSq0YgWBjxHOSS1j9U7VFmfHIvIsen6qDTXas5Db23rkNb+kiL0gsEEhoMs2VJ3/O3N8GsSWEaG8Q==", - "requires": { - "@datadog/sketches-js": "^1.0.2", - "@types/node": "^10.12.18", - "form-data": "^3.0.0", - "koalas": "^1.0.2", - "limiter": "^1.1.4", - "lodash.kebabcase": "^4.1.1", - "lodash.pick": "^4.4.0", - "lodash.sortby": "^4.7.0", - "lodash.uniq": "^4.5.0", - "methods": "^1.1.2", - "module-details-from-path": "^1.0.3", - "multer": "^1.4.2", - "nan": "^2.12.1", - "node-gyp-build": "^3.8.0", - "opentracing": ">=0.12.1", - "path-to-regexp": "^0.1.2", - "performance-now": "^2.1.0", - "protobufjs": "^6.9.0", - "semver": "^5.5.0", - "shimmer": "1.2.1", - "source-map": "^0.7.3", - "source-map-resolve": "^0.6.0" - }, - "dependencies": { - "@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" - }, - "node-gyp-build": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", - "integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - }, - "source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - } - } - }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -6638,11 +5936,6 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -6713,11 +6006,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6732,11 +6020,6 @@ "minimalistic-assert": "^1.0.0" } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -6781,38 +6064,6 @@ "minimist": "^1.1.1" } }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -6934,14 +6185,6 @@ "domhandler": "^4.2.0" } }, - "dot-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", - "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", - "requires": { - "is-obj": "^1.0.0" - } - }, "dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", @@ -6960,43 +6203,6 @@ "dotenv": "^8.0.0" } }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "requires": { - "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", @@ -7008,22 +6214,6 @@ "stream-shift": "^1.0.0" } }, - "easy-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", - "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", - "requires": { - "ansi-regex": "^3.0.0", - "wcwidth": ">=1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - } - } - }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -7032,11 +6222,6 @@ "safe-buffer": "^5.0.1" } }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, "ejs": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz", @@ -7086,11 +6271,6 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, "encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -7105,11 +6285,6 @@ "integrity": "sha512-bd/DFLAoJetvv7ar/KIpE3CNO8wEuyrt9Xuw6nSMiZ+Vrz/Q21BPsMHvARL2Wz6IKHKXgb+DWZqtRg1vql9cBg==", "dev": true }, - "encoding-negotiator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/encoding-negotiator/-/encoding-negotiator-2.0.1.tgz", - "integrity": "sha512-GSK7qphNR4iPcejfAlZxKDoz3xMhnspwImK+Af5WhePS9jUpK/Oh7rUdyENWu+9rgDflOCTmAojBsgsvM8neAQ==" - }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -7204,11 +6379,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -7974,11 +7144,6 @@ "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-2.0.0.tgz", "integrity": "sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ==" }, - "fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8001,17 +7166,6 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "fast-json-stringify": { - "version": "2.7.8", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-2.7.8.tgz", - "integrity": "sha512-HRSGwEWe0/5EH7GEaWg1by4dInnBb1WFf4umMPr+lL5xb0VP0VbpNGklp4L0/BseD+BmtIZpjqJjnLFwaQ21dg==", - "requires": { - "ajv": "^6.11.0", - "deepmerge": "^4.2.2", - "rfdc": "^1.2.0", - "string-similarity": "^4.0.1" - } - }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -8032,121 +7186,6 @@ "resolved": "https://registry.npmjs.org/fast-write-atomic/-/fast-write-atomic-0.2.1.tgz", "integrity": "sha512-WvJe06IfNYlr+6cO3uQkdKdy3Cb1LlCJSF8zRs2eT8yuhdbSlR9nIt+TgQ92RUxiRrQm+/S7RARnMfCs5iuAjw==" }, - "fastify": { - "version": "3.19.2", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-3.19.2.tgz", - "integrity": "sha512-s9naCdC0V1ynEzxMoe/0oX8XgcLk90VAnIms4z6KcF7Rpn1XiguoMyZSviTmv1x5rgy/OjGGBM45sNpMoBzCUQ==", - "requires": { - "@fastify/ajv-compiler": "^1.0.0", - "abstract-logging": "^2.0.0", - "avvio": "^7.1.2", - "fast-json-stringify": "^2.5.2", - "fastify-error": "^0.3.0", - "fastify-warning": "^0.2.0", - "find-my-way": "^4.0.0", - "flatstr": "^1.0.12", - "light-my-request": "^4.2.0", - "pino": "^6.13.0", - "proxy-addr": "^2.0.7", - "readable-stream": "^3.4.0", - "rfdc": "^1.1.4", - "secure-json-parse": "^2.0.0", - "semver": "^7.3.2", - "tiny-lru": "^7.0.0" - } - }, - "fastify-basic-auth": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fastify-basic-auth/-/fastify-basic-auth-2.1.0.tgz", - "integrity": "sha512-2ZLFjozJgOOpoOkqFpclOqrwoQGua2JNu+pMoAfhtnhehuIseGO9bUg1lBSwC+3WU53ebDMHmc65SYvPBhxBGQ==", - "requires": { - "basic-auth": "^2.0.1", - "fastify-plugin": "^3.0.0", - "http-errors": "^1.7.3" - } - }, - "fastify-blipp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fastify-blipp/-/fastify-blipp-3.1.0.tgz", - "integrity": "sha512-9tmQ2ljvUKOHKSRPMx1WhkvkZrinBc9y/EwU5THO27TDB+2haZ1BfGuGB4Dsb2R6CVGpuEjh8+dneV0CfLspQw==", - "requires": { - "chalk": "^4.1.1", - "fastify-plugin": "^3.0.0" - } - }, - "fastify-cors": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/fastify-cors/-/fastify-cors-6.0.2.tgz", - "integrity": "sha512-sE0AOyzmj5hLLRRVgenjA6G2iOGX35/1S3QGYB9rr9TXelMZB3lFrXy4CzwYVOMiujJeMiLgO4J7eRm8sQSv8Q==", - "requires": { - "fastify-plugin": "^3.0.0", - "vary": "^1.1.2" - } - }, - "fastify-error": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/fastify-error/-/fastify-error-0.3.1.tgz", - "integrity": "sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ==" - }, - "fastify-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.0.tgz", - "integrity": "sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w==" - }, - "fastify-static": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/fastify-static/-/fastify-static-4.2.2.tgz", - "integrity": "sha512-C631EuGdMlUzFXuuP4SqXBoQEiit9S0uYRd97cF2mFhgStvZvQKIjtzUk/GMQu6EfEdm0ddj3UAc0C6dVeNyKA==", - "requires": { - "content-disposition": "^0.5.3", - "encoding-negotiator": "^2.0.1", - "fastify-plugin": "^3.0.0", - "glob": "^7.1.4", - "readable-stream": "^3.4.0", - "send": "^0.17.1" - } - }, - "fastify-swagger": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/fastify-swagger/-/fastify-swagger-4.8.4.tgz", - "integrity": "sha512-kiQ/LBAvDbm/DfM8vCkGrBSKHvVO5615zufK4gzLHFF9e5rEegu0sOCqwkqoSblYQcpijn2/lwDWHjPRIUeuPg==", - "requires": { - "fastify-plugin": "^3.0.0", - "fastify-static": "^4.0.0", - "js-yaml": "^4.0.0", - "json-schema-resolver": "^1.2.0", - "openapi-types": "^9.1.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - } - } - }, - "fastify-warning": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz", - "integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw==" - }, - "fastify-websocket": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fastify-websocket/-/fastify-websocket-3.2.0.tgz", - "integrity": "sha512-O1I04Y6aV2rkO+RRMYQzIe2h7omA20p+Nk/HAdJmfHvNErY7OCani06+rDMmQCKYbZT6RWrsy13U+GrJPhSO6Q==", - "requires": { - "fastify-plugin": "^3.0.0", - "ws": "^7.4.2" - } - }, "fastq": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", @@ -8163,36 +7202,6 @@ "bser": "2.1.1" } }, - "fengari": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/fengari/-/fengari-0.1.4.tgz", - "integrity": "sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==", - "requires": { - "readline-sync": "^1.4.9", - "sprintf-js": "^1.1.1", - "tmp": "^0.0.33" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - } - } - }, - "fengari-interop": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/fengari-interop/-/fengari-interop-0.1.2.tgz", - "integrity": "sha512-8iTvaByZVoi+lQJhHH9vC+c/Yaok9CwOqNQZN6JrVpjmWwW4dDkeblBXhnHC+BoI6eF4Cy5NKW3z6ICEjvgywQ==" - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -8218,40 +7227,6 @@ "to-regex-range": "^5.0.1" } }, - "filled-array": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", - "integrity": "sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q=" - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "find-cache-dir": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", @@ -8305,17 +7280,6 @@ } } }, - "find-my-way": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-4.3.3.tgz", - "integrity": "sha512-5E4bRdaATB1MewjOCBjx4xvD205a4t2ripCnXB+YFhYEJ0NABtrcC7XLXLq0TPoFe/WYGUFqys3Qk3HCOGeNcw==", - "requires": { - "fast-decode-uri-component": "^1.0.1", - "fast-deep-equal": "^3.1.3", - "safe-regex2": "^2.0.0", - "semver-store": "^0.3.0" - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8435,11 +7399,6 @@ "mime-types": "^2.1.12" } }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, "fraction.js": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", @@ -8453,11 +7412,6 @@ "map-cache": "^0.2.2" } }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -8869,21 +7823,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } - } - }, "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", @@ -9354,46 +8293,6 @@ "side-channel": "^1.0.4" } }, - "ioredis": { - "version": "4.27.6", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.6.tgz", - "integrity": "sha512-6W3ZHMbpCa8ByMyC1LJGOi7P2WiOKP9B3resoZOVLDhi+6dDBOW+KNsRq3yI36Hmnb2sifCxHX+YSarTeXh48A==", - "requires": { - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "dependencies": { - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - } - } - }, - "ioredis-mock": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/ioredis-mock/-/ioredis-mock-5.6.0.tgz", - "integrity": "sha512-Ow+tyKdijg/gA2gSEv7lq8dLp6bO7FnwDXbJ9as37NF23XNRGMLzBc7ITaqMydfrbTodWnLcE2lKEaBs7SBpyA==", - "requires": { - "fengari": "^0.1.4", - "fengari-interop": "^0.1.2", - "lodash": "^4.17.21", - "standard-as-callback": "^2.1.0" - } - }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" - }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -9573,11 +8472,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -9635,11 +8529,6 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9707,11 +8596,6 @@ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "dev": true }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" - }, "is-regex": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", @@ -9734,11 +8618,6 @@ "is-unc-path": "^1.0.0" } }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -10829,16 +9708,6 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, - "json-schema-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-1.2.2.tgz", - "integrity": "sha512-sW4b4BDJzYiKpJind7l1JtH3P1yn43vCv3w51YR2Ixse5rXr006TL10gM0Ek54pET6vxwiWq5RQuIMgmH9YrrQ==", - "requires": { - "debug": "^4.1.1", - "rfdc": "^1.1.4", - "uri-js": "^4.2.2" - } - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10959,11 +9828,6 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" }, - "koalas": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/koalas/-/koalas-1.0.2.tgz", - "integrity": "sha1-MYQz8HQjXbePrlZhoCqMpT7ilc0=" - }, "language-subtag-registry": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", @@ -10977,14 +9841,6 @@ "language-subtag-registry": "~0.3.2" } }, - "latest-version": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", - "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=", - "requires": { - "package-json": "^2.0.0" - } - }, "lazystream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", @@ -11117,28 +9973,11 @@ "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=", "dev": true }, - "light-my-request": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-4.4.1.tgz", - "integrity": "sha512-FDNRF2mYjthIRWE7O8d/X7AzDx4otQHl4/QXbu3Q/FRwBFcgb+ZoDaUd5HwN53uQXLAiw76osN+Va0NEaOW6rQ==", - "requires": { - "ajv": "^6.12.2", - "cookie": "^0.4.0", - "fastify-warning": "^0.2.0", - "readable-stream": "^3.6.0", - "set-cookie-parser": "^2.4.1" - } - }, "lilconfig": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==" }, - "limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" - }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -11304,7 +10143,8 @@ "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true }, "lodash.debounce": { "version": "4.0.8", @@ -11361,11 +10201,6 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=" - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -11376,11 +10211,6 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -11474,11 +10304,6 @@ } } }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "longest-streak": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.0.0.tgz", @@ -11505,16 +10330,6 @@ "yallist": "^4.0.0" } }, - "lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" - }, - "luxon": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", - "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" - }, "lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", @@ -11644,100 +10459,6 @@ "escape-string-regexp": "^4.0.0" } }, - "math-abs": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/math-abs/-/math-abs-1.0.2.tgz", - "integrity": "sha1-j7JnXZaTJ6YaYpgh/CPkH6rFxNM=" - }, - "math-float64-copysign": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-copysign/-/math-float64-copysign-1.0.0.tgz", - "integrity": "sha1-iuyF0o2Bx6I8qwdIojxfAZFpZvY=", - "requires": { - "math-float64-from-words": "^1.0.0", - "math-float64-get-high-word": "^1.0.0", - "math-float64-to-words": "^1.0.0" - } - }, - "math-float64-exponent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-exponent/-/math-float64-exponent-1.0.0.tgz", - "integrity": "sha1-2Ik+g19eZYEq3Wznq6/uk9qnXKU=", - "requires": { - "math-float64-get-high-word": "^1.0.0" - } - }, - "math-float64-frexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-frexp/-/math-float64-frexp-1.0.0.tgz", - "integrity": "sha1-zKlg1PzM/zAYkJ+GF0mUhSET1QU=", - "requires": { - "const-ninf-float64": "^1.0.0", - "const-pinf-float64": "^1.0.0", - "math-float64-exponent": "^1.0.0", - "math-float64-from-words": "^1.0.0", - "math-float64-normalize": "^1.0.0", - "math-float64-to-words": "^1.0.0" - } - }, - "math-float64-from-words": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-from-words/-/math-float64-from-words-1.0.0.tgz", - "integrity": "sha1-lGSPk3f4EoqK3VSFbLqVH26QoTk=", - "requires": { - "utils-is-little-endian": "^1.0.0" - } - }, - "math-float64-get-high-word": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-get-high-word/-/math-float64-get-high-word-1.0.0.tgz", - "integrity": "sha1-n7cQfjZlhdW7Dv3cA2+UfJZmYRo=", - "requires": { - "utils-is-little-endian": "^1.0.0" - } - }, - "math-float64-ldexp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-float64-ldexp/-/math-float64-ldexp-1.0.1.tgz", - "integrity": "sha1-6AdtqimmUppXIAL6n9nj48UiU9o=", - "requires": { - "const-ninf-float64": "^1.0.0", - "const-pinf-float64": "^1.0.0", - "math-float64-copysign": "^1.0.0", - "math-float64-exponent": "^1.0.0", - "math-float64-from-words": "^1.0.0", - "math-float64-normalize": "^1.0.0", - "math-float64-to-words": "^1.0.0" - } - }, - "math-float64-normalize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-normalize/-/math-float64-normalize-1.0.0.tgz", - "integrity": "sha1-2LujCATfF3vx129kExUwH6EmAHc=", - "requires": { - "const-smallest-float64": "^1.0.0", - "math-abs": "^1.0.2", - "validate.io-infinite": "^1.0.0" - } - }, - "math-float64-to-words": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-float64-to-words/-/math-float64-to-words-1.0.0.tgz", - "integrity": "sha1-Yk69AMf/h7K7y6biMPkE6Kg+Bh4=", - "requires": { - "utils-is-little-endian": "^1.0.0" - } - }, - "md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -11811,11 +10532,6 @@ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -11873,11 +10589,6 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, "micromark": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.3.tgz", @@ -12104,11 +10815,6 @@ } } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, "mime-db": { "version": "1.49.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", @@ -12211,11 +10917,6 @@ "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==" }, - "module-details-from-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" - }, "moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -12231,31 +10932,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, "multimatch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", @@ -12268,11 +10944,6 @@ "minimatch": "^3.0.4" } }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" - }, "nano-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", @@ -12778,11 +11449,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" }, - "node-status-codes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", - "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=" - }, "nodemailer": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz", @@ -12872,11 +11538,6 @@ "schema-utils": "^3.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", @@ -13007,14 +11668,6 @@ "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, "on-headers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", @@ -13036,26 +11689,6 @@ "mimic-fn": "^2.1.0" } }, - "open": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", - "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "openapi-types": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.1.0.tgz", - "integrity": "sha512-mhXh8QN8sbErlxfxBeZ/pzgvmDn443p8CXlxwGSi2bWANZAFvjLPI0PoGjqHW+JdBbXg6uvmvM81WXaweh/SVA==" - }, - "opentracing": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.5.tgz", - "integrity": "sha512-XLKtEfHxqrWyF1fzxznsv78w3csW41ucHnjiKnfzZLD5FN8UBDZZL1i4q0FR29zjxXhm+2Hop+5Vr/b8tKIvEg==" - }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -13127,11 +11760,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, "os-name": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", @@ -13141,20 +11769,6 @@ "windows-release": "^4.0.0" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -13249,91 +11863,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, - "package-json": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", - "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", - "requires": { - "got": "^5.0.0", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - }, - "dependencies": { - "got": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", - "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", - "requires": { - "create-error-class": "^3.0.1", - "duplexer2": "^0.1.4", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "node-status-codes": "^1.0.0", - "object-assign": "^4.0.1", - "parse-json": "^2.1.0", - "pinkie-promise": "^2.0.0", - "read-all-stream": "^3.0.0", - "readable-stream": "^2.0.5", - "timed-out": "^3.0.0", - "unzip-response": "^1.0.2", - "url-parse-lax": "^1.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -13437,11 +11966,6 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -13529,11 +12053,6 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -13556,11 +12075,6 @@ "sha.js": "^2.4.8" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -13690,24 +12204,11 @@ } } }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" - }, "platform": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, - "plausible-telemetry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/plausible-telemetry/-/plausible-telemetry-0.1.0.tgz", - "integrity": "sha512-wz3RTCMXGV54ilCkWRcI1gbiko8j/qH/A6402ScFUM5XY2rnqAX/Prc+i/lvHwLORL2ZLdCWvngvOpXCmmkRHQ==", - "requires": { - "cross-fetch": "^3.0.6" - } - }, "please-upgrade-node": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", @@ -13796,11 +12297,6 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, "prettier": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", @@ -13808,9 +12304,9 @@ "dev": true }, "prettier-plugin-prisma": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-prisma/-/prettier-plugin-prisma-0.14.0.tgz", - "integrity": "sha512-NNFDETaD7bzwl+xJTRWp5zGefeMq1++ngebzyKxo7HJrE14mK7blgMTTgFB7LiitUkpjd0dJyu3NIfLgsejumQ==", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-prisma/-/prettier-plugin-prisma-2.28.0.tgz", + "integrity": "sha512-1wurRMbBQa8S+Lx/4JRRcU/Cgk38rqsIZAHDm4HE4wx/8n/91KCo6IYxU2n3hFbbjI0X7zvkaLIbZFyTMfJJPg==", "dev": true }, "pretty-bytes": { @@ -14060,42 +12556,6 @@ "xtend": "^4.0.0" } }, - "protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - } - } - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -14297,11 +12757,6 @@ "weak-map": "^1.0.5" } }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, "querystring": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", @@ -14340,123 +12795,6 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" }, - "quirrel": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/quirrel/-/quirrel-1.6.3.tgz", - "integrity": "sha512-CVEr79zjHSi0MsBLjTTy8+M6EKfx+W88XCEbz1jxuJRXl2mXuZIxfg/VCc1exCp8D/2zYgqeIgXWsDPa3Lu06Q==", - "requires": { - "@babel/parser": "^7.14.7", - "@babel/traverse": "^7.14.7", - "@quirrel/ioredis-mock": "^5.6.1", - "@quirrel/owl": "^0.14.0", - "@sentry/node": "6.10.0", - "@sentry/tracing": "6.10.0", - "basic-auth": "2.0.1", - "body-parser": "1.19.0", - "chalk": "4.1.1", - "chokidar": "3.5.2", - "commander": "^8.0.0", - "conditional-type-checks": "1.0.5", - "connect": "3.7.0", - "cron-parser": "3.5.0", - "cross-fetch": "^3.1.4", - "cross-spawn": "7.0.3", - "dd-trace": "^1.0.0", - "easy-table": "1.1.1", - "expand-tilde": "2.0.2", - "fast-glob": "3.2.7", - "fastify": "3.19.2", - "fastify-basic-auth": "2.1.0", - "fastify-blipp": "3.1.0", - "fastify-cors": "6.0.2", - "fastify-plugin": "3.0.0", - "fastify-static": "^4.2.2", - "fastify-swagger": "^4.5.0", - "fastify-websocket": "3.2.0", - "ioredis": "4.27.6", - "ipaddr.js": "^2.0.1", - "js-yaml": "^4.1.0", - "jsonwebtoken": "^8.5.1", - "ms": "2.1.3", - "node-fetch": "^2.6.1", - "open": "8.2.1", - "opentracing": "^0.14.5", - "parse-gitignore": "1.0.1", - "pino": "6.13.0", - "plausible-telemetry": "0.1.0", - "secure-e2ee": "0.4.0", - "secure-webhooks": "^0.3.0", - "superjson": "^1.7.3", - "uuid": "^8.3.2", - "zod": "^3.0.0-alpha.29" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "commander": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.1.0.tgz", - "integrity": "sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "superjson": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.7.5.tgz", - "integrity": "sha512-AHuFroOcMTK6LdG/irwXIHwH6Gof5nh42iywnhhf7hMZ6UJqFDRtJ82ViJg14UX3AG8vWRf4Dh3oPIJcqu16Nw==", - "requires": { - "debug": "^4.3.1", - "lodash.clonedeep": "^4.5.0" - } - } - } - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -14474,11 +12812,6 @@ "safe-buffer": "^5.1.0" } }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, "raw-body": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", @@ -14500,29 +12833,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - } - } - }, "react": { "version": "18.0.0-alpha-6f3fcbd6f-20210730", "resolved": "https://registry.npmjs.org/react/-/react-18.0.0-alpha-6f3fcbd6f-20210730.tgz", @@ -14654,44 +12964,6 @@ "resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-9.1.3.tgz", "integrity": "sha512-CdqA2SmS/fj3kkS2W8ZU8wjTbVBAIwDWaRprX7OKaj7HlGwBasGEFggmk5qNklknqk9zK/h8D355bEJFTpqEMg==" }, - "read-all-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", - "integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=", - "requires": { - "pinkie-promise": "^2.0.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -14783,11 +13055,6 @@ "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw=" }, - "readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==" - }, "recast": { "version": "0.20.4", "resolved": "https://registry.npmjs.org/recast/-/recast-0.20.4.tgz", @@ -14826,24 +13093,6 @@ "esprima": "~4.0.0" } }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, "reduce-css-calc": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", @@ -14927,23 +13176,6 @@ "unicode-match-property-value-ecmascript": "^1.2.0" } }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "requires": { - "rc": "^1.0.1" - } - }, "regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", @@ -15099,14 +13331,6 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "requires": { - "is-finite": "^1.0.0" - } - }, "replace-ext": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", @@ -15340,21 +13564,6 @@ "ret": "~0.1.10" } }, - "safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "requires": { - "ret": "~0.2.0" - }, - "dependencies": { - "ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" - } - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -15574,20 +13783,6 @@ "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" }, - "secure-e2ee": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/secure-e2ee/-/secure-e2ee-0.4.0.tgz", - "integrity": "sha512-NeByVRKg6SYY5bWaUQjTD8xeInegvCKCINW53pBE7bYLp9g2a6+n+Gu9+qZhANkqF65yelX7ZvbjaN/ulHZG6Q==", - "requires": { - "base64-js": "^1.5.1", - "md5": "^2.3.0" - } - }, - "secure-json-parse": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", - "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" - }, "secure-password": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/secure-password/-/secure-password-4.0.0.tgz", @@ -15597,11 +13792,6 @@ "sodium-native": "^3.1.1" } }, - "secure-webhooks": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/secure-webhooks/-/secure-webhooks-0.3.0.tgz", - "integrity": "sha512-UMcMEIvRHfbrAY2zhfv6ugEjkgbBXNwml50iDzq2FZBMeFoeJnfzw7RZFSUBcGG+/myfRP5A5Shw3msWdENxUA==" - }, "semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -15615,73 +13805,6 @@ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "requires": { - "semver": "^5.0.3" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "semver-store": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz", - "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, "serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", @@ -15710,11 +13833,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "set-cookie-parser": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz", - "integrity": "sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg==" - }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -15787,11 +13905,6 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "optional": true }, - "shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -15847,11 +13960,6 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -16127,11 +14235,6 @@ } } }, - "standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -16204,11 +14307,6 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -16229,11 +14327,6 @@ "strip-ansi": "^6.0.0" } }, - "string-similarity": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz", - "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==" - }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", @@ -16878,11 +14971,6 @@ } } }, - "timed-out": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", - "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=" - }, "timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -16891,11 +14979,6 @@ "setimmediate": "^1.0.4" } }, - "tiny-lru": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", - "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==" - }, "tlds": { "version": "1.219.0", "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.219.0.tgz", @@ -17221,20 +15304,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -17457,71 +15526,11 @@ } } }, - "unzip-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", - "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=" - }, "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, - "update-notifier": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-0.6.3.tgz", - "integrity": "sha1-d23sjaoT6WKjQeih2YNUMGtnrgg=", - "requires": { - "boxen": "^0.3.1", - "chalk": "^1.0.0", - "configstore": "^2.0.0", - "is-npm": "^1.0.0", - "latest-version": "^2.0.0", - "semver-diff": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -17565,14 +15574,6 @@ "requires-port": "^1.0.0" } }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - }, "urlsafe-base64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz", @@ -17677,26 +15678,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "utils-define-read-only-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/utils-define-read-only-property/-/utils-define-read-only-property-1.0.0.tgz", - "integrity": "sha1-Ll21WssKnwb1yButn0JtDiT44W8=" - }, - "utils-is-little-endian": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/utils-is-little-endian/-/utils-is-little-endian-1.0.0.tgz", - "integrity": "sha1-oRHW4jWT09IosCVGZM01xW3VrSc=", - "requires": { - "minimist": "^1.2.0", - "pkginfo": "^0.3.1", - "update-notifier": "^0.6.0" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -17733,21 +15714,11 @@ "spdx-expression-parse": "^3.0.0" } }, - "validate.io-infinite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/validate.io-infinite/-/validate.io-infinite-1.0.0.tgz", - "integrity": "sha1-kHbPC0H3rCAaQtWGl8RhFCtK/Ps=" - }, "value-or-function": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, "vfile": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.0.2.tgz", @@ -18410,14 +16381,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==" }, - "xdg-basedir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", - "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=", - "requires": { - "os-homedir": "^1.0.0" - } - }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index d9f38d1..05d039b 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,11 @@ "@fortawesome/pro-light-svg-icons": "file:./fontawesome/fortawesome-pro-light-svg-icons-5.15.3.tgz", "@fortawesome/pro-regular-svg-icons": "file:./fontawesome/fortawesome-pro-regular-svg-icons-5.15.3.tgz", "@fortawesome/pro-solid-svg-icons": "file:./fontawesome/fortawesome-pro-solid-svg-icons-5.15.3.tgz", - "@fortawesome/react-fontawesome": "0.1.14", + "@fortawesome/react-fontawesome": "0.1.15", "@headlessui/react": "1.4.0", "@heroicons/react": "1.0.3", "@hookform/resolvers": "2.6.1", - "@prisma/client": "2.27.0", + "@prisma/client": "2.28.0", "@react-aria/interactions": "3.5.0", "@tailwindcss/forms": "0.3.3", "@tailwindcss/typography": "0.4.1", @@ -60,7 +60,7 @@ "pino": "6.13.0", "pino-pretty": "5.1.2", "postcss": "8.3.6", - "quirrel": "1.6.3", + "quirrel": "1.7.0", "react": "18.0.0-alpha-6f3fcbd6f-20210730", "react-dom": "18.0.0-alpha-6f3fcbd6f-20210730", "react-hook-form": "7.12.2", @@ -84,7 +84,7 @@ "lint-staged": "10.5.4", "next-test-api-route-handler": "2.0.2", "prettier": "2.3.2", - "prettier-plugin-prisma": "0.14.0", + "prettier-plugin-prisma": "2.28.0", "pretty-quick": "3.1.1", "preview-email": "3.0.4", "prisma": "2.28.0", diff --git a/types.ts b/types.ts index ce874e0..04d6229 100644 --- a/types.ts +++ b/types.ts @@ -1,6 +1,8 @@ import { DefaultCtx, SessionContext, SimpleRolesIsAuthorized } from "blitz"; -import { User, Role } from "./db"; +import { Organization, User, GlobalRole, MembershipRole } from "./db"; + +type Role = GlobalRole | MembershipRole; declare module "blitz" { export interface Ctx extends DefaultCtx { @@ -11,7 +13,8 @@ declare module "blitz" { isAuthorized: SimpleRolesIsAuthorized; PublicData: { userId: User["id"]; - role: Role; + roles: Role[]; + orgId: Organization["id"]; hasCompletedOnboarding?: true; }; }