Compare commits
50 Commits
feat/mania
...
feat/add-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe493adbb5 | ||
|
|
9437639c53 | ||
|
|
2afe8ecef0 | ||
|
|
fe939d8aa4 | ||
|
|
b918e6ba77 | ||
|
|
ae7a372ca0 | ||
|
|
a18932378f | ||
|
|
5f27eb20fd | ||
|
|
0d91e61a50 | ||
|
|
a2322e68d7 | ||
|
|
acb1fde5e7 | ||
|
|
f057666776 | ||
|
|
e82c12154b | ||
|
|
7efa8f0fc8 | ||
|
|
63c2c4acf4 | ||
|
|
5c81105e71 | ||
|
|
7bd847c0d3 | ||
|
|
a20b1e2f6a | ||
|
|
572f9d5ad6 | ||
|
|
e49c102e8c | ||
|
|
2348221b03 | ||
|
|
a0c327cfcf | ||
|
|
ddbeb9e3db | ||
|
|
e8205c1390 | ||
|
|
fbb53fc8c3 | ||
|
|
d7fed5a5dc | ||
|
|
11fce3ffba | ||
|
|
86dddb8a4d | ||
|
|
fba38f7b02 | ||
|
|
2e2ef4c20b | ||
|
|
3285881586 | ||
|
|
3ff61607a2 | ||
|
|
1b31d590a5 | ||
|
|
a882b72492 | ||
|
|
03fa6a3fa9 | ||
|
|
bcf1ffabd4 | ||
|
|
8eff8db990 | ||
|
|
cbb56891c4 | ||
|
|
5d5c5b2748 | ||
|
|
97b4655bc8 | ||
|
|
3c43d7af05 | ||
|
|
5beb1c6192 | ||
|
|
c2a9647c90 | ||
|
|
02424596db | ||
|
|
ba31138217 | ||
|
|
7a9d987258 | ||
|
|
b0592c5d00 | ||
|
|
aee103d4e3 | ||
|
|
0148c0f0cf | ||
|
|
e47ab838c5 |
14
.env.example
14
.env.example
@@ -15,6 +15,20 @@ NEXT_PRIVATE_DATABASE_URL="postgres://documenso:password@127.0.0.1:54320/documen
|
||||
# Defines the URL to use for the database when running migrations and other commands that won't work with a connection pool.
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL="postgres://documenso:password@127.0.0.1:54320/documenso"
|
||||
|
||||
# [[E2E Tests]]
|
||||
E2E_TEST_SIGNER_NAME="Test Signer"
|
||||
E2E_TEST_SIGNER_EMAIL="testsigner@mail.com"
|
||||
E2E_TEST_SIGNING_SUBJECT="Test subject"
|
||||
E2E_TEST_SIGNING_MESSAGE="Test message"
|
||||
# User for the "auth.setup.ts" file and the authenticated tests
|
||||
E2E_TEST_AUTHENTICATE_USERNAME="New user"
|
||||
E2E_TEST_AUTHENTICATE_USER_EMAIL="mytestnewuser@mail.com"
|
||||
E2E_TEST_AUTHENTICATE_USER_PASSWORD="new_test_password"
|
||||
# User for the *.unauthenticated.ts files
|
||||
E2E_TEST_USERNAME="Test"
|
||||
E2E_TEST_USER_EMAIL="mytestuser@mail.com"
|
||||
E2E_TEST_USER_PASSWORD="test_password"
|
||||
|
||||
# [[STORAGE]]
|
||||
# OPTIONAL: Defines the storage transport to use. Available options: database (default) | s3
|
||||
NEXT_PUBLIC_UPLOAD_TRANSPORT="database"
|
||||
|
||||
53
.github/workflows/e2e-tests.yml
vendored
Normal file
53
.github/workflows/e2e-tests.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [feat/refresh, feat/add-e2e-testing]
|
||||
pull_request:
|
||||
branches: [feat/refresh, feat/add-e2e-testing]
|
||||
jobs:
|
||||
e2e_tests:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
NEXT_PRIVATE_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/documenso
|
||||
NEXT_PRIVATE_DIRECT_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/documenso
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Generate package-lock.json
|
||||
run: npm cache clean --force && npm install
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Copy env
|
||||
run: cp .env.example .env
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Install Prisma Client
|
||||
run: npm install @prisma/client
|
||||
- name: Generate Prisma Client
|
||||
run: npm run prisma:generate -w @documenso/prisma
|
||||
- name: Create the database
|
||||
run: npm run prisma:migrate-dev -w @documenso/prisma
|
||||
- name: Run Playwright tests
|
||||
run: npm run ci
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -47,3 +47,8 @@ yarn-error.log*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
**/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
||||
playwright/.auth
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
|
||||
> 🦺 Documenso 1.0 is deployed to our <a href="https://documen.so/staging" target="_blank">Staging Environment</a>.
|
||||
>
|
||||
> The code be found on the [feat/refresh](https://github.com/documenso/documenso/tree/feat/refresh) branch.
|
||||
> The code can be found on the [feat/refresh](https://github.com/documenso/documenso/tree/feat/refresh) branch.
|
||||
>
|
||||
> The new Version will be release after the current testing phase.
|
||||
> The new Version will be released after the current testing phase.
|
||||
|
||||
|
||||
# Join us in testing Documenso 1.0 during [MALFUNCTION MANIA](https://documenso.com/blog/manfunction-mania)
|
||||
# Join us in testing Documenso 1.0 during [MALFUNCTION MANIA](https://documenso.com/blog/malfunction-mania)
|
||||
<div>
|
||||
<img style="display: block; height: 120px; width: 24%"
|
||||
src="https://github.com/documenso/documenso/assets/1309312/67e08c98-c153-4115-aa2d-77979bb12c94)">
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
62
apps/marketing/content/blog/shop.mdx
Normal file
62
apps/marketing/content/blog/shop.mdx
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Shop and Limited Edition "Mania" Shirt
|
||||
description: Happy Launch Week Day 3. The limited edition "Malfunction Mania" shirt is here. Grab it, while you can.
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2023-09-27
|
||||
tags:
|
||||
- Testing
|
||||
- Rewrite
|
||||
- Bounties
|
||||
---
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/mania-shirt.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Malfunctioning Mania Themed Documenso Shirt"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
The Limited Edition "Malfunction Mania" Shirt - Only during Malfunction Mania
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
> TLDR; We have a fancy limited edition shirt. Contribute to Malfunction Mania to get one.
|
||||
|
||||
We kicked off [Malfunction Mania](https://documenso.com/blog/malfunction-mania) yesterday, and the first [Issues](https://github.com/documenso/documenso/issues) are coming in. As mentioned, there will be dollar bounties, but we also wanted to celebrate entering the final stage of version 1.0 with something special. This is why we created the limited edition shirt for Malfunction Mania. It will only be available during the runtime of Malfunction Mania. We have yet to set an exact end date, the next event in October, however, is looming, ready to end MM.
|
||||
|
||||
## Documenso Merch Shop
|
||||
|
||||
The shirt will be available in our [merch shop](https://documen.so/shop) via a unique discount code. While the shirt will be gone after Malfunction Mania, the shop is here to stay and provide a well-deserved reward for great community members and contributors.
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/shop.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Malfunctioning Mania Themed Documenso Shirt"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
Merch at Documenso is always given to those who deserve it.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## How to get the shirt
|
||||
|
||||
If you have been following us, you know we are not big on formalities but highly value rewarding merit. That being said, any worthwhile contribution has a chance to get one. To inspire, here are a few ideas on how to contribute to securing one:
|
||||
|
||||
- Report a bug with detailed reproduction details
|
||||
- Fix a bug (you or somebody else reported)
|
||||
- Analyze and describe a usability or user experience shortcoming
|
||||
- Test the product in a systematic and least somewhat documented way
|
||||
- Engage in discussion about the current version and its choices
|
||||
- Raise awareness for Malfunction Mania and try out the [version currently in staging](https://documen.so/staging)
|
||||
- Review the version with a video, stream, or screenshots and post about it
|
||||
- Review existing or create missing documenso
|
||||
- ...
|
||||
|
||||
**[Follow Documenso on Twitter / X](https://documen.so/tw) and [join the Discord server](https://documen.so/discord) to get the latest about Malfunction Mania.**
|
||||
BIN
apps/marketing/public/blog/mania-shirt.png
Normal file
BIN
apps/marketing/public/blog/mania-shirt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 839 KiB |
BIN
apps/marketing/public/blog/shop.png
Normal file
BIN
apps/marketing/public/blog/shop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 956 KiB |
@@ -73,7 +73,7 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
|
||||
<ConfettiScreen duration={3000} gravity={0.075} initialVelocityY={50} wind={0.005} />
|
||||
)}
|
||||
|
||||
<h2 className="text-center text-2xl font-semibold leading-normal md:text-3xl lg:mb-2 lg:text-4xl">
|
||||
<h2 className="relative z-10 text-center text-2xl font-semibold leading-normal md:text-3xl lg:mb-2 lg:text-4xl">
|
||||
You have signed
|
||||
<span className="mt-2 block">{document.title}</span>
|
||||
</h2>
|
||||
@@ -84,17 +84,17 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
|
||||
signingCelebrationImage={signingCelebration}
|
||||
/>
|
||||
|
||||
<div className="mt-8 w-full">
|
||||
<div className="relative mt-8 w-full">
|
||||
<div className={cn('flex flex-col items-center', className)}>
|
||||
<div className="grid w-full max-w-sm grid-cols-2 gap-4">
|
||||
{/* TODO: Hook this up */}
|
||||
<Button variant="outline" className="flex-1" disabled>
|
||||
<Button variant="outline" className="flex-1 bg-transparent backdrop-blur-sm" disabled>
|
||||
<Share className="mr-2 h-5 w-5" />
|
||||
Share
|
||||
</Button>
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
className="flex-1 bg-transparent backdrop-blur-sm"
|
||||
fileName={document.title}
|
||||
documentData={document.documentData}
|
||||
disabled={document.status !== DocumentStatus.COMPLETED}
|
||||
|
||||
3
apps/web/.gitignore
vendored
Normal file
3
apps/web/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
playwright-report/
|
||||
playwright/.auth
|
||||
16
apps/web/global.teardown.ts
Normal file
16
apps/web/global.teardown.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { deleteUserAndItsData } from '@documenso/lib/server-only/user/delete-user-and-data';
|
||||
|
||||
async function teardown() {
|
||||
if (!process.env.E2E_TEST_USERNAME || !process.env.E2E_TEST_AUTHENTICATE_USERNAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteUserAndItsData(process.env.E2E_TEST_USERNAME);
|
||||
await deleteUserAndItsData(process.env.E2E_TEST_AUTHENTICATE_USERNAME);
|
||||
} catch (e) {
|
||||
throw new Error(`Error deleting user: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default teardown;
|
||||
@@ -8,6 +8,8 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test:dev": "playwright test",
|
||||
"test:e2e": "start-server-and-test \"npm run start\" http://localhost:3000 \"playwright test\"",
|
||||
"clean": "rimraf .next && rimraf node_modules",
|
||||
"copy:pdfjs": "node ../../scripts/copy-pdfjs.cjs"
|
||||
},
|
||||
@@ -41,9 +43,11 @@
|
||||
"sharp": "0.32.5",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"typescript": "5.1.6",
|
||||
"zod": "^3.21.4"
|
||||
"zod": "^3.21.4",
|
||||
"start-server-and-test": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.38.0",
|
||||
"@types/formidable": "^2.0.6",
|
||||
"@types/luxon": "^3.3.1",
|
||||
"@types/node": "20.1.0",
|
||||
|
||||
66
apps/web/playwright.config.ts
Normal file
66
apps/web/playwright.config.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
import { config as dotenvConfig } from 'dotenv';
|
||||
|
||||
dotenvConfig();
|
||||
|
||||
export const STORAGE_STATE = 'playwright/.auth/user.json';
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './src/tests/e2e',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
navigationTimeout: 60 * 1000,
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{ name: 'setup', testMatch: '**/*.setup.ts' },
|
||||
{
|
||||
name: 'Authenticated User Tests',
|
||||
testMatch: '*.authenticated.spec.ts',
|
||||
dependencies: ['setup', 'cleanup db'],
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: STORAGE_STATE,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Unauthenticated User Tests',
|
||||
dependencies: ['cleanup db'],
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
testMatch: '*.unauthenticated.spec.ts',
|
||||
testIgnore: ['*.setup.ts', '*.authenticated.spec.ts'],
|
||||
},
|
||||
{
|
||||
name: 'cleanup db',
|
||||
testMatch: /global.teardown\.ts/,
|
||||
},
|
||||
],
|
||||
|
||||
globalTeardown: './global.teardown.ts',
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ImageResponse } from 'next/server';
|
||||
import { ImageResponse, NextResponse } from 'next/server';
|
||||
|
||||
import { P, match } from 'ts-pattern';
|
||||
|
||||
@@ -21,7 +21,7 @@ type SharePageOpenGraphImageProps = {
|
||||
params: { slug: string };
|
||||
};
|
||||
|
||||
export default async function Image({ params: { slug } }: SharePageOpenGraphImageProps) {
|
||||
export async function GET(_request: Request, { params: { slug } }: SharePageOpenGraphImageProps) {
|
||||
const [interSemiBold, interRegular, caveatRegular, shareFrameImage] = await Promise.all([
|
||||
getAssetBuffer('/fonts/inter-semibold.ttf'),
|
||||
getAssetBuffer('/fonts/inter-regular.ttf'),
|
||||
@@ -32,7 +32,7 @@ export default async function Image({ params: { slug } }: SharePageOpenGraphImag
|
||||
const recipientOrSender = await getRecipientOrSenderByShareLinkSlug({ slug }).catch(() => null);
|
||||
|
||||
if (!recipientOrSender) {
|
||||
return null;
|
||||
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const isRecipient = 'Signature' in recipientOrSender;
|
||||
@@ -1,11 +1,39 @@
|
||||
import { Metadata } from 'next';
|
||||
import { headers } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { Redirect } from './redirect';
|
||||
import { APP_BASE_URL } from '@documenso/lib/constants/app';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Documenso - Share',
|
||||
type SharePageProps = {
|
||||
params: { slug: string };
|
||||
};
|
||||
|
||||
export default function SharePage() {
|
||||
return <Redirect />;
|
||||
export function generateMetadata({ params: { slug } }: SharePageProps) {
|
||||
return {
|
||||
title: 'Documenso - Share',
|
||||
description: 'I just signed a document with Documenso!',
|
||||
openGraph: {
|
||||
title: 'Documenso - Join the open source signing revolution',
|
||||
description: 'I just signed with Documenso!',
|
||||
type: 'website',
|
||||
images: [`${APP_BASE_URL}/share/${slug}/opengraph`],
|
||||
},
|
||||
twitter: {
|
||||
site: '@documenso',
|
||||
card: 'summary_large_image',
|
||||
images: [`${APP_BASE_URL}/share/${slug}/opengraph`],
|
||||
description: 'I just signed with Documenso!',
|
||||
},
|
||||
} satisfies Metadata;
|
||||
}
|
||||
|
||||
export default function SharePage() {
|
||||
const userAgent = headers().get('User-Agent') ?? '';
|
||||
|
||||
// https://stackoverflow.com/questions/47026171/how-to-detect-bots-for-open-graph-with-user-agent
|
||||
if (/bot|facebookexternalhit|WhatsApp|google|bing|duckduckbot|MetaInspector/i.test(userAgent)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
redirect(process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001');
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const Redirect = () => {
|
||||
useEffect(() => {
|
||||
window.location.href = process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001';
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -9,7 +9,7 @@ import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-f
|
||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
|
||||
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
|
||||
import { SigningCard } from '@documenso/ui/components/signing-card';
|
||||
import { SigningCard3D } from '@documenso/ui/components/signing-card';
|
||||
|
||||
import signingCelebration from '~/assets/signing-celebration.png';
|
||||
|
||||
@@ -53,11 +53,11 @@ export default async function CompletedSigningPage({
|
||||
recipient.email;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center pt-24">
|
||||
<div className="flex flex-col items-center pt-24 lg:pt-36 xl:pt-44">
|
||||
{/* Card with recipient */}
|
||||
<SigningCard name={recipientName} signingCelebrationImage={signingCelebration} />
|
||||
<SigningCard3D name={recipientName} signingCelebrationImage={signingCelebration} />
|
||||
|
||||
<div className="mt-6">
|
||||
<div className="relative mt-6 flex w-full flex-col items-center">
|
||||
{match(document.status)
|
||||
.with(DocumentStatus.COMPLETED, () => (
|
||||
<div className="text-documenso-700 flex items-center text-center">
|
||||
@@ -71,41 +71,44 @@ export default async function CompletedSigningPage({
|
||||
<span className="text-sm">Waiting for others to sign</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<h2 className="mt-6 max-w-[35ch] text-center text-2xl font-semibold leading-normal md:text-3xl lg:text-4xl">
|
||||
You have signed "{document.title}"
|
||||
</h2>
|
||||
|
||||
{match(document.status)
|
||||
.with(DocumentStatus.COMPLETED, () => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
Everyone has signed! You will receive an Email copy of the signed document.
|
||||
</p>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
You will receive an Email copy of the signed document once everyone has signed.
|
||||
</p>
|
||||
))}
|
||||
|
||||
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
|
||||
<ShareButton documentId={document.id} token={recipient.token} />
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
fileName={document.title}
|
||||
documentData={documentData}
|
||||
disabled={document.status !== DocumentStatus.COMPLETED}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground/60 mt-36 text-sm">
|
||||
Want to send slick signing links like this one?{' '}
|
||||
<Link
|
||||
href="https://documenso.com"
|
||||
className="text-documenso-700 hover:text-documenso-600"
|
||||
>
|
||||
Check out Documenso.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 className="mt-6 max-w-[35ch] text-center text-2xl font-semibold leading-normal md:text-3xl lg:text-4xl">
|
||||
You have signed "{document.title}"
|
||||
</h2>
|
||||
|
||||
{match(document.status)
|
||||
.with(DocumentStatus.COMPLETED, () => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
Everyone has signed! You will receive an Email copy of the signed document.
|
||||
</p>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
|
||||
You will receive an Email copy of the signed document once everyone has signed.
|
||||
</p>
|
||||
))}
|
||||
|
||||
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
|
||||
<ShareButton documentId={document.id} token={recipient.token} />
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
fileName={document.title}
|
||||
documentData={documentData}
|
||||
disabled={document.status !== DocumentStatus.COMPLETED}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground/60 mt-36 text-sm">
|
||||
Want to send slick signing links like this one?{' '}
|
||||
<Link href="https://documenso.com" className="text-documenso-700 hover:text-documenso-600">
|
||||
Check out Documenso.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { notFound, redirect } from 'next/navigation';
|
||||
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { viewedDocument } from '@documenso/lib/server-only/document/viewed-docum
|
||||
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
|
||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, FieldType, SigningStatus } from '@documenso/prisma/client';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
||||
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
||||
@@ -53,6 +53,13 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
|
||||
|
||||
const user = await getServerComponentSession();
|
||||
|
||||
if (
|
||||
document.status === DocumentStatus.COMPLETED ||
|
||||
recipient.signingStatus === SigningStatus.SIGNED
|
||||
) {
|
||||
redirect(`/sign/${token}/complete`);
|
||||
}
|
||||
|
||||
return (
|
||||
<SigningProvider email={recipient.email} fullName={recipient.name} signature={user?.signature}>
|
||||
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
|
||||
|
||||
@@ -40,7 +40,7 @@ export const StackAvatarsWithTooltip = ({
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<Tooltip delayDuration={200}>
|
||||
<TooltipTrigger className="flex cursor-pointer">
|
||||
{children || <StackAvatars recipients={recipients} />}
|
||||
</TooltipTrigger>
|
||||
|
||||
35
apps/web/src/tests/e2e/auth.setup.ts
Normal file
35
apps/web/src/tests/e2e/auth.setup.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { type Page, expect, test as setup } from '@playwright/test';
|
||||
|
||||
import { STORAGE_STATE } from '../../../playwright.config';
|
||||
|
||||
const username = process.env.E2E_TEST_AUTHENTICATE_USERNAME || '';
|
||||
const email = process.env.E2E_TEST_AUTHENTICATE_USER_EMAIL || '';
|
||||
const password = process.env.E2E_TEST_AUTHENTICATE_USER_PASSWORD || '';
|
||||
|
||||
setup('authenticate', async ({ page }: { page: Page }) => {
|
||||
await page.goto('/signup');
|
||||
await page.getByLabel('Name').fill(username);
|
||||
await page.getByLabel('Email').fill(email);
|
||||
await page.getByLabel('Password', { exact: true }).fill(password);
|
||||
|
||||
const canvas = page.locator('canvas');
|
||||
const box = await canvas.boundingBox();
|
||||
|
||||
if (box) {
|
||||
await page.mouse.move(box.x + box.width / 8, box.y + box.height / 6);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(box.x + box.width / 8, box.y + box.height / 6);
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Sign Up' }).click();
|
||||
|
||||
await page.goto('/');
|
||||
await page.getByLabel('Email').fill(email);
|
||||
await page.getByLabel('Password', { exact: true }).fill(password);
|
||||
await page.getByRole('button', { name: 'Sign In' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await expect(page).toHaveURL('/documents');
|
||||
await page.context().storageState({ path: STORAGE_STATE });
|
||||
});
|
||||
57
apps/web/src/tests/e2e/documenso.pdf
Normal file
57
apps/web/src/tests/e2e/documenso.pdf
Normal file
@@ -0,0 +1,57 @@
|
||||
%PDF-1.4
|
||||
%<25><><EFBFBD><EFBFBD>
|
||||
1 0 obj
|
||||
<</Title (documenso)
|
||||
/Producer (Skia/PDF m118 Google Docs Renderer)>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<</ca 1
|
||||
/BM /Normal>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<</Length 84>> stream
|
||||
1 0 0 -1 0 842 cm
|
||||
q
|
||||
.75 0 0 .75 0 0 cm
|
||||
1 1 1 RG 1 1 1 rg
|
||||
/G3 gs
|
||||
0 0 794 1123 re
|
||||
f
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
2 0 obj
|
||||
<</Type /Page
|
||||
/Resources <</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
|
||||
/ExtGState <</G3 3 0 R>>>>
|
||||
/MediaBox [0 0 596 842]
|
||||
/Contents 4 0 R
|
||||
/StructParents 0
|
||||
/Parent 5 0 R>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<</Type /Pages
|
||||
/Count 1
|
||||
/Kids [2 0 R]>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<</Type /Catalog
|
||||
/Pages 5 0 R>>
|
||||
endobj
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000269 00000 n
|
||||
0000000100 00000 n
|
||||
0000000137 00000 n
|
||||
0000000457 00000 n
|
||||
0000000512 00000 n
|
||||
trailer
|
||||
<</Size 7
|
||||
/Root 6 0 R
|
||||
/Info 1 0 R>>
|
||||
startxref
|
||||
559
|
||||
%%EOF
|
||||
@@ -0,0 +1,45 @@
|
||||
import { type Page, expect, test } from '@playwright/test';
|
||||
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
/*
|
||||
Using them sequentially so the 2nd test
|
||||
uses the details from the 1st (registration) test
|
||||
*/
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
const username = process.env.E2E_TEST_USERNAME || '';
|
||||
const email = process.env.E2E_TEST_USER_EMAIL || '';
|
||||
const password = process.env.E2E_TEST_USER_PASSWORD || '';
|
||||
|
||||
test('user can sign up with email and password', async ({ page }: { page: Page }) => {
|
||||
await page.goto('/signup');
|
||||
await page.getByLabel('Name').fill(username);
|
||||
await page.getByLabel('Email').fill(email);
|
||||
await page.getByLabel('Password', { exact: true }).fill(password);
|
||||
|
||||
const canvas = page.locator('canvas');
|
||||
const box = await canvas.boundingBox();
|
||||
|
||||
if (box) {
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Sign Up' }).click();
|
||||
await page.waitForURL('/documents');
|
||||
|
||||
await expect(page).toHaveURL('/documents');
|
||||
});
|
||||
|
||||
test('user can login with user and password', async ({ page }: { page: Page }) => {
|
||||
await page.goto('/signin');
|
||||
await page.getByLabel('Email').fill(email);
|
||||
await page.getByLabel('Password', { exact: true }).fill(password);
|
||||
await page.getByRole('button', { name: 'Sign In' }).click();
|
||||
|
||||
await page.waitForURL('/documents');
|
||||
await expect(page).toHaveURL('/documents');
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import path from 'path';
|
||||
|
||||
import { expect, test } from '../test-fixtures/documents-page/documents-page';
|
||||
|
||||
const signer_name = process.env.E2E_TEST_SIGNER_NAME;
|
||||
const signer_email = process.env.E2E_TEST_SIGNER_EMAIL;
|
||||
const signing_subject = process.env.E2E_TEST_SIGNING_SUBJECT;
|
||||
const signing_message = process.env.E2E_TEST_SIGNING_MESSAGE;
|
||||
|
||||
if (!signer_name || !signer_email || !signing_subject || !signing_message) {
|
||||
throw new Error('Required environment variables for tests are not defined');
|
||||
}
|
||||
|
||||
test.describe('Document upload test', () => {
|
||||
test.beforeEach(async ({ documentsPage }) => {
|
||||
await documentsPage.uploadDocument(path.join(__dirname, './documenso.pdf'));
|
||||
});
|
||||
|
||||
test('user can see /documents page', async ({ page, documentsPage }) => {
|
||||
await documentsPage.goToDocumentsPage();
|
||||
await expect(page).toHaveTitle('Documenso - The Open Source DocuSign Alternative');
|
||||
});
|
||||
|
||||
test('user can upload a document succesfully', async ({ page }) => {
|
||||
await expect(page.getByText('Drag & drop your document here.')).toBeVisible();
|
||||
});
|
||||
|
||||
test('user can send the document for signing succesfully', async ({ documentsPage, page }) => {
|
||||
await documentsPage.addSigner(signer_email, signer_name);
|
||||
await documentsPage.addSignatureField(signer_name);
|
||||
await documentsPage.addSubjectAndMessage(signing_subject, signing_message);
|
||||
|
||||
await expect(page).toHaveURL('/documents');
|
||||
await expect(page.getByRole('status').locator('div').nth(2)).toHaveText(
|
||||
'Your document has been sent successfully.',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
|
||||
export class DocumentsPage {
|
||||
private readonly fileInput: Locator;
|
||||
private readonly subject: Locator;
|
||||
private readonly message: Locator;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
constructor(public readonly page: Page) {
|
||||
this.fileInput = this.page.locator('input[type=file]');
|
||||
this.subject = this.page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Subject \(Optional\)$/ })
|
||||
.locator('input');
|
||||
this.message = this.page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Message \(Optional\)$/ })
|
||||
.locator('textarea');
|
||||
}
|
||||
|
||||
async goToDocumentsPage() {
|
||||
await this.page.goto('/documents');
|
||||
}
|
||||
|
||||
async uploadDocument(filePath: string) {
|
||||
await this.goToDocumentsPage();
|
||||
|
||||
await this.fileInput.setInputFiles(filePath);
|
||||
}
|
||||
|
||||
async addSigner(email: string, name: string) {
|
||||
await this.page.getByLabel('Email*').fill(email);
|
||||
await this.page.getByLabel('Name').fill(name);
|
||||
await this.page.getByRole('button', { name: 'Continue' }).click();
|
||||
}
|
||||
|
||||
async addSignatureField(name: string) {
|
||||
await this.page
|
||||
.getByRole('button', { name: `${name} Signature` })
|
||||
.dragTo(this.page.locator('canvas'));
|
||||
await this.page.getByRole('button', { name: 'Continue' }).click();
|
||||
}
|
||||
|
||||
async addSubjectAndMessage(subject: string, message: string) {
|
||||
await this.subject.fill(subject);
|
||||
await this.message.fill(message);
|
||||
await this.page.getByRole('button', { name: 'Send' }).click();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { test as base } from '@playwright/test';
|
||||
|
||||
import { DocumentsPage } from './DocumentsPageObject';
|
||||
|
||||
export const test = base.extend<{ documentsPage: DocumentsPage }>({
|
||||
documentsPage: async ({ page }, use) => {
|
||||
const documentsPage = new DocumentsPage(page);
|
||||
|
||||
await use(documentsPage);
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from '@playwright/test';
|
||||
320
package-lock.json
generated
320
package-lock.json
generated
@@ -98,11 +98,13 @@
|
||||
"react-icons": "^4.8.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"sharp": "0.32.5",
|
||||
"start-server-and-test": "^2.0.1",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"typescript": "5.1.6",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.38.0",
|
||||
"@types/formidable": "^2.0.6",
|
||||
"@types/luxon": "^3.3.1",
|
||||
"@types/node": "20.1.0",
|
||||
@@ -2450,6 +2452,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
|
||||
},
|
||||
"node_modules/@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.0.tgz",
|
||||
@@ -3786,6 +3801,21 @@
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
|
||||
"integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.38.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz",
|
||||
@@ -5435,6 +5465,24 @@
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
|
||||
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/formula": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
|
||||
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
|
||||
},
|
||||
"node_modules/@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
|
||||
},
|
||||
"node_modules/@sindresorhus/slugify": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz",
|
||||
@@ -7619,6 +7667,14 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/check-more-types": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
|
||||
"integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
@@ -9124,6 +9180,11 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
|
||||
},
|
||||
"node_modules/duplexer2": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
|
||||
@@ -10328,6 +10389,20 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/event-stream": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
|
||||
"integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
|
||||
"dependencies": {
|
||||
"duplexer": "~0.1.1",
|
||||
"from": "~0",
|
||||
"map-stream": "~0.1.0",
|
||||
"pause-stream": "0.0.11",
|
||||
"split": "0.3",
|
||||
"stream-combiner": "~0.0.4",
|
||||
"through": "~2.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
@@ -10727,6 +10802,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
"integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g=="
|
||||
},
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
@@ -12165,6 +12245,18 @@
|
||||
"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
|
||||
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "17.10.2",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.10.2.tgz",
|
||||
"integrity": "sha512-hcVhjBxRNW/is3nNLdGLIjkgXetkeGc2wyhydhz8KumG23Aerk4HPjU5zaPAMRqXQFc0xNqXTC7+zQjxr0GlKA==",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0",
|
||||
"@hapi/topo": "^5.0.0",
|
||||
"@sideway/address": "^4.1.3",
|
||||
"@sideway/formula": "^3.0.1",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz",
|
||||
@@ -12399,6 +12491,14 @@
|
||||
"language-subtag-registry": "~0.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lazy-ass": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
|
||||
"integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
|
||||
"engines": {
|
||||
"node": "> 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/leac": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
|
||||
@@ -12946,6 +13046,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/map-stream": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
|
||||
"integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g=="
|
||||
},
|
||||
"node_modules/markdown-extensions": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
||||
@@ -14999,6 +15104,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pause-stream": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
|
||||
"integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
|
||||
"dependencies": {
|
||||
"through": "~2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/pdf-lib": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
|
||||
@@ -15097,6 +15210,36 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
|
||||
"integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.38.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
|
||||
"integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.27",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz",
|
||||
@@ -15647,6 +15790,20 @@
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
|
||||
},
|
||||
"node_modules/ps-tree": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
|
||||
"integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
|
||||
"dependencies": {
|
||||
"event-stream": "=3.3.4"
|
||||
},
|
||||
"bin": {
|
||||
"ps-tree": "bin/ps-tree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
@@ -17267,6 +17424,14 @@
|
||||
"resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.14.tgz",
|
||||
"integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA=="
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
@@ -17642,6 +17807,17 @@
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
|
||||
"integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w=="
|
||||
},
|
||||
"node_modules/split": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
|
||||
"integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
|
||||
"dependencies": {
|
||||
"through": "2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
|
||||
@@ -17673,6 +17849,121 @@
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/start-server-and-test": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz",
|
||||
"integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==",
|
||||
"dependencies": {
|
||||
"arg": "^5.0.2",
|
||||
"bluebird": "3.7.2",
|
||||
"check-more-types": "2.24.0",
|
||||
"debug": "4.3.4",
|
||||
"execa": "5.1.1",
|
||||
"lazy-ass": "1.6.0",
|
||||
"ps-tree": "1.2.0",
|
||||
"wait-on": "7.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"server-test": "src/bin/start.js",
|
||||
"start-server-and-test": "src/bin/start.js",
|
||||
"start-test": "src/bin/start.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^6.0.0",
|
||||
"human-signals": "^2.1.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^4.0.1",
|
||||
"onetime": "^5.1.2",
|
||||
"signal-exit": "^3.0.3",
|
||||
"strip-final-newline": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||
"engines": {
|
||||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||
"dependencies": {
|
||||
"path-key": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/onetime": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"dependencies": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/start-server-and-test/node_modules/strip-final-newline": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
@@ -17681,6 +17972,14 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-combiner": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
|
||||
"integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
|
||||
"dependencies": {
|
||||
"duplexer": "~0.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-shift": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
||||
@@ -18226,8 +18525,7 @@
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"node_modules/through2": {
|
||||
"version": "4.0.2",
|
||||
@@ -19488,6 +19786,24 @@
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/wait-on": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz",
|
||||
"integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==",
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"joi": "^17.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.7",
|
||||
"rxjs": "^7.8.0"
|
||||
},
|
||||
"bin": {
|
||||
"wait-on": "bin/wait-on"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev --filter=@documenso/web --filter=@documenso/marketing",
|
||||
"start": "cd apps && cd web && next start",
|
||||
"test": "cd apps && cd web && npm run test:e2e",
|
||||
"lint": "turbo run lint",
|
||||
"format": "prettier --write \"**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,mdx}\"",
|
||||
"prepare": "husky install",
|
||||
"commitlint": "commitlint --edit",
|
||||
"ci": "turbo run build test:e2e lint",
|
||||
"clean": "turbo run clean && rimraf node_modules"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
41
packages/lib/server-only/user/delete-user-and-data.ts
Normal file
41
packages/lib/server-only/user/delete-user-and-data.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export const deleteUserAndItsData = async (name: string) => {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
name: {
|
||||
contains: name,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`User with name ${name} not found`);
|
||||
}
|
||||
|
||||
const document = await prisma.document.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
select: {
|
||||
documentData: {
|
||||
select: {
|
||||
data: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
await prisma.user.delete({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
}),
|
||||
await prisma.documentData.deleteMany({
|
||||
where: {
|
||||
data: document[0]?.documentData.data,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "PasswordResetToken" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"userId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "PasswordResetToken_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PasswordResetToken_token_key" ON "PasswordResetToken"("token");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PasswordResetToken" ADD CONSTRAINT "PasswordResetToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `expiry` to the `PasswordResetToken` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "PasswordResetToken" ADD COLUMN "expiry" TIMESTAMP(3) NOT NULL;
|
||||
@@ -1,3 +1,5 @@
|
||||
DROP TABLE IF EXISTS "PasswordResetToken" CASCADE;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PasswordResetToken" (
|
||||
"id" SERIAL NOT NULL,
|
||||
|
||||
@@ -148,7 +148,7 @@ const SigningCardContent = ({ className, name }: SigningCardContentProps) => {
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
'group mx-auto flex aspect-[21/9] w-full items-center justify-center',
|
||||
'group z-10 mx-auto flex aspect-[21/9] w-full items-center justify-center',
|
||||
className,
|
||||
)}
|
||||
degrees={-145}
|
||||
@@ -180,14 +180,14 @@ type SigningCardImageProps = {
|
||||
const SigningCardImage = ({ signingCelebrationImage }: SigningCardImageProps) => {
|
||||
return (
|
||||
<motion.div
|
||||
className="pointer-events-none absolute -inset-32 -z-10 flex items-center justify-center md:-inset-44 xl:-inset-60 2xl:-inset-80"
|
||||
className="pointer-events-none absolute -inset-32 -z-50 flex items-center justify-center md:-inset-44 xl:-inset-60 2xl:-inset-80"
|
||||
initial={{
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
scale: 0.6,
|
||||
}}
|
||||
animate={{
|
||||
scale: 1,
|
||||
opacity: 0.5,
|
||||
opacity: 0.6,
|
||||
}}
|
||||
transition={{
|
||||
delay: 0.5,
|
||||
@@ -197,7 +197,7 @@ const SigningCardImage = ({ signingCelebrationImage }: SigningCardImageProps) =>
|
||||
<Image
|
||||
src={signingCelebrationImage}
|
||||
alt="background pattern"
|
||||
className="w-full dark:invert dark:sepia"
|
||||
className="w-full dark:brightness-150 dark:contrast-[70%] dark:invert dark:sepia"
|
||||
style={{
|
||||
mask: 'radial-gradient(rgba(255, 255, 255, 1) 0%, transparent 67%)',
|
||||
WebkitMask: 'radial-gradient(rgba(255, 255, 255, 1) 0%, transparent 67%)',
|
||||
|
||||
28
turbo.json
28
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": {
|
||||
@@ -17,13 +12,26 @@
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"test:e2e": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
},
|
||||
"globalDependencies": [
|
||||
"**/.env.*local"
|
||||
],
|
||||
"globalDependencies": ["**/.env.*local"],
|
||||
"globalEnv": [
|
||||
"APP_VERSION",
|
||||
|
||||
"E2E_TEST_USERNAME",
|
||||
"E2E_TEST_USER_EMAIL",
|
||||
"E2E_TEST_USER_PASSWORD",
|
||||
"E2E_TEST_SIGNER_NAME",
|
||||
"E2E_TEST_SIGNER_EMAIL",
|
||||
"E2E_TEST_SIGNING_SUBJECT",
|
||||
"E2E_TEST_SIGNING_MESSAGE",
|
||||
"E2E_TEST_AUTHENTICATE_USERNAME",
|
||||
"E2E_TEST_AUTHENTICATE_USER_EMAIL",
|
||||
"E2E_TEST_AUTHENTICATE_USER_PASSWORD",
|
||||
|
||||
"NEXTAUTH_URL",
|
||||
"NEXTAUTH_SECRET",
|
||||
"NEXT_PUBLIC_PROJECT",
|
||||
|
||||
Reference in New Issue
Block a user