diff --git a/.env.example b/.env.example
index fa03747963..f7045d3f19 100644
--- a/.env.example
+++ b/.env.example
@@ -2,8 +2,12 @@
SECRET=RANDOM_STRING
DATABASE_URL='postgresql://user@localhost:5432/snoopforms?schema=public'
NEXTAUTH_URL=http://localhost:3000
-ADMIN_EMAIL=user@example.com
-ADMIN_PASSWORD='admin123'
+
+MAIL_FROM=noreply@example.com
+SMTP_HOST=smtp.example.com
+SMTP_PORT=587
+SMTP_USER=smtpUser
+SMTP_PASSWORD=smtpPassword
# For Docker Setup use this Database URL:
# DATABASE_URL='postgresql://postgres:postgres@postgres:5432/snoopforms?schema=public'
diff --git a/Dockerfile b/Dockerfile
index b4d18046ae..6af7c3ffec 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,6 @@ WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn prisma generate
-RUN yarn tsc prisma/seed.ts
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline
# Production image, copy all the files and run next
diff --git a/README.md b/README.md
index 847723e8b1..9d0cdb4b78 100644
--- a/README.md
+++ b/README.md
@@ -69,7 +69,7 @@ yarn install
cp .env.example .env
```
-4. Use the code editor of your choice to edit the .env file.
+4. Use the code editor of your choice to edit the .env file. You need to change all fields according to your setup. The SMTP-credentials are essential for verification emails to work during user signup.
5. Make sure your PostgreSQL Database Server is running. Then let prisma set up the database for you:
@@ -101,7 +101,7 @@ git clone https://github.com/snoopForms/snoopforms.git && cd snoopforms
```
-Create a `.env` file based on `.env.example` and change it according to your setup.
+Create a `.env` file based on `.env.example` and change all fields according to your setup. The SMTP-credentials are essential for verification emails to work during user signup.
```
diff --git a/docker-compose.yml b/docker-compose.yml
index c83f902ad9..982bfaa55e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,12 +9,7 @@ services:
snoopforms:
build: .
- command:
- [
- sh,
- -c,
- "yarn prisma migrate deploy && yarn prisma db seed && yarn start",
- ]
+ command: [sh, -c, "yarn prisma migrate deploy && yarn start"]
depends_on:
- postgres
ports:
diff --git a/lib/email.ts b/lib/email.ts
new file mode 100644
index 0000000000..d4aae5946a
--- /dev/null
+++ b/lib/email.ts
@@ -0,0 +1,48 @@
+import jwt from "jsonwebtoken";
+const nodemailer = require("nodemailer");
+
+interface sendEmailData {
+ to: string;
+ subject: string;
+ text?: string;
+ html: string;
+}
+
+export const sendEmail = async (emailData: sendEmailData) => {
+ let transporter = nodemailer.createTransport({
+ host: process.env.SMTP_HOST,
+ port: process.env.SMTP_PORT,
+ secure: process.env.SMTP_SECURE_ENABLED || false, // true for 465, false for other ports
+ auth: {
+ user: process.env.SMTP_USER,
+ pass: process.env.SMTP_PASSWORD,
+ },
+ });
+ const emailDefaults = {
+ from: process.env.MAIL_FROM || "noreply@snoopforms.com",
+ };
+ await transporter.sendMail({ ...emailDefaults, ...emailData });
+};
+
+export const sendVerificationEmail = async (user) => {
+ const token = jwt.sign({ id: user.id }, process.env.SECRET + user.email, {
+ expiresIn: "1d",
+ });
+ const verifyLink = `${
+ process.env.NEXTAUTH_URL
+ }/auth/verify?token=${encodeURIComponent(token)}`;
+ const verificationRequestLink = `${
+ process.env.NEXTAUTH_URL
+ }/auth/verification-requested?email=${encodeURIComponent(user.email)}`;
+ await sendEmail({
+ to: user.email,
+ subject: "Welcome to snoopForms",
+ html: `Welcome to snoopForms!
To verify your email address and start using snoopForms please click this link:
+ ${verifyLink}
+
+ The link is valid for one day. If it has expired please request a new token here:
+ ${verificationRequestLink}
+
+ Your snoopForms Team`,
+ });
+};
diff --git a/lib/users.ts b/lib/users.ts
new file mode 100644
index 0000000000..74cbf067e5
--- /dev/null
+++ b/lib/users.ts
@@ -0,0 +1,43 @@
+import { hashPassword } from "./auth";
+
+export const createUser = async (firstname, lastname, email, password) => {
+ const hashedPassword = await hashPassword(password);
+ try {
+ const res = await fetch(`/api/public/users`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ firstname,
+ lastname,
+ email,
+ password: hashedPassword,
+ }),
+ });
+ if (res.status !== 200) {
+ const json = await res.json();
+ throw Error(json.error);
+ }
+ return await res.json();
+ } catch (error) {
+ throw Error(`${error.message}`);
+ }
+};
+
+export const resendVerificationEmail = async (email) => {
+ try {
+ const res = await fetch(`/api/public/users/verfication-email`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ email,
+ }),
+ });
+ if (res.status !== 200) {
+ const json = await res.json();
+ throw Error(json.error);
+ }
+ return await res.json();
+ } catch (error) {
+ throw Error(`${error.message}`);
+ }
+};
diff --git a/package.json b/package.json
index c7a47eb7a5..8fe6ebd3af 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint",
- "seed:prod": "node prisma/seed.js"
+ "lint": "next lint"
},
"dependencies": {
"@editorjs/editorjs": "^2.24.3",
@@ -15,7 +14,7 @@
"@editorjs/paragraph": "^2.8.0",
"@headlessui/react": "^1.6.1",
"@heroicons/react": "^1.0.6",
- "@prisma/client": "^3.15.1",
+ "@prisma/client": "^4.0.0",
"@snoopforms/react": "^0.0.3",
"babel-plugin-superjson-next": "^0.4.3",
"bcryptjs": "^2.4.3",
@@ -23,10 +22,11 @@
"editorjs-drag-drop": "^1.1.2",
"editorjs-undo": "^2.0.3",
"json2csv": "^5.0.7",
+ "jsonwebtoken": "^8.5.1",
"next": "12.1.6",
"next-auth": "^4.3.4",
"nextjs-cors": "^2.1.1",
- "nodemailer": "^6.7.5",
+ "nodemailer": "^6.7.7",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-icons": "^4.4.0",
@@ -45,12 +45,9 @@
"eslint": "8.15.0",
"eslint-config-next": "12.1.6",
"postcss": "^8.4.13",
- "prisma": "^3.15.1",
+ "prisma": "^4.0.0",
"tailwindcss": "^3.0.24",
"ts-node": "^10.7.0",
"typescript": "4.6.4"
- },
- "prisma": {
- "seed": "ts-node prisma/seed.ts"
}
}
diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts
index 9976a9ccd0..f75b6f22cf 100644
--- a/pages/api/auth/[...nextauth].ts
+++ b/pages/api/auth/[...nextauth].ts
@@ -1,4 +1,5 @@
import { NextApiRequest, NextApiResponse } from "next";
+import jwt from "jsonwebtoken";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { prisma } from "../../../lib/prisma";
@@ -8,6 +9,7 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
return await NextAuth(req, res, {
providers: [
CredentialsProvider({
+ id: "credentials",
// The name to display on the sign in form (e.g. "Sign in with...")
name: "Credentials",
// The credentials is used to generate a suitable form on the sign in page.
@@ -65,7 +67,86 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
};
},
}),
+ CredentialsProvider({
+ id: "token",
+ // The name to display on the sign in form (e.g. "Sign in with...")
+ name: "Token",
+ // The credentials is used to generate a suitable form on the sign in page.
+ // You can specify whatever fields you are expecting to be submitted.
+ // e.g. domain, username, password, 2FA token, etc.
+ // You can pass any HTML attribute to the tag through the object.
+ credentials: {
+ token: {
+ label: "Verification Token",
+ type: "string",
+ },
+ },
+ async authorize(credentials, _req) {
+ let user;
+ try {
+ const { id } = await jwt.decode(credentials?.token);
+ user = await prisma.user.findUnique({
+ where: {
+ id: id,
+ },
+ });
+ } catch (e) {
+ console.error(e);
+ throw Error("Internal server error. Please try again later");
+ }
+
+ if (!user) {
+ throw new Error("User not found");
+ }
+
+ if (user.emailVerified) {
+ throw new Error("Email already verified");
+ }
+
+ const isValid = await new Promise((resolve) => {
+ jwt.verify(
+ credentials?.token,
+ process.env.SECRET + user.email,
+ (err) => {
+ if (err) resolve(false);
+ if (!err) resolve(true);
+ }
+ );
+ });
+
+ if (!isValid) {
+ throw new Error("Token is not valid or expired");
+ }
+
+ user = await prisma.user.update({
+ where: {
+ id: user.id,
+ },
+ data: { emailVerified: new Date().toISOString() },
+ });
+
+ return {
+ id: user.id,
+ email: user.email,
+ firstname: user.firstname,
+ lastname: user.firstname,
+ emailVerified: user.emailVerified,
+ };
+ },
+ }),
],
+ callbacks: {
+ async signIn({ user }) {
+ if (user.emailVerified) {
+ return true;
+ } else {
+ // Return false to display a default error message or you can return a URL to redirect to
+ return `/auth/verification-requested?email=${encodeURIComponent(
+ user.email
+ )}`;
+ }
+ },
+ },
secret: process.env.SECRET,
pages: {
signIn: "/auth/signin",
diff --git a/pages/api/forms/index.ts b/pages/api/forms/index.ts
index d49dec70ff..ae969dc076 100644
--- a/pages/api/forms/index.ts
+++ b/pages/api/forms/index.ts
@@ -23,7 +23,7 @@ export default async function handle(
},
include: {
owner: {
- select: { name: true },
+ select: { firstname: true },
},
_count: {
select: { submissionSessions: true },
diff --git a/pages/api/public/users/index.tsx b/pages/api/public/users/index.tsx
new file mode 100644
index 0000000000..c0ba0d3a5f
--- /dev/null
+++ b/pages/api/public/users/index.tsx
@@ -0,0 +1,45 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+import { prisma } from "../../../../lib/prisma";
+import { sendVerificationEmail } from "../../../../lib/email";
+
+export default async function handle(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ // POST /api/public/users
+ // Creates a new user
+ // Required fields in body: email, password (hashed)
+ // Optional fields in body: firstname, lastname
+ if (req.method === "POST") {
+ const user = req.body;
+ // create user in database
+ try {
+ const userData = await prisma.user.create({
+ data: {
+ ...user,
+ },
+ });
+ await sendVerificationEmail(userData);
+ res.json(userData);
+ } catch (e) {
+ if (e.code === "P2002") {
+ return res.status(409).json({
+ error: "user with this email address already exists",
+ errorCode: e.code,
+ });
+ } else {
+ return res.status(500).json({
+ error: e.message,
+ errorCode: e.code,
+ });
+ }
+ }
+ }
+
+ // Unknown HTTP Method
+ else {
+ throw new Error(
+ `The HTTP ${req.method} method is not supported by this route.`
+ );
+ }
+}
diff --git a/pages/api/public/users/verfication-email.tsx b/pages/api/public/users/verfication-email.tsx
new file mode 100644
index 0000000000..93270e4fb5
--- /dev/null
+++ b/pages/api/public/users/verfication-email.tsx
@@ -0,0 +1,44 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+import { prisma } from "../../../../lib/prisma";
+import { sendVerificationEmail } from "../../../../lib/email";
+
+export default async function handle(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ // POST /api/public/users
+ // Sends a new verification email to a user with a specific email address
+ // Required fields in body: email
+ if (req.method === "POST") {
+ const { email } = req.body;
+ // create user in database
+ try {
+ const user = await prisma.user.findUnique({
+ where: { email },
+ });
+ if (!user) {
+ return res.status(404).json({
+ error: "No user with this email address found",
+ });
+ }
+ if (user.emailVerified) {
+ return res.status(400).json({
+ error: "Email address has already been verified",
+ });
+ }
+ await sendVerificationEmail(user);
+ res.json(user);
+ } catch (e) {
+ return res.status(500).json({
+ error: e.message,
+ });
+ }
+ }
+
+ // Unknown HTTP Method
+ else {
+ throw new Error(
+ `The HTTP ${req.method} method is not supported by this route.`
+ );
+ }
+}
diff --git a/pages/auth/signin.tsx b/pages/auth/signin.tsx
index fcb70a80bd..0b47369b59 100644
--- a/pages/auth/signin.tsx
+++ b/pages/auth/signin.tsx
@@ -3,6 +3,7 @@ import { useRouter } from "next/router";
import { XCircleIcon } from "@heroicons/react/solid";
import { GetServerSideProps } from "next";
import Image from "next/image";
+import Link from "next/link";
interface props {
csrfToken: string;
@@ -101,10 +102,15 @@ export default function SignIn({ csrfToken }: props) {
>
Sign in
-
diff --git a/pages/auth/signup.tsx b/pages/auth/signup.tsx
new file mode 100644
index 0000000000..77124b0358
--- /dev/null
+++ b/pages/auth/signup.tsx
@@ -0,0 +1,183 @@
+import { getCsrfToken } from "next-auth/react";
+import { XCircleIcon } from "@heroicons/react/solid";
+import { GetServerSideProps } from "next";
+import Image from "next/image";
+import Link from "next/link";
+import { createUser } from "../../lib/users";
+import { useState } from "react";
+import { useRouter } from "next/router";
+
+interface props {
+ csrfToken: string;
+}
+
+export default function SignIn({ csrfToken }: props) {
+ const router = useRouter();
+ const [error, setError] = useState("");
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ await createUser(
+ e.target.elements.firstname.value,
+ e.target.elements.lastname.value,
+ e.target.elements.email.value,
+ e.target.elements.password.value
+ );
+ router.push(
+ `/auth/verification-requested?email=${encodeURIComponent(
+ e.target.elements.email.value
+ )}`
+ );
+ } catch (e) {
+ setError(e.message);
+ }
+ };
+ return (
+
+
+ {error && (
+
+
+
+
+
+
+
+ An error occurred when logging you in
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+
+ Create your own forms and collect submissions by creating a
+ snoopForms account.
+
+
+
+
+
+
+ );
+}
+
+export const getServerSideProps: GetServerSideProps = async (context) => {
+ const csrfToken = await getCsrfToken(context);
+ return {
+ props: { csrfToken },
+ };
+};
diff --git a/pages/auth/verification-requested.tsx b/pages/auth/verification-requested.tsx
new file mode 100644
index 0000000000..5283493621
--- /dev/null
+++ b/pages/auth/verification-requested.tsx
@@ -0,0 +1,68 @@
+import Image from "next/image";
+import { useRouter } from "next/router";
+import { toast } from "react-toastify";
+import { resendVerificationEmail } from "../../lib/users";
+
+interface props {
+ csrfToken: string;
+}
+
+export default function SignIn({}: props) {
+ const router = useRouter();
+ const email = router.query.email;
+
+ const requestVerificationEmail = async () => {
+ try {
+ await resendVerificationEmail(email);
+ toast("Verification email successfully sent. Please check your inbox.");
+ } catch (e) {
+ toast.error(`Error: ${e.message}`);
+ }
+ };
+ return (
+
+
+
+
+
+
+
+
+ {email ? (
+ <>
+
+ Please verify your email address
+
+
+ We have sent you an email to the address{" "}
+ {router.query.email}. Please
+ click the link in the email to activate your account.
+
+
+
+ You didn't receive an email or your link expired?
+
+ Click the button below to request a new email.
+
+
{" "}
+ >
+ ) : (
+
No E-Mail Address provided
+ )}
+
+
+
+
+ );
+}
diff --git a/pages/auth/verify.tsx b/pages/auth/verify.tsx
new file mode 100644
index 0000000000..a91da42f2d
--- /dev/null
+++ b/pages/auth/verify.tsx
@@ -0,0 +1,27 @@
+import { signIn } from "next-auth/react";
+import { useRouter } from "next/router";
+import { useEffect } from "react";
+
+export default function Verify() {
+ const router = useRouter();
+ const token = router.query.token?.toString();
+ useEffect(() => {
+ if (token) {
+ signIn("token", {
+ token,
+ callbackUrl: `/forms`,
+ });
+ }
+ }, [token]);
+ return (
+
+
+
+
+ {!token ? "No Token provided" : "Verifying..."}
+
+
+
+
+ );
+}
diff --git a/prisma/migrations/20220713132158_add_signup/migration.sql b/prisma/migrations/20220713132158_add_signup/migration.sql
new file mode 100644
index 0000000000..a0324a7cf5
--- /dev/null
+++ b/prisma/migrations/20220713132158_add_signup/migration.sql
@@ -0,0 +1,24 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `name` on the `users` table. All the data in the column will be lost.
+ - You are about to drop the `verification_requests` table. If the table is not empty, all the data it contains will be lost.
+ - Made the column `email` on table `users` required. This step will fail if there are existing NULL values in that column.
+ - Made the column `password` on table `users` required. This step will fail if there are existing NULL values in that column.
+
+*/
+-- DropForeignKey
+ALTER TABLE "Form" DROP CONSTRAINT "Form_ownerId_fkey";
+
+-- AlterTable
+ALTER TABLE "users" DROP COLUMN "name",
+ADD COLUMN "firstname" TEXT,
+ADD COLUMN "lastname" TEXT,
+ALTER COLUMN "email" SET NOT NULL,
+ALTER COLUMN "password" SET NOT NULL;
+
+-- DropTable
+DROP TABLE "verification_requests";
+
+-- AddForeignKey
+ALTER TABLE "Form" ADD CONSTRAINT "Form_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 9a9716c6f9..5d6aad9857 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -20,7 +20,7 @@ model Form {
id String @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- owner User @relation(fields: [ownerId], references: [id])
+ owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
ownerId Int
formType FormType @default(NOCODE)
name String @default("")
@@ -73,24 +73,14 @@ model SessionEvent {
model User {
id Int @id @default(autoincrement())
- name String?
- email String? @unique
+ firstname String?
+ lastname String?
+ email String @unique
emailVerified DateTime? @map(name: "email_verified")
- password String?
+ password String
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
forms Form[]
@@map(name: "users")
-}
-
-model VerificationRequest {
- id Int @id @default(autoincrement())
- identifier String
- token String @unique
- expires DateTime
- createdAt DateTime @default(now()) @map(name: "created_at")
- updatedAt DateTime @default(now()) @map(name: "updated_at")
-
- @@map(name: "verification_requests")
}
\ No newline at end of file
diff --git a/prisma/seed.ts b/prisma/seed.ts
deleted file mode 100644
index 79d79b0490..0000000000
--- a/prisma/seed.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { PrismaClient, Prisma } from "@prisma/client";
-import { hash } from "bcryptjs";
-
-const prisma = new PrismaClient();
-
-async function main() {
- console.log(`Start seeding ...`);
- if (process.env.ADMIN_PASSWORD) {
- const passwordHash = await hash(process.env.ADMIN_PASSWORD, 12);
-
- if (typeof passwordHash === "string") {
- const users: Prisma.UserCreateInput[] = [
- {
- name: "Admin",
- email: process.env.ADMIN_EMAIL,
- password: passwordHash,
- },
- ];
-
- for (const user of users) {
- const userRes = await prisma.user.upsert({
- where: {
- email: user.email,
- },
- update: {},
- create: user,
- });
- console.log(`Created user with id: ${userRes.id}`);
- }
- console.log(`Seeding finished.`);
- }
- }
-}
-
-main()
- .catch((e) => {
- console.error(e);
- process.exit(1);
- })
- .finally(async () => {
- await prisma.$disconnect();
- });
diff --git a/yarn.lock b/yarn.lock
index 84a50868ef..16470351a1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -230,22 +230,22 @@
resolved "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz"
integrity sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==
-"@prisma/client@^3.15.1":
- version "3.15.1"
- resolved "https://registry.npmjs.org/@prisma/client/-/client-3.15.1.tgz"
- integrity sha512-Lsk7oupvO9g99mrIs07iE6BIMouHs46Yq/YY8itTsUQNKfecsPuZvVYvcKci0pqRQ0neOpvIvoA/ouZmIMBCrQ==
+"@prisma/client@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.0.0.tgz#ed2f46930a1da0d8ae88d7965485973576b04270"
+ integrity sha512-g1h2OGoRo7anBVQ9Cw3gsbjwPtvf7i0pkGxKeZICtwkvE5CZXW+xZF4FZdmrViYkKaAShbISL0teNpu9ecpf4g==
dependencies:
- "@prisma/engines-version" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
+ "@prisma/engines-version" "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
-"@prisma/engines-version@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e":
- version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
- resolved "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz"
- integrity sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w==
+"@prisma/engines-version@3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11":
+ version "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
+ resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11.tgz#4b5efe5eee2feef12910e4627a572cd96ed83236"
+ integrity sha512-PiZhdD624SrYEjyLboI0X7OugNbxUzDJx9v/6ldTKuqNDVUCmRH/Z00XwDi/dgM4FlqOSO+YiUsSiSKjxxG8cw==
-"@prisma/engines@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e":
- version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
- resolved "https://registry.npmjs.org/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz"
- integrity sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==
+"@prisma/engines@3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11":
+ version "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
+ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11.tgz#82f0018153cffa05d61422f9c0c7b0479b180f75"
+ integrity sha512-u/rG4lDHALolWBLr3yebZ+N2qImp3SDMcu7bHNJuRDaYvYEXy/MqfNRNEgd9GoPsXL3gofYf0VzJf2AmCG3YVw==
"@rushstack/eslint-patch@^1.1.3":
version "1.1.3"
@@ -574,6 +574,11 @@ browserslist@^4.20.3:
node-releases "^2.0.5"
picocolors "^1.0.0"
+buffer-equal-constant-time@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
+ integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
+
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz"
@@ -800,6 +805,13 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+ecdsa-sig-formatter@1.0.11:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
+ integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
+ dependencies:
+ safe-buffer "^5.0.1"
+
editorjs-drag-drop@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/editorjs-drag-drop/-/editorjs-drag-drop-1.1.2.tgz"
@@ -1508,6 +1520,22 @@ jsonparse@^1.3.1:
resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
+jsonwebtoken@^8.5.1:
+ version "8.5.1"
+ resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
+ integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
+ dependencies:
+ jws "^3.2.2"
+ lodash.includes "^4.3.0"
+ lodash.isboolean "^3.0.3"
+ lodash.isinteger "^4.0.4"
+ lodash.isnumber "^3.0.3"
+ lodash.isplainobject "^4.0.6"
+ lodash.isstring "^4.0.1"
+ lodash.once "^4.0.0"
+ ms "^2.1.1"
+ semver "^5.6.0"
+
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.2.1:
version "3.3.0"
resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz"
@@ -1516,6 +1544,23 @@ jsonparse@^1.3.1:
array-includes "^3.1.4"
object.assign "^4.1.2"
+jwa@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
+ integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
+ dependencies:
+ buffer-equal-constant-time "1.0.1"
+ ecdsa-sig-formatter "1.0.11"
+ safe-buffer "^5.0.1"
+
+jws@^3.2.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
+ integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
+ dependencies:
+ jwa "^1.4.1"
+ safe-buffer "^5.0.1"
+
language-subtag-registry@~0.3.2:
version "0.3.21"
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz"
@@ -1559,16 +1604,46 @@ lodash.get@^4.4.2:
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz"
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
+lodash.includes@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
+ integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==
+
+lodash.isboolean@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
+ integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
+
+lodash.isinteger@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
+ integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==
+
+lodash.isnumber@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
+ integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
+
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+lodash.isstring@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+ integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
+
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+lodash.once@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+ integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
+
loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
@@ -1693,10 +1768,10 @@ node-releases@^2.0.5:
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz"
integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==
-nodemailer@^6.7.5:
- version "6.7.5"
- resolved "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz"
- integrity sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg==
+nodemailer@^6.7.7:
+ version "6.7.7"
+ resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.7.tgz#e522fbd7507b81c51446d3f79c4603bf00083ddd"
+ integrity sha512-pOLC/s+2I1EXuSqO5Wa34i3kXZG3gugDssH+ZNCevHad65tc8vQlCQpOLaUjopvkRQKm2Cki2aME7fEOPRy3bA==
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
@@ -1967,12 +2042,12 @@ pretty-format@^3.8.0:
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz"
integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==
-prisma@^3.15.1:
- version "3.15.1"
- resolved "https://registry.npmjs.org/prisma/-/prisma-3.15.1.tgz"
- integrity sha512-MLO3JUGJpe5+EVisA/i47+zlyF8Ug0ivvGYG4B9oSXQcPiUHB1ccmnpxqR7o0Up5SQgmxkBiEU//HgR6UuIKOw==
+prisma@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.0.0.tgz#4ddb8fcd4f64d33aff8c198a6986dcce66dc8152"
+ integrity sha512-Dtsar03XpCBkcEb2ooGWO/WcgblDTLzGhPcustbehwlFXuTMliMDRzXsfygsgYwQoZnAUKRd1rhpvBNEUziOVw==
dependencies:
- "@prisma/engines" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
+ "@prisma/engines" "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
prop-types@^15.8.1:
version "15.8.1"
@@ -2111,6 +2186,11 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+safe-buffer@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
@@ -2119,6 +2199,11 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
+semver@^5.6.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+ integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
semver@^6.3.0:
version "6.3.0"
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"