diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index e5b6d8d6a..000000000 --- a/.changeset/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changesets - -Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works -with multi-package repos, or single-package repos to help you version and publish your code. You can -find the full documentation for it [in our repository](https://github.com/changesets/changesets) - -We have a quick list of common questions to get you started engaging with this project in -[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json deleted file mode 100644 index 91c0df52e..000000000 --- a/.changeset/config.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "linked": [], - "access": "public", - "baseBranch": "main", - "updateInternalDependencies": "patch" -} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a2ba921f5..60b385403 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,18 +14,18 @@ "customizations": { "vscode": { "extensions": [ - "GitHub.vscode-pull-request-github", - "GitHub.copilot-labs", - "GitHub.copilot-chat", - "GitHub.copilot", "aaron-bond.better-comments", - "mikestead.dotenv", - "VisualStudioExptTeam.vscodeintellicode", - "Prisma.prisma", "bradlc.vscode-tailwindcss", "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "mikestead.dotenv", "unifiedjs.vscode-mdx", - "esbenp.prettier-vscode" + "GitHub.copilot-chat", + "GitHub.copilot-labs", + "GitHub.copilot", + "GitHub.vscode-pull-request-github", + "Prisma.prisma", + "VisualStudioExptTeam.vscodeintellicode", ] } } diff --git a/.env.gitpod b/.env.gitpod deleted file mode 100644 index fcb274047..000000000 --- a/.env.gitpod +++ /dev/null @@ -1,29 +0,0 @@ -DATABASE_URL="postgres://documenso:password@127.0.0.1:54320/documenso" -NEXT_PUBLIC_WEBAPP_URL="" - -# AUTH -NEXTAUTH_SECRET='lorem ipsum sit dolor random string for encryption this could literally be anything' -NEXTAUTH_URL="" - -# SIGNING -CERT_FILE_PATH="" -CERT_PASSPHRASE="" -CERT_FILE_ENCODING="" - -# EMAIL -SMTP_MAIL_HOST='127.0.0.1' -SMTP_MAIL_PORT='2500' -SMTP_MAIL_USER='documenso' -SMTP_MAIL_PASSWORD='documenso' -MAIL_FROM='documenso@gitpod.io' - -# STRIPE -STRIPE_API_KEY="" -STRIPE_WEBHOOK_SECRET="" -NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID="" -NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID="" - -#FEATURE FLAGS -# Allow users to register via the /signup page. Otherwise they will be redirect to the home page. -NEXT_PUBLIC_ALLOW_SIGNUP=true -NEXT_PUBLIC_ALLOW_SUBSCRIPTIONS=false \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b4be2fb4..fe2bb8304 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,11 +52,3 @@ You can build the project with: ```bash npm run build ``` - -### Making a Pull Request - -When making a pull request, be sure to add a changeset when something has changed with Documenso. - -```shell -npm run changeset -``` diff --git a/README.md b/README.md index 409d4c8f1..82ab469eb 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Issues · Roadmap - · + · Upcoming Launches

diff --git a/apps/marketing/content/blog/next.mdx b/apps/marketing/content/blog/next.mdx index 4f846a0a7..6bc14e053 100644 --- a/apps/marketing/content/blog/next.mdx +++ b/apps/marketing/content/blog/next.mdx @@ -20,11 +20,11 @@ Today, I'm pleased to share with you a preview of the next Documenso. We redesigned the whole signing flow to make it more appealing and more convenient. -We improved the overall look and feel by making it more elegant and appropriately playful. Focused on the task at hand, but explicitly enjoying doing it. +We improved the overall look and feel by making it more elegant and appropriately playful. Focused on the task at hand, but explicitly enjoying doing it. **We call it happy minimalism.** -We paid particular attention to the moment of signing, which should be celebrated. +We paid particular attention to the moment of signing, which should be celebrated. The image below is the final bloom of the completion celebration we added: diff --git a/apps/marketing/src/app/layout.tsx b/apps/marketing/src/app/layout.tsx index edc1136b5..05206a76f 100644 --- a/apps/marketing/src/app/layout.tsx +++ b/apps/marketing/src/app/layout.tsx @@ -69,6 +69,8 @@ export default async function RootLayout({ children }: { children: React.ReactNo + + ); diff --git a/apps/marketing/src/components/(marketing)/callout.tsx b/apps/marketing/src/components/(marketing)/callout.tsx index 89ff9e766..72ae3907b 100644 --- a/apps/marketing/src/components/(marketing)/callout.tsx +++ b/apps/marketing/src/components/(marketing)/callout.tsx @@ -40,7 +40,7 @@ export const Callout = ({ starCount }: CalloutProps) => { className="rounded-full bg-transparent backdrop-blur-sm" onClick={onSignUpClick} > - Get the Community Plan + Get the Early Adopters Plan $30/mo. forever! diff --git a/apps/marketing/src/components/(marketing)/header.tsx b/apps/marketing/src/components/(marketing)/header.tsx index 6aa7db77d..194c67c0f 100644 --- a/apps/marketing/src/components/(marketing)/header.tsx +++ b/apps/marketing/src/components/(marketing)/header.tsx @@ -24,7 +24,13 @@ export const Header = ({ className, ...props }: HeaderProps) => {
setIsHamburgerMenuOpen(false)}> - Documenso Logo + Documenso Logo {isSinglePlayerModeMarketingEnabled && ( diff --git a/apps/marketing/src/components/(marketing)/hero.tsx b/apps/marketing/src/components/(marketing)/hero.tsx index 88e21bca2..4ff8fbcc2 100644 --- a/apps/marketing/src/components/(marketing)/hero.tsx +++ b/apps/marketing/src/components/(marketing)/hero.tsx @@ -114,7 +114,7 @@ export const Hero = ({ className, ...props }: HeroProps) => { className="rounded-full bg-transparent backdrop-blur-sm" onClick={onSignUpClick} > - Get the Community Plan + Get the Early Adopters Plan $30/mo. forever! diff --git a/apps/marketing/src/components/(marketing)/pricing-table.tsx b/apps/marketing/src/components/(marketing)/pricing-table.tsx index bcfc2d176..712435e68 100644 --- a/apps/marketing/src/components/(marketing)/pricing-table.tsx +++ b/apps/marketing/src/components/(marketing)/pricing-table.tsx @@ -109,7 +109,7 @@ export const PricingTable = ({ className, ...props }: PricingTableProps) => { data-plan="community" className="border-primary bg-background shadow-foreground/5 flex flex-col items-center justify-center rounded-lg border-2 px-8 py-12 shadow-[0px_0px_0px_4px_#E3E3E380]" > -

Community

+

Early Adopters

{period === 'MONTHLY' && $30} @@ -168,7 +168,7 @@ export const PricingTable = ({ className, ...props }: PricingTableProps) => {
-

Everything in Community, plus:

+

Everything in Early Adopters, plus:

Custom Subdomain

Compliance Check

Guaranteed Uptime

diff --git a/apps/marketing/src/components/(marketing)/widget.tsx b/apps/marketing/src/components/(marketing)/widget.tsx index 71dc20d99..7fd4aaa49 100644 --- a/apps/marketing/src/components/(marketing)/widget.tsx +++ b/apps/marketing/src/components/(marketing)/widget.tsx @@ -189,7 +189,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => { className="bg-foreground/5 col-span-12 flex flex-col rounded-2xl p-6 lg:col-span-5" onSubmit={handleSubmit(onFormSubmit)} > -

Sign up for the community plan

+

Sign up for the early adopters plan

with Timur Ercan & Lucas Smith from Documenso

diff --git a/apps/web/components/forgot-password.tsx b/apps/web/components/forgot-password.tsx deleted file mode 100644 index 8235a80d9..000000000 --- a/apps/web/components/forgot-password.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useState } from "react"; -import Link from "next/link"; -import { Button } from "@documenso/ui"; -import Logo from "./logo"; -import { ArrowLeftIcon } from "@heroicons/react/24/outline"; -import { FormProvider, useForm } from "react-hook-form"; -import { toast } from "react-hot-toast"; - -interface ForgotPasswordForm { - email: string; -} - -export default function ForgotPassword() { - const { register, formState, resetField, handleSubmit } = useForm(); - const [resetSuccessful, setResetSuccessful] = useState(false); - - const onSubmit = async (values: ForgotPasswordForm) => { - const response = await toast.promise( - fetch(`/api/auth/forgot-password`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(values), - }), - { - loading: "Sending...", - success: "Reset link sent.", - error: "Could not send reset link :/", - } - ); - - if (!response.ok) { - toast.dismiss(); - - if (response.status == 404) { - toast.error("Email address not found."); - } - - if (response.status == 400) { - toast.error("Password reset requested."); - } - - if (response.status == 500) { - toast.error("Something went wrong."); - } - - return; - } - - if (response.ok) { - setResetSuccessful(true); - } - - resetField("email"); - }; - - return ( - <> -
-
-
- -

- {resetSuccessful ? "Reset Password" : "Forgot Password?"} -

-

- {resetSuccessful - ? "Please check your email for reset instructions." - : "No worries, we'll send you reset instructions."} -

-
- {!resetSuccessful && ( -
-
-
- - -
-
- -
- -
-
- )} -
- -
- - Back to log in -
- -
-
-
- - ); -} diff --git a/apps/web/components/reset-password.tsx b/apps/web/components/reset-password.tsx deleted file mode 100644 index 9f5f1d466..000000000 --- a/apps/web/components/reset-password.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useState } from "react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { Button } from "@documenso/ui"; -import Logo from "./logo"; -import { ArrowLeftIcon } from "@heroicons/react/24/outline"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { toast } from "react-hot-toast"; -import * as z from "zod"; - -const ZResetPasswordFormSchema = z - .object({ - password: z.string().min(8, { message: "Password must be at least 8 characters" }), - confirmPassword: z.string().min(8, { message: "Password must be at least 8 characters" }), - }) - .refine((data) => data.password === data.confirmPassword, { - path: ["confirmPassword"], - message: "Password don't match", - }); - -type TResetPasswordFormSchema = z.infer; - -export default function ResetPassword() { - const router = useRouter(); - const { token } = router.query; - - const { - register, - formState: { errors, isSubmitting }, - handleSubmit, - } = useForm({ - resolver: zodResolver(ZResetPasswordFormSchema), - }); - - const [resetSuccessful, setResetSuccessful] = useState(false); - - const onSubmit = async ({ password }: TResetPasswordFormSchema) => { - const response = await toast.promise( - fetch(`/api/auth/reset-password`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ password, token }), - }), - { - loading: "Resetting...", - success: `Reset password successful`, - error: "Could not reset password :/", - } - ); - - if (!response.ok) { - toast.dismiss(); - const error = await response.json(); - toast.error(error.message); - } - - if (response.ok) { - setResetSuccessful(true); - setTimeout(() => { - router.push("/login"); - }, 3000); - } - }; - - return ( - <> -
-
-
- -

- Reset Password -

-

- {resetSuccessful ? "Your password has been reset." : "Please chose your new password"} -

-
- {!resetSuccessful && ( -
-
-
- - -
- -
- - -
-
- - {errors && ( - {errors.confirmPassword?.message} - )} - -
- -
-
- )} - -
- -
- - Back to log in -
- -
-
-
- - ); -} diff --git a/apps/web/package.json b/apps/web/package.json index 6714d68a1..90da68c4d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -22,7 +22,7 @@ "@tanstack/react-query": "^4.29.5", "formidable": "^2.1.1", "framer-motion": "^10.12.8", - "lucide-react": "^0.277.0", + "lucide-react": "^0.279.0", "luxon": "^3.4.0", "micro": "^10.0.1", "next": "14.0.0", @@ -50,4 +50,4 @@ "@types/react": "18.2.18", "@types/react-dom": "18.2.7" } -} \ No newline at end of file +} diff --git a/apps/web/pages/api/auth/forgot-password.ts b/apps/web/pages/api/auth/forgot-password.ts deleted file mode 100644 index 98e4a6676..000000000 --- a/apps/web/pages/api/auth/forgot-password.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import { sendResetPassword } from "@documenso/lib/mail"; -import { defaultHandler, defaultResponder } from "@documenso/lib/server"; -import prisma from "@documenso/prisma"; -import crypto from "crypto"; - -async function postHandler(req: NextApiRequest, res: NextApiResponse) { - const { email } = req.body; - const cleanEmail = email.toLowerCase(); - - if (!cleanEmail || !/.+@.+/.test(cleanEmail)) { - res.status(400).json({ message: "Invalid email" }); - return; - } - - const user = await prisma.user.findFirst({ - where: { - email: cleanEmail, - }, - }); - - if (!user) { - return res.status(200).json({ message: "A password reset email has been sent." }); - } - - const existingToken = await prisma.passwordResetToken.findFirst({ - where: { - userId: user.id, - createdAt: { - gte: new Date(Date.now() - 1000 * 60 * 60), - }, - }, - }); - - if (existingToken) { - return res.status(200).json({ message: "A password reset email has been sent." }); - } - - const token = crypto.randomBytes(64).toString("hex"); - const expiry = new Date(); - expiry.setHours(expiry.getHours() + 24); // Set expiry to one hour from now - - let passwordResetToken; - try { - passwordResetToken = await prisma.passwordResetToken.create({ - data: { - token, - expiry, - userId: user.id, - }, - }); - } catch (error) { - return res.status(500).json({ message: "Something went wrong" }); - } - - await sendResetPassword(user, passwordResetToken.token); - - return res.status(200).json({ message: "A password reset email has been sent." }); -} - -export default defaultHandler({ - POST: Promise.resolve({ default: defaultResponder(postHandler) }), -}); diff --git a/apps/web/pages/api/auth/reset-password.ts b/apps/web/pages/api/auth/reset-password.ts deleted file mode 100644 index 78a81b7d4..000000000 --- a/apps/web/pages/api/auth/reset-password.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import { hashPassword, verifyPassword } from "@documenso/lib/auth"; -import { sendResetPasswordSuccessMail } from "@documenso/lib/mail"; -import { defaultHandler, defaultResponder } from "@documenso/lib/server"; -import prisma from "@documenso/prisma"; - -async function postHandler(req: NextApiRequest, res: NextApiResponse) { - const { token, password } = req.body; - - if (!token) { - res.status(400).json({ message: "Invalid token" }); - return; - } - - const foundToken = await prisma.passwordResetToken.findUnique({ - where: { - token, - }, - include: { - User: true, - }, - }); - - if (!foundToken) { - return res.status(404).json({ message: "Invalid token." }); - } - - const now = new Date(); - - if (now > foundToken.expiry) { - return res.status(400).json({ message: "Token has expired" }); - } - - const isSamePassword = await verifyPassword(password, foundToken.User.password!); - - if (isSamePassword) { - return res.status(400).json({ message: "New password must be different" }); - } - - const hashedPassword = await hashPassword(password); - - const transaction = await prisma.$transaction([ - prisma.user.update({ - where: { - id: foundToken.userId, - }, - data: { - password: hashedPassword, - }, - }), - prisma.passwordResetToken.deleteMany({ - where: { - userId: foundToken.userId, - }, - }), - ]); - - if (!transaction) { - return res.status(500).json({ message: "Error resetting password." }); - } - - await sendResetPasswordSuccessMail(foundToken.User); - - res.status(200).json({ message: "Password reset successful." }); -} - -export default defaultHandler({ - POST: Promise.resolve({ default: defaultResponder(postHandler) }), -}); diff --git a/apps/web/pages/auth/reset/[token].tsx b/apps/web/pages/auth/reset/[token].tsx deleted file mode 100644 index 33868f762..000000000 --- a/apps/web/pages/auth/reset/[token].tsx +++ /dev/null @@ -1,30 +0,0 @@ -import Head from "next/head"; -import { getUserFromToken } from "@documenso/lib/server"; -import ResetPassword from "../../../components/reset-password"; - -export default function ResetPasswordPage() { - return ( - <> - - Reset Password | Documenso - - - - ); -} - -export async function getServerSideProps(context: any) { - const user = await getUserFromToken(context.req, context.res); - if (user) - return { - redirect: { - source: "/login", - destination: "/dashboard", - permanent: false, - }, - }; - - return { - props: {}, - }; -} diff --git a/apps/web/pages/auth/reset/index.tsx b/apps/web/pages/auth/reset/index.tsx deleted file mode 100644 index f21145422..000000000 --- a/apps/web/pages/auth/reset/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import Logo from "../../../components/logo"; - -export default function ResetPage() { - return ( -
-
-
- -

- Reset Password -

-

- The token you provided is invalid. Please try again. -

-
-
-
- ); -} diff --git a/apps/web/pages/forgot-password.tsx b/apps/web/pages/forgot-password.tsx deleted file mode 100644 index 4591921a4..000000000 --- a/apps/web/pages/forgot-password.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { GetServerSideProps, GetServerSidePropsContext } from "next"; -import Head from "next/head"; -import { getUserFromToken } from "@documenso/lib/server"; -import ForgotPassword from "../components/forgot-password"; - -export default function ForgotPasswordPage() { - return ( - <> - - Forgot Password | Documenso - - - - ); -} - -export async function getServerSideProps({ req }: GetServerSidePropsContext) { - const user = await getUserFromToken(req); - - if (user) - return { - redirect: { - source: "/login", - destination: "/dashboard", - permanent: false, - }, - }; - - return { - props: {}, - }; -} diff --git a/apps/web/src/app/(signing)/sign/[token]/page.tsx b/apps/web/src/app/(signing)/sign/[token]/page.tsx index f8c83acef..a1ad0b170 100644 --- a/apps/web/src/app/(signing)/sign/[token]/page.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/page.tsx @@ -1,4 +1,4 @@ -import { notFound } from 'next/navigation'; +import { notFound, redirect } from 'next/navigation'; import { match } from 'ts-pattern'; @@ -40,7 +40,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp viewedDocument({ token }).catch(() => null), ]); - if (!document || !recipient) { + if (!document || !document.documentData || !recipient) { return notFound(); } @@ -55,14 +55,6 @@ export default async function SigningPage({ params: { token } }: SigningPageProp redirect(`/sign/${token}/complete`); } - const user = await getServerComponentSession(); - - const documentDataUrl = await getFile(documentData) - .then((buffer) => Buffer.from(buffer).toString('base64')) - .then((data) => `data:application/pdf;base64,${data}`); - - const user = await getServerComponentSession(); - return ( { const { toast } = useToast(); + const [showPassword, setShowPassword] = useState(false); const { register, diff --git a/packages/lib/server-only/feature-flags/get-asset-buffer.ts b/apps/web/src/helpers/get-asset-buffer.ts similarity index 100% rename from packages/lib/server-only/feature-flags/get-asset-buffer.ts rename to apps/web/src/helpers/get-asset-buffer.ts diff --git a/docker/Dockerfile b/docker/Dockerfile index 4cc82df04..a50726eff 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,14 +1,7 @@ -ARG NEXT_PUBLIC_WEBAPP_URL=http://localhost:3000 -ARG NEXT_PUBLIC_ALLOW_SIGNUP=true -ARG NEXT_PUBLIC_ALLOW_SUBSCRIPTIONS=false -ARG NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID -ARG NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID - FROM node:18-alpine AS base # Install dependencies only when needed FROM base AS production_deps - WORKDIR /app # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. @@ -21,13 +14,6 @@ RUN npm ci --production # Install dependencies only when needed FROM base AS builder - -ARG NEXT_PUBLIC_WEBAPP_URL -ARG NEXT_PUBLIC_ALLOW_SIGNUP -ARG NEXT_PUBLIC_ALLOW_SUBSCRIPTIONS -ARG NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID -ARG NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID - WORKDIR /app # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. @@ -45,7 +31,6 @@ RUN npm run build --workspaces # Production image, copy all the files and run next FROM base AS runner - WORKDIR /app ENV NODE_ENV production diff --git a/docker/compose-without-app.yml b/docker/compose-without-app.yml index 8b781a9a7..e0b566e96 100644 --- a/docker/compose-without-app.yml +++ b/docker/compose-without-app.yml @@ -1,3 +1,4 @@ +name: documenso services: database: image: postgres:15 diff --git a/package-lock.json b/package-lock.json index 4ee25390b..b1c97d01c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,7 @@ "@tanstack/react-query": "^4.29.5", "formidable": "^2.1.1", "framer-motion": "^10.12.8", - "lucide-react": "^0.277.0", + "lucide-react": "^0.279.0", "luxon": "^3.4.0", "micro": "^10.0.1", "next": "14.0.0", @@ -7178,15 +7178,6 @@ "node": ">=8" } }, - "node_modules/breakword": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/breakword/-/breakword-1.0.6.tgz", - "integrity": "sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==", - "dev": true, - "dependencies": { - "wcwidth": "^1.0.1" - } - }, "node_modules/browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -8571,18 +8562,6 @@ "node": ">= 0.4" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -10095,16 +10074,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-yarn-workspace-root2": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", - "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", - "dev": true, - "dependencies": { - "micromatch": "^4.0.2", - "pkg-dir": "^4.2.0" - } - }, "node_modules/flat-cache": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", @@ -11393,18 +11362,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", @@ -11662,18 +11619,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-subdir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", - "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", - "dev": true, - "dependencies": { - "better-path-resolve": "1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/is-symbol": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", @@ -12569,9 +12514,9 @@ } }, "node_modules/lucide-react": { - "version": "0.277.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.277.0.tgz", - "integrity": "sha512-9epmznme+vW14V9d2rsMeLr3fMnf59lYDUOVUg6s7oVN22Zq8h4B30+3CIdFFV9UXCjPG5ZNKHfO/hf96cl46A==", + "version": "0.279.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.279.0.tgz", + "integrity": "sha512-LJ8g66+Bxc3t3x9vKTeK3wn3xucrOQGfJ9ou9GsBwCt2offsrT2BB90XrTrIzE1noYYDe2O8jZaRHi6sAHXNxw==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } @@ -19956,7 +19901,7 @@ "clsx": "^1.2.1", "cmdk": "^0.2.0", "framer-motion": "^10.12.8", - "lucide-react": "^0.277.0", + "lucide-react": "^0.279.0", "luxon": "^3.4.2", "next": "14.0.0", "pdfjs-dist": "3.6.172", diff --git a/package.json b/package.json index ee2aa8bd7..5bbd4f431 100644 --- a/package.json +++ b/package.json @@ -44,5 +44,8 @@ "workspaces": [ "apps/*", "packages/*" - ] + ], + "dependencies": { + "recharts": "^2.7.2" + } } diff --git a/packages/lib/mail/resetPasswordSuccessTemplate.ts b/packages/lib/mail/resetPasswordSuccessTemplate.ts deleted file mode 100644 index 25a0a9ff5..000000000 --- a/packages/lib/mail/resetPasswordSuccessTemplate.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { NEXT_PUBLIC_WEBAPP_URL } from "../constants"; -import { User } from "@prisma/client"; - -export const resetPasswordSuccessTemplate = (user: User) => { - return ` -
-
- Documenso Logo - -

Password updated!

- -

- Hi ${user.name ? user.name : user.email}, -

- -

- We've changed your password as you asked. You can now sign in with your new password. -

- -

- Didn't request a password change? We are here to help you secure your account, just contact us. -

- -

-

- The Documenso Team -

-

- -

- Want to send you own signing links? - Hosted Documenso is here!. -

-
-
-
-
- Need help? -
- Contact us at hi@documenso.com -
-
-
- Easy and beautiful document signing by Documenso. -
-
-`; -}; -export default resetPasswordSuccessTemplate; diff --git a/packages/lib/mail/resetPasswordTemplate.ts b/packages/lib/mail/resetPasswordTemplate.ts deleted file mode 100644 index b86b404fd..000000000 --- a/packages/lib/mail/resetPasswordTemplate.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { NEXT_PUBLIC_WEBAPP_URL } from "../constants"; - -export const resetPasswordTemplate = (ctaLink: string, ctaLabel: string) => { - const customContent = ` -

Forgot your password?

-

- That's okay, it happens! Click the button below to reset your password. -

- -

- - ${ctaLabel} - -

-

- Want to send you own signing links? Hosted Documenso is here!. -

`; - - const html = ` -
-
- Documenso Logo - ${customContent} -
-
- `; - - const footer = ` -
-
- Need help? -
- Contact us at hi@documenso.com -
-
-
- Easy and beautiful document signing by Documenso. -
-
`; - - return html + footer; -}; - -export default resetPasswordTemplate; diff --git a/packages/lib/mail/sendResetPassword.ts b/packages/lib/mail/sendResetPassword.ts deleted file mode 100644 index 32e098a4c..000000000 --- a/packages/lib/mail/sendResetPassword.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { resetPasswordTemplate } from "@documenso/lib/mail"; -import { NEXT_PUBLIC_WEBAPP_URL } from "../constants"; -import { sendMail } from "./sendMail"; -import { User } from "@prisma/client"; - -export const sendResetPassword = async (user: User, token: string) => { - await sendMail( - user.email, - "Forgot password?", - resetPasswordTemplate(`${NEXT_PUBLIC_WEBAPP_URL}/auth/reset/${token}`, "Reset Your Password") - ).catch((err) => { - throw err; - }); -}; diff --git a/packages/lib/mail/sendResetPasswordSuccessMail.ts b/packages/lib/mail/sendResetPasswordSuccessMail.ts deleted file mode 100644 index 6877700fb..000000000 --- a/packages/lib/mail/sendResetPasswordSuccessMail.ts +++ /dev/null @@ -1,11 +0,0 @@ -import resetPasswordSuccessTemplate from "./resetPasswordSuccessTemplate"; -import { sendMail } from "./sendMail"; -import { User } from "@prisma/client"; - -export const sendResetPasswordSuccessMail = async (user: User) => { - await sendMail(user.email, "Password Reset Success!", resetPasswordSuccessTemplate(user)).catch( - (err) => { - throw err; - } - ); -}; diff --git a/packages/lib/server-only/document/send-document.tsx b/packages/lib/server-only/document/send-document.tsx index 221b4f7d4..febe619f0 100644 --- a/packages/lib/server-only/document/send-document.tsx +++ b/packages/lib/server-only/document/send-document.tsx @@ -4,6 +4,7 @@ import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite'; import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email'; +import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, SendStatus } from '@documenso/prisma/client'; diff --git a/packages/trpc/server/router.ts b/packages/trpc/server/router.ts index b8c97ba00..519096da9 100644 --- a/packages/trpc/server/router.ts +++ b/packages/trpc/server/router.ts @@ -14,6 +14,7 @@ export const appRouter = router({ profile: profileRouter, document: documentRouter, field: fieldRouter, + admin: adminRouter, shareLink: shareLinkRouter, }); diff --git a/packages/ui/icons/signature.tsx b/packages/ui/icons/signature.tsx index ec71086cb..6c118b222 100644 --- a/packages/ui/icons/signature.tsx +++ b/packages/ui/icons/signature.tsx @@ -1,29 +1,34 @@ -import type { LucideIcon, LucideProps } from 'lucide-react/dist/lucide-react'; +import { forwardRef } from 'react'; -// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -export const SignatureIcon = (({ - size = 24, - color = 'currentColor', - strokeWidth = 1.33, - absoluteStrokeWidth, - ...props -}: LucideProps) => { - return ( - - - - ); -}) as LucideIcon; +import type { LucideIcon } from 'lucide-react/dist/lucide-react'; + +export const SignatureIcon: LucideIcon = forwardRef( + ( + { size = 24, color = 'currentColor', strokeWidth = 1.33, absoluteStrokeWidth, ...props }, + ref, + ) => { + return ( + + + + ); + }, +); + +SignatureIcon.displayName = 'SignatureIcon'; diff --git a/packages/ui/package.json b/packages/ui/package.json index a5f3e4e2b..545e00ce6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -58,7 +58,7 @@ "clsx": "^1.2.1", "cmdk": "^0.2.0", "framer-motion": "^10.12.8", - "lucide-react": "^0.277.0", + "lucide-react": "^0.279.0", "luxon": "^3.4.2", "next": "14.0.0", "pdfjs-dist": "3.6.172", diff --git a/turbo.json b/turbo.json index 984a657f2..d32fec8b6 100644 --- a/turbo.json +++ b/turbo.json @@ -2,13 +2,8 @@ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - ".next/**", - "!.next/cache/**" - ] + "dependsOn": ["^build"], + "outputs": [".next/**", "!.next/cache/**"] }, "lint": {}, "clean": { @@ -30,9 +25,7 @@ "dependsOn": ["^build"] } }, - "globalDependencies": [ - "**/.env.*local" - ], + "globalDependencies": ["**/.env.*local"], "globalEnv": [ "APP_VERSION", "NEXTAUTH_URL",