diff --git a/.env.example b/.env.example index a8cab596e..f723e1c66 100644 --- a/.env.example +++ b/.env.example @@ -211,3 +211,17 @@ NEXT_PRIVATE_LOGGER_FILE_PATH= # [[PLAIN SUPPORT]] NEXT_PRIVATE_PLAIN_API_KEY= + +# [[DOCUMENT CONVERSION]] +# OPTIONAL: Base URL of a Gotenberg-compatible service used to convert uploaded +# DOCX files to PDF on the server. When unset, DOCX uploads are disabled and +# only PDF is accepted. The dev docker compose exposes Gotenberg on port 3005. +# NEXT_PRIVATE_DOCUMENT_CONVERSION_URL="http://localhost:3005" +# OPTIONAL: Per-request timeout in milliseconds for the conversion service. +# Defaults to 30000 (30s) if unset. +# NEXT_PRIVATE_DOCUMENT_CONVERSION_TIMEOUT_MS=30000 +# OPTIONAL: HTTP Basic auth credentials for the conversion service. Set both +# when the service is started with `--api-enable-basic-auth` (the dev compose +# does this; the matching values there are `documenso` / `password`). +# NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME=documenso +# NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD=password diff --git a/.github/actions/node-install/action.yml b/.github/actions/node-install/action.yml index bea5a0a49..b01a28740 100644 --- a/.github/actions/node-install/action.yml +++ b/.github/actions/node-install/action.yml @@ -1,4 +1,4 @@ -name: 'Setup node and cache node_modules' +name: 'Setup node' inputs: node_version: required: false @@ -16,25 +16,7 @@ runs: shell: bash run: corepack enable npm - - name: Cache npm - uses: actions/cache@v3 - with: - path: ~/.npm - key: npm-${{ hashFiles('package-lock.json') }} - restore-keys: npm- - - - name: Cache node_modules - uses: actions/cache@v3 - id: cache-node-modules - with: - path: | - node_modules - packages/*/node_modules - apps/*/node_modules - key: modules-${{ hashFiles('package-lock.json') }} - - name: Install dependencies - if: steps.cache-node-modules.outputs.cache-hit != 'true' shell: bash run: | npm ci --no-audit diff --git a/.github/actions/playwright-install/action.yml b/.github/actions/playwright-install/action.yml index 27d0e66b4..ac3202f3d 100644 --- a/.github/actions/playwright-install/action.yml +++ b/.github/actions/playwright-install/action.yml @@ -1,19 +1,8 @@ name: Install playwright binaries -description: 'Install playwright, cache and restore if necessary' +description: 'Install playwright' runs: using: 'composite' steps: - - name: Cache playwright - id: cache-playwright - uses: actions/cache@v3 - with: - path: | - ~/.cache/ms-playwright - ${{ github.workspace }}/node_modules/playwright - key: playwright-${{ hashFiles('**/package-lock.json') }} - restore-keys: playwright- - - name: Install playwright - if: steps.cache-playwright.outputs.cache-hit != 'true' run: npx playwright install --with-deps shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a7afc44f..55ed7f27d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,14 +41,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Cache Docker layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - name: Build Docker Image uses: docker/build-push-action@v5 with: @@ -56,13 +48,3 @@ jobs: context: . file: ./docker/Dockerfile tags: documenso-${{ github.sha }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - - - # Temp fix - # https://github.com/docker/build-push-action/issues/252 - # https://github.com/moby/buildkit/issues/1896 - name: Move cache - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/issue-assignee-check.yml b/.github/workflows/issue-assignee-check.yml index b601a8dc3..de53564ec 100644 --- a/.github/workflows/issue-assignee-check.yml +++ b/.github/workflows/issue-assignee-check.yml @@ -20,7 +20,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: '18' - cache: npm - name: Install Octokit run: npm install @octokit/rest@18 diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 1a5afd359..15fe7cbfa 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -1,7 +1,7 @@ name: 'PR Labeler' on: - - pull_request_target + - pull_request concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/pr-review-reminder.yml b/.github/workflows/pr-review-reminder.yml index c81d9a34e..67dc32f34 100644 --- a/.github/workflows/pr-review-reminder.yml +++ b/.github/workflows/pr-review-reminder.yml @@ -20,7 +20,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: '18' - cache: npm - name: Install Octokit run: npm install @octokit/rest@18 diff --git a/.github/workflows/semantic-pull-requests.yml b/.github/workflows/semantic-pull-requests.yml index 37b764652..76a1b2f42 100644 --- a/.github/workflows/semantic-pull-requests.yml +++ b/.github/workflows/semantic-pull-requests.yml @@ -1,7 +1,7 @@ name: 'Validate PR Name' on: - pull_request_target: + pull_request: types: - opened - reopened diff --git a/README.md b/README.md index b0fc9990f..f5117abd0 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ · Website · + Documentation + · Issues · Upcoming Releases @@ -146,45 +148,7 @@ npm run d ### Manual Setup -Follow these steps to setup Documenso on your local machine: - -1. [Fork this repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) to your GitHub account. - -After forking the repository, clone it to your local device by using the following command: - -```sh -git clone https://github.com//documenso -``` - -2. Run `npm i` in the root directory - -3. Create your `.env` from the `.env.example`. You can use `cp .env.example .env` to get started with our handpicked defaults. - -4. Set the following environment variables: - - - NEXTAUTH_SECRET - - NEXT_PUBLIC_WEBAPP_URL - - NEXT_PRIVATE_DATABASE_URL - - NEXT_PRIVATE_DIRECT_DATABASE_URL - - NEXT_PRIVATE_SMTP_FROM_NAME - - NEXT_PRIVATE_SMTP_FROM_ADDRESS - -5. Create the database schema by running `npm run prisma:migrate-dev` - -6. Run `npm run translate:compile` in the root directory to compile lingui - -7. Run `npm run dev` in the root directory to start - -8. Register a new user at http://localhost:3000/signup - ---- - -- Optional: Seed the database using `npm run prisma:seed -w @documenso/prisma` to create a test user and document. -- Optional: Create your own signing certificate. - - To generate your own using these steps and a Linux Terminal or Windows Subsystem for Linux (WSL), see **[Create your own signing certificate](./SIGNING.md)**. -- Optional: Configure job provider for document reminders. - - The default local job provider does not support scheduled jobs required for document reminders. - - To enable reminders, set `NEXT_PRIVATE_JOBS_PROVIDER=inngest` and provide `NEXT_PRIVATE_INNGEST_EVENT_KEY` in your `.env` file. +Follow the [manual setup guide](https://docs.documenso.com/docs/developers/local-development/manual) to configure Documenso on your local machine. ### Run in Gitpod @@ -204,138 +168,44 @@ If you're a visual learner and prefer to watch a video walkthrough of setting up ## Docker -We provide a Docker container for Documenso, which is published on both DockerHub and GitHub Container Registry. +We provide official Docker images on [DockerHub](https://hub.docker.com/r/documenso/documenso) and [GitHub Container Registry](https://ghcr.io/documenso/documenso). -- DockerHub: [https://hub.docker.com/r/documenso/documenso](https://hub.docker.com/r/documenso/documenso) -- GitHub Container Registry: [https://ghcr.io/documenso/documenso](https://ghcr.io/documenso/documenso) - -You can pull the Docker image from either of these registries and run it with your preferred container hosting provider. - -Please note that you will need to provide environment variables for connecting to the database, mailserver, and so forth. - -For detailed instructions on how to configure and run the Docker container, please refer to the [Docker README](./docker/README.md) in the `docker` directory. +For setup instructions, see the [Docker Deployment](https://docs.documenso.com/docs/self-hosting/deployment/docker) and [Docker Compose](https://docs.documenso.com/docs/self-hosting/deployment/docker-compose) guides. ## Self Hosting -We support a variety of deployment methods, and are actively working on adding more. Stay tuned for updates! +We support a variety of deployment methods including Docker, Docker Compose, Railway, Kubernetes, and manual deployment. -### Fetch, configure, and build +For full instructions, requirements, and configuration details, see the [Self Hosting documentation](https://docs.documenso.com/docs/self-hosting). -First, clone the code from Github: +### One-Click Deploys -``` -git clone https://github.com/documenso/documenso.git -``` - -Then, inside the `documenso` folder, copy the example env file: - -``` -cp .env.example .env -``` - -The following environment variables must be set: - -- `NEXTAUTH_SECRET` -- `NEXT_PUBLIC_WEBAPP_URL` -- `NEXT_PRIVATE_DATABASE_URL` -- `NEXT_PRIVATE_DIRECT_DATABASE_URL` -- `NEXT_PRIVATE_SMTP_FROM_NAME` -- `NEXT_PRIVATE_SMTP_FROM_ADDRESS` - -> If you are using a reverse proxy in front of Documenso, don't forget to provide the public URL for the `NEXT_PUBLIC_WEBAPP_URL` variable! - -Now you can install the dependencies and build it: - -``` -npm i -npm run build -npm run prisma:migrate-deploy -``` - -Finally, you can start it with: - -``` -cd apps/remix -npm run start -``` - -This will start the server on `localhost:3000`. For now, any reverse proxy can then do the frontend and SSL termination. - -> If you want to run with another port than 3000, you can start the application with `next -p ` from the `apps/remix` folder. - -### Run as a service - -You can use a systemd service file to run the app. Here is a simple example of the service running on port 3500 (using 3000 by default): - -```bash -[Unit] -Description=documenso -After=network.target - -[Service] -Environment=PATH=/path/to/your/node/binaries -Type=simple -User=www-data -WorkingDirectory=/var/www/documenso/apps/remix -ExecStart=/usr/bin/next start -p 3500 -TimeoutSec=15 -Restart=always - -[Install] -WantedBy=multi-user.target -``` - -### Railway +#### Railway [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/bG6D4p) -### Render +#### Render [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/documenso/documenso) -### Koyeb +#### Koyeb [![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?type=git&repository=github.com/documenso/documenso&branch=main&name=documenso-app&builder=dockerfile&dockerfile=/docker/Dockerfile) -## Elestio +#### Elestio [![Deploy on Elestio](https://elest.io/images/logos/deploy-to-elestio-btn.png)](https://elest.io/open-source/documenso) ## Troubleshooting +For troubleshooting self-hosted deployments, see the [Troubleshooting guide](https://docs.documenso.com/docs/self-hosting/maintenance/troubleshooting) and [Tips & Common Pitfalls](https://docs.documenso.com/docs/self-hosting/getting-started/tips). + ### I'm not receiving any emails when using the developer quickstart. When using the developer quickstart, an [Inbucket](https://inbucket.org/) server will be spun up in a docker container that will store all outgoing emails locally for you to view. The Web UI can be found at http://localhost:9000, while the SMTP port will be on localhost:2500. -### Support IPv6 - -If you are deploying to a cluster that uses only IPv6, You can use a custom command to pass a parameter to the Remix start command - -For local docker run - -```bash -docker run -it documenso:latest npm run start -- -H :: -``` - -For k8s or docker-compose - -```yaml -containers: - - name: documenso - image: documenso:latest - imagePullPolicy: IfNotPresent - command: - - npm - args: - - run - - start - - -- - - -H - - '::' -``` - ### I can't see environment variables in my package scripts. Wrap your package script with the `with:env` script like such: diff --git a/apps/docs/content/docs/developers/embedding/authoring/index.mdx b/apps/docs/content/docs/developers/embedding/editor/index.mdx similarity index 59% rename from apps/docs/content/docs/developers/embedding/authoring/index.mdx rename to apps/docs/content/docs/developers/embedding/editor/index.mdx index 834d3cc71..bfd056458 100644 --- a/apps/docs/content/docs/developers/embedding/authoring/index.mdx +++ b/apps/docs/content/docs/developers/embedding/editor/index.mdx @@ -1,24 +1,24 @@ --- -title: Authoring +title: Editor description: Embed document, template, and envelope creation directly in your application. --- import { Callout } from 'fumadocs-ui/components/callout'; -In addition to embedding signing, Documenso supports embedded authoring. It allows your users to create and edit documents, templates, and envelopes without leaving your application. +In addition to embedding signing, Documenso supports embedded editor. It allows your users to create and edit documents, templates, and envelopes without leaving your application. - Embedded authoring is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is + Embedded editor is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is also available as a paid add-on for the [Platform Plan](https://documen.so/platform-cta-pricing). Contact sales for access. ## Versions -Embedded authoring is available in two versions: +Embedded editor is available in two versions: -- **[V1 Authoring](/docs/developers/embedding/authoring/v1)** — Works with V1 Documents and Templates. -- **[V2 Authoring](/docs/developers/embedding/authoring/v2)** — Works with Envelopes, which are the unified model for documents and templates. +- **[V1 Editor](/docs/developers/embedding/editor/v1)** — Works with V1 Documents and Templates. +- **[V2 Editor](/docs/developers/embedding/editor/v2)** — Works with Envelopes, which are the unified model for documents and templates. ### Comparison @@ -32,7 +32,7 @@ Embedded authoring is available in two versions: ## Presign Tokens -Before using any authoring component, obtain a presign token from your backend: +Before using any editor component, obtain a presign token from your backend: ``` POST /api/v2/embedding/create-presign-token @@ -50,7 +50,7 @@ See the [API documentation](https://openapi.documenso.com/reference#tag/embeddin ## Next Steps -- [V1 Authoring](/docs/developers/embedding/authoring/v1) — Create and edit documents and templates using V1 components -- [V2 Authoring](/docs/developers/embedding/authoring/v2) — Create and edit envelopes using V2 components +- [V1 Editor](/docs/developers/embedding/editor/v1) — Create and edit documents and templates using V1 components +- [V2 Editor](/docs/developers/embedding/editor/v2) — Create and edit envelopes using V2 components - [CSS Variables](/docs/developers/embedding/css-variables) — Customize the appearance of embedded components - [SDKs](/docs/developers/embedding/sdks) — Framework-specific SDK documentation diff --git a/apps/docs/content/docs/developers/embedding/authoring/meta.json b/apps/docs/content/docs/developers/embedding/editor/meta.json similarity index 53% rename from apps/docs/content/docs/developers/embedding/authoring/meta.json rename to apps/docs/content/docs/developers/embedding/editor/meta.json index 45cb7a47b..b6c6a5b07 100644 --- a/apps/docs/content/docs/developers/embedding/authoring/meta.json +++ b/apps/docs/content/docs/developers/embedding/editor/meta.json @@ -1,4 +1,4 @@ { - "title": "Authoring", + "title": "Editor", "pages": ["v1", "v2"] } diff --git a/apps/docs/content/docs/developers/embedding/authoring/v1.mdx b/apps/docs/content/docs/developers/embedding/editor/v1.mdx similarity index 91% rename from apps/docs/content/docs/developers/embedding/authoring/v1.mdx rename to apps/docs/content/docs/developers/embedding/editor/v1.mdx index f10763fb3..0926aec68 100644 --- a/apps/docs/content/docs/developers/embedding/authoring/v1.mdx +++ b/apps/docs/content/docs/developers/embedding/editor/v1.mdx @@ -1,21 +1,21 @@ --- -title: V1 Authoring +title: V1 Editor description: Embed V1 document and template creation directly in your application. --- import { Callout } from 'fumadocs-ui/components/callout'; -V1 authoring components allow your users to create and edit documents and templates using the V1 Documents and Templates API without leaving your application. +V1 editor components allow your users to create and edit documents and templates using the V1 Documents and Templates API without leaving your application. - Embedded authoring is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is + Embedded editor is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is also available as a paid add-on for the [Platform Plan](https://documen.so/platform-cta-pricing). Contact sales for access. ## Components -The SDK provides four V1 authoring components: +The SDK provides four V1 editor components: | Component | Purpose | | ----------------------- | ----------------------- | @@ -29,7 +29,7 @@ The SDK provides four V1 authoring components: ## Presign Tokens -All authoring components require a **presign token** for authentication. See the [Authoring overview](/docs/developers/embedding/authoring) for details on obtaining presign tokens. +All editor components require a **presign token** for authentication. See the [Editor overview](/docs/developers/embedding/editor) for details on obtaining presign tokens. @@ -131,7 +131,7 @@ const TemplateEditor = ({ presignToken, templateId }) => { ## Props -### All Authoring Components +### All Editor Components | Prop | Type | Required | Description | | ------------------ | --------- | -------- | -------------------------------------------------------- | @@ -143,7 +143,7 @@ const TemplateEditor = ({ presignToken, templateId }) => { | `darkModeDisabled` | `boolean` | No | Disable dark mode (Platform Plan) | | `language` | `string` | No | Set the UI language. See [Supported Languages](https://github.com/documenso/documenso/tree/main/packages/lib/constants/locales.ts) | | `className` | `string` | No | CSS class for the iframe | -| `features` | `object` | No | Feature toggles for the authoring experience | +| `features` | `object` | No | Feature toggles for the editor experience | ### Update Components Only @@ -157,7 +157,7 @@ const TemplateEditor = ({ presignToken, templateId }) => { ## Feature Toggles -Customize what options are available in the authoring experience: +Customize what options are available in the editor experience: ```jsx - Embedded authoring is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is + Embedded editor is included with [Enterprise](https://documen.so/enterprise-cta) plans. It is also available as a paid add-on for the [Platform Plan](https://documen.so/platform-cta-pricing). Contact sales for access. ## Components -The SDK provides two V2 authoring components: +The SDK provides two V2 editor components: | Component | Purpose | | ---------------------- | ------------------------ | @@ -26,7 +26,7 @@ The SDK provides two V2 authoring components: ## Presign Tokens -All authoring components require a **presign token** for authentication. See the [Authoring overview](/docs/developers/embedding/authoring) for details on obtaining presign tokens. +All editor components require a **presign token** for authentication. See the [Editor overview](/docs/developers/embedding/editor) for details on obtaining presign tokens. A presigned token is NOT an API token @@ -100,7 +100,7 @@ const EnvelopeEditor = ({ presignToken, envelopeId }) => { ## Props -### All V2 Authoring Components +### All V2 Editor Components | Prop | Type | Required | Description | | ---------------- | --------- | -------- | -------------------------------------------------------- | @@ -113,7 +113,7 @@ const EnvelopeEditor = ({ presignToken, envelopeId }) => { | `language` | `string` | No | Set the UI language. See [Supported Languages](https://github.com/documenso/documenso/tree/main/packages/lib/constants/locales.ts) | | `className` | `string` | No | CSS class for the iframe | | `user` | `object` | No | Current user info. When provided, enables the "Add Myself" button in the recipients list. Object with optional `email` and `name` fields | -| `features` | `object` | No | Feature toggles for the authoring experience | +| `features` | `object` | No | Feature toggles for the editor experience | ### Create Component Only @@ -132,7 +132,7 @@ const EnvelopeEditor = ({ presignToken, envelopeId }) => { ## Feature Toggles -V2 authoring provides rich, structured feature toggles organized into sections. Pass a partial configuration to customize the authoring experience — any omitted fields will use their defaults. +V2 editor provides rich, structured feature toggles organized into sections. Pass a partial configuration to customize the editor experience — any omitted fields will use their defaults. ```jsx { ## See Also -- [Authoring Overview](/docs/developers/embedding/authoring) - V1 vs V2 comparison and presign tokens -- [V1 Authoring](/docs/developers/embedding/authoring/v1) - V1 document and template authoring +- [Editor Overview](/docs/developers/embedding/editor) - V1 vs V2 comparison and presign tokens +- [V1 Editor](/docs/developers/embedding/editor/v1) - V1 document and template editor - [Embedding Overview](/docs/developers/embedding) - Signing embed concepts and props - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance diff --git a/apps/docs/content/docs/developers/embedding/index.mdx b/apps/docs/content/docs/developers/embedding/index.mdx index 1b96a457d..0cf9a149e 100644 --- a/apps/docs/content/docs/developers/embedding/index.mdx +++ b/apps/docs/content/docs/developers/embedding/index.mdx @@ -6,14 +6,14 @@ description: Embed document signing experiences directly in your application usi import { Callout } from 'fumadocs-ui/components/callout'; import { Step, Steps } from 'fumadocs-ui/components/steps'; -## Embedded Signing vs Embedded Authoring +## Embedded Signing vs Embedded Editor Documenso offers two types of embedding: - **Embedded Signing** lets you embed the signing experience in your application. Your users sign documents without leaving your site. Available on Teams Plan and above. -- **Embedded Authoring** lets you embed document and template _creation and editing_ in your application. This is an [Enterprise](/docs/policies/enterprise-edition) feature (also available as a Platform Plan add-on). See the [Authoring](/docs/developers/embedding/authoring) guide. +- **Embedded Editor** lets you embed document and template _creation and editing_ in your application. This is an [Enterprise](/docs/policies/enterprise-edition) feature (also available as a Platform Plan add-on). See the [Editor](/docs/developers/embedding/editor) guide. -This page covers **embedded signing**. If you need your users to create or edit documents inside your app, see [Authoring](/docs/developers/embedding/authoring). +This page covers **embedded signing**. If you need your users to create or edit documents inside your app, see [Editor](/docs/developers/embedding/editor). --- @@ -229,9 +229,9 @@ Receives an object with: href="/docs/developers/embedding/css-variables" /> diff --git a/apps/docs/content/docs/developers/embedding/meta.json b/apps/docs/content/docs/developers/embedding/meta.json index 293124961..7ccec52c0 100644 --- a/apps/docs/content/docs/developers/embedding/meta.json +++ b/apps/docs/content/docs/developers/embedding/meta.json @@ -1,4 +1,4 @@ { "title": "Embedding", - "pages": ["sdks", "direct-links", "css-variables", "authoring"] + "pages": ["sdks", "direct-links", "css-variables", "editor"] } diff --git a/apps/docs/content/docs/developers/embedding/sdks/angular.mdx b/apps/docs/content/docs/developers/embedding/sdks/angular.mdx index 1ebe6f9e8..e79bd3182 100644 --- a/apps/docs/content/docs/developers/embedding/sdks/angular.mdx +++ b/apps/docs/content/docs/developers/embedding/sdks/angular.mdx @@ -89,4 +89,4 @@ export class SigningComponent { - [Embedding Overview](/docs/developers/embedding) - Props reference and concepts - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance -- [Authoring](/docs/developers/embedding/authoring) - Embed document creation +- [Editor](/docs/developers/embedding/editor) - Embed document creation diff --git a/apps/docs/content/docs/developers/embedding/sdks/preact.mdx b/apps/docs/content/docs/developers/embedding/sdks/preact.mdx index f552e507d..e7c3acefb 100644 --- a/apps/docs/content/docs/developers/embedding/sdks/preact.mdx +++ b/apps/docs/content/docs/developers/embedding/sdks/preact.mdx @@ -93,4 +93,4 @@ See [CSS Variables](/docs/developers/embedding/css-variables) for all available - [Embedding Overview](/docs/developers/embedding) - Props reference and concepts - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance -- [Authoring](/docs/developers/embedding/authoring) - Embed document creation +- [Editor](/docs/developers/embedding/editor) - Embed document creation diff --git a/apps/docs/content/docs/developers/embedding/sdks/react.mdx b/apps/docs/content/docs/developers/embedding/sdks/react.mdx index 65e9058df..58ef0c5d4 100644 --- a/apps/docs/content/docs/developers/embedding/sdks/react.mdx +++ b/apps/docs/content/docs/developers/embedding/sdks/react.mdx @@ -133,4 +133,4 @@ const DocumentSigning = ({ token }: { token: string }) => { - [Embedding Overview](/docs/developers/embedding) - Props reference and concepts - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance -- [Authoring](/docs/developers/embedding/authoring) - Embed document creation +- [Editor](/docs/developers/embedding/editor) - Embed document creation diff --git a/apps/docs/content/docs/developers/embedding/sdks/solid.mdx b/apps/docs/content/docs/developers/embedding/sdks/solid.mdx index a3a5eb972..e81c855ce 100644 --- a/apps/docs/content/docs/developers/embedding/sdks/solid.mdx +++ b/apps/docs/content/docs/developers/embedding/sdks/solid.mdx @@ -93,4 +93,4 @@ See [CSS Variables](/docs/developers/embedding/css-variables) for all available - [Embedding Overview](/docs/developers/embedding) - Props reference and concepts - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance -- [Authoring](/docs/developers/embedding/authoring) - Embed document creation +- [Editor](/docs/developers/embedding/editor) - Embed document creation diff --git a/apps/docs/content/docs/developers/embedding/sdks/svelte.mdx b/apps/docs/content/docs/developers/embedding/sdks/svelte.mdx index 9492bb6eb..edbb41550 100644 --- a/apps/docs/content/docs/developers/embedding/sdks/svelte.mdx +++ b/apps/docs/content/docs/developers/embedding/sdks/svelte.mdx @@ -101,4 +101,4 @@ See [CSS Variables](/docs/developers/embedding/css-variables) for all available - [Embedding Overview](/docs/developers/embedding) - Props reference and concepts - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance -- [Authoring](/docs/developers/embedding/authoring) - Embed document creation +- [Editor](/docs/developers/embedding/editor) - Embed document creation diff --git a/apps/docs/content/docs/developers/embedding/sdks/vue.mdx b/apps/docs/content/docs/developers/embedding/sdks/vue.mdx index fc174eab4..9b309c850 100644 --- a/apps/docs/content/docs/developers/embedding/sdks/vue.mdx +++ b/apps/docs/content/docs/developers/embedding/sdks/vue.mdx @@ -104,4 +104,4 @@ See [CSS Variables](/docs/developers/embedding/css-variables) for all available - [Embedding Overview](/docs/developers/embedding) - Props reference and concepts - [CSS Variables](/docs/developers/embedding/css-variables) - Customize appearance -- [Authoring](/docs/developers/embedding/authoring) - Embed document creation +- [Editor](/docs/developers/embedding/editor) - Embed document creation diff --git a/apps/docs/content/docs/developers/local-development/manual.mdx b/apps/docs/content/docs/developers/local-development/manual.mdx index 86ec490af..f87bc99a1 100644 --- a/apps/docs/content/docs/developers/local-development/manual.mdx +++ b/apps/docs/content/docs/developers/local-development/manual.mdx @@ -83,6 +83,15 @@ npm run prisma:seed -w @documenso/prisma + +### Optional: configure job provider + +The default local job provider does not support scheduled jobs required for document reminders. + +See the [Background Jobs](/docs/self-hosting/configuration/background-jobs) page for more information. + + + ### Start the application @@ -105,6 +114,20 @@ Access the Documenso application by visiting `http://localhost:3000` in your web certificate](/docs/developers/local-development/signing-certificate)**. +## Running Scripts with Environment Variables + +If a package script does not automatically load your `.env` and `.env.local` files, wrap it with the `with:env` script: + +```bash +npm run with:env -- npm run myscript +``` + +The same works for `npx` when running bin scripts: + +```bash +npm run with:env -- npx myscript +``` + ## See Also - [Developer Quickstart](/docs/developers/local-development/quickstart) - Quick Docker-based setup diff --git a/apps/docs/content/docs/policies/enterprise-edition.mdx b/apps/docs/content/docs/policies/enterprise-edition.mdx index c2a551b50..a611db5cd 100644 --- a/apps/docs/content/docs/policies/enterprise-edition.mdx +++ b/apps/docs/content/docs/policies/enterprise-edition.mdx @@ -53,8 +53,8 @@ The Enterprise Edition is required when you: - Document Action Reauthentication (Passkeys and 2FA) - 21 CFR Part 11 Compliance - Email Domains (custom sender addresses) - - Embed Authoring - - Embed Authoring White Label + - Embed Editor + - Embed Editor White Label - Custom signing certificates - Priority feature requests diff --git a/apps/docs/content/docs/policies/fair-use.mdx b/apps/docs/content/docs/policies/fair-use.mdx index 416597fcd..0c4de348d 100644 --- a/apps/docs/content/docs/policies/fair-use.mdx +++ b/apps/docs/content/docs/policies/fair-use.mdx @@ -19,16 +19,19 @@ Use the limitless plans as much as you like. They are meant to offer a lot. Plea ### Do -- Sign as many documents as you need with the individual plan for your single business or organisation -- Use the API and automation tools to automate your signing workflows -- Experiment with plans and integrations while testing what you want to build +- Use team or platform plans to run your workflows, even with significant volume, as long as it aligns with the plan’s intended purpose. +- Experiment and automate freely within the plan features. +- If volume grows beyond what’s sustainable on your plan, we’ll reach out to discuss an upgrade. +- Assume that extreme usage will lead to us contacting you. You can scale up—or scale back. It’s about finding the right fit. ### Don't - -- Use an individual account API to power a platform or product -- Run a large company signing thousands of documents per day on a small team plan -- Expect enterprise-level support on a fair support plan -- Overthink this policy — if you are a paying customer, we want you to win +- Use an individual account's API to power a platform or product. +- Run a large company signing thousands of documents per day on a small team plan. +- Expect enterprise-level support on a fair support plan (i.e. business edition). +- Use a team plan to power an external platform or commercial product or platform beyond moderate testing. +- Expect a platform plan to support enterprise-level volumes indefinitely without a conversation. +- Don’t expect the platform plan to cover enterprise-scale volume or support. If you reach that point, we’ll reach out to guide you to the right fit. +- Don’t overthink this – if you’re building something valuable, we want to see you succeed. If we need to talk, we will. ## Rate Limits diff --git a/apps/docs/content/docs/self-hosting/configuration/advanced/ai-features.mdx b/apps/docs/content/docs/self-hosting/configuration/advanced/ai-features.mdx index d2979910b..b1dfa709e 100644 --- a/apps/docs/content/docs/self-hosting/configuration/advanced/ai-features.mdx +++ b/apps/docs/content/docs/self-hosting/configuration/advanced/ai-features.mdx @@ -1,5 +1,5 @@ --- -title: AI Recipient & Field Detection (Self-hosting) +title: AI Recipient & Field Detection description: Configure Google Vertex AI so Documenso can detect recipients and fields automatically. --- diff --git a/apps/docs/content/docs/self-hosting/configuration/advanced/document-conversion.mdx b/apps/docs/content/docs/self-hosting/configuration/advanced/document-conversion.mdx new file mode 100644 index 000000000..004025af0 --- /dev/null +++ b/apps/docs/content/docs/self-hosting/configuration/advanced/document-conversion.mdx @@ -0,0 +1,408 @@ +--- +title: Document Conversion +description: Enable DOCX uploads on a self-hosted Documenso instance by running a Gotenberg sidecar that converts Word documents to PDF. +--- + +import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'; +import { Callout } from 'fumadocs-ui/components/callout'; +import { Step, Steps } from 'fumadocs-ui/components/steps'; +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + +## Overview + +Documenso can accept `.docx` uploads in addition to PDFs. When a user uploads a Word document, the Documenso server sends it to a [Gotenberg](https://gotenberg.dev) service which uses LibreOffice to convert it to PDF. The converted PDF is what gets stored, signed, and downloaded. The original DOCX is discarded. + +This feature is **opt-in for self-hosted instances**. When the conversion service is not configured, DOCX uploads are rejected in the UI and only PDFs are accepted. + +| Property | Value | +| ----------------------- | -------------------------------------------------------------------- | +| Conversion engine | [Gotenberg](https://gotenberg.dev) + LibreOffice | +| Input format | `.docx` (Office Open XML Word documents) | +| Output format | PDF | +| Network requirement | Documenso must reach the Gotenberg HTTP API | +| Default request timeout | 30 seconds per file | +| Failure handling | An internal circuit breaker opens for 30 seconds after a failure | + + + Only `.docx` is accepted. Legacy `.doc`, `.odt`, `.rtf`, and other LibreOffice-supported formats + are rejected at the upload step even when Gotenberg is configured. + + +--- + +## Requirements + +- A running Gotenberg 8 instance with the LibreOffice module (`gotenberg/gotenberg:8-libreoffice` or newer). +- Network reachability from the Documenso container to the Gotenberg HTTP API. +- A version of Documenso that includes the document conversion feature. + +## Build the Gotenberg Image + +The upstream `gotenberg/gotenberg:8-libreoffice` image works out of the box, but it ships only **metric-compatible font substitutes** (Carlito for Calibri, Liberation for Arial/Times/Courier). Layout widths are preserved but documents will look noticeably different from Word. + +For better fidelity, especially for non-Latin scripts, build a derived image that adds Microsoft Core Fonts and additional language fonts. The Documenso repository ships a reference Dockerfile at [`docker/development/Dockerfile.gotenberg`](https://github.com/documenso/documenso/blob/main/docker/development/Dockerfile.gotenberg) that you can use as a starting point: + +```dockerfile +FROM gotenberg/gotenberg:8-libreoffice + +USER root + +RUN echo "deb http://deb.debian.org/debian trixie contrib non-free" \ + > /etc/apt/sources.list.d/contrib.list \ + && echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" \ + | debconf-set-selections \ + && apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \ + ca-certificates \ + ttf-mscorefonts-installer \ + fonts-symbola \ + fonts-noto-extra \ + fonts-hosny-amiri \ + fonts-thai-tlwg \ + fonts-sil-padauk \ + fonts-sarai \ + fonts-samyak-taml \ + culmus \ + libfribidi0 \ + libharfbuzz0b \ + && fc-cache -f \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +USER gotenberg +``` + + + `ttf-mscorefonts-installer` accepts the Microsoft Core Fonts EULA on your behalf via debconf. By + installing this image you are agreeing to those licence terms. Review them before publishing the + image. + + +Build and publish the image to a registry you control: + +```bash +docker build -t registry.example.com/documenso/gotenberg:8 \ + -f Dockerfile.gotenberg . +docker push registry.example.com/documenso/gotenberg:8 +``` + +If you do not need extra fonts, skip the build step entirely and reference `gotenberg/gotenberg:8-libreoffice` directly in the next section. + +## Deploy the Service + +The Gotenberg service should run **alongside** your Documenso container, not exposed to the public internet. The conversion service has no built-in authorisation beyond HTTP Basic auth, so it should sit on a private network or behind your existing reverse proxy. + + + + +Add a `gotenberg` service to the `compose.yml` you use for Documenso: + +```yaml +services: + gotenberg: + image: registry.example.com/documenso/gotenberg:8 + # Or use upstream directly: + # image: gotenberg/gotenberg:8-libreoffice + restart: unless-stopped + environment: + GOTENBERG_API_BASIC_AUTH_USERNAME: ${GOTENBERG_USERNAME} + GOTENBERG_API_BASIC_AUTH_PASSWORD: ${GOTENBERG_PASSWORD} + command: + - gotenberg + - --api-enable-basic-auth + - --libreoffice-deny-private-ips + - --api-timeout=500s + - --libreoffice-auto-start + - --libreoffice-start-timeout=300s + - --pdfengines-disable-routes + - --webhook-disable + healthcheck: + test: ['CMD', 'curl', '-fsS', 'http://localhost:3000/health'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + + documenso: + # existing config + environment: + NEXT_PRIVATE_DOCUMENT_CONVERSION_URL: http://gotenberg:3000 + NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME: ${GOTENBERG_USERNAME} + NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD: ${GOTENBERG_PASSWORD} + depends_on: + gotenberg: + condition: service_healthy +``` + +Do **not** publish Gotenberg's port (`3000`) to the host. Documenso reaches it over the internal Docker network using the service name (`http://gotenberg:3000`). + + + + +Create a Deployment, Service, and Secret. Example manifests: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: gotenberg-auth + namespace: documenso +stringData: + username: documenso + password: replace-me-with-a-strong-password +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gotenberg + namespace: documenso +spec: + replicas: 1 + selector: + matchLabels: { app: gotenberg } + template: + metadata: + labels: { app: gotenberg } + spec: + containers: + - name: gotenberg + image: registry.example.com/documenso/gotenberg:8 + args: + - gotenberg + - --api-enable-basic-auth + - --libreoffice-deny-private-ips + - --api-timeout=500s + - --libreoffice-auto-start + - --libreoffice-start-timeout=300s + - --pdfengines-disable-routes + - --webhook-disable + env: + - name: GOTENBERG_API_BASIC_AUTH_USERNAME + valueFrom: { secretKeyRef: { name: gotenberg-auth, key: username } } + - name: GOTENBERG_API_BASIC_AUTH_PASSWORD + valueFrom: { secretKeyRef: { name: gotenberg-auth, key: password } } + ports: + - containerPort: 3000 + readinessProbe: + httpGet: { path: /health, port: 3000 } + livenessProbe: + httpGet: { path: /health, port: 3000 } + initialDelaySeconds: 30 +--- +apiVersion: v1 +kind: Service +metadata: + name: gotenberg + namespace: documenso +spec: + selector: { app: gotenberg } + ports: + - port: 3000 + targetPort: 3000 +``` + +Then reference the in-cluster URL from Documenso's environment: + +``` +NEXT_PRIVATE_DOCUMENT_CONVERSION_URL=http://gotenberg.documenso.svc.cluster.local:3000 +NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME=documenso +NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD=replace-me-with-a-strong-password +``` + + + + +Documenso does not have to colocate with Gotenberg. You can point it at any reachable Gotenberg deployment: a managed instance, a shared internal service, or a Gotenberg-compatible API. + +```bash +NEXT_PRIVATE_DOCUMENT_CONVERSION_URL=https://gotenberg.internal.example.com +NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME=documenso +NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD=replace-me-with-a-strong-password +``` + +The remote instance must: + +- Expose the LibreOffice route `/forms/libreoffice/convert`. +- Be reachable from the Documenso container with low enough latency that the 30 second per-request timeout is comfortable. +- Be on a private network or require authentication. Uploaded documents are sent to it as multipart form data and may contain sensitive content. + + + + +## Recommended Gotenberg Flags + +The flags in the examples above are not arbitrary. Each one matters for a production deployment. + +| Flag | Why it matters | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--api-enable-basic-auth` | Requires HTTP Basic credentials on every API route. Without this, anyone with network access to the container can convert arbitrary documents. | +| `--libreoffice-deny-private-ips` | Rejects any outbound fetch LibreOffice tries to make to private, loopback, link-local, or cloud-metadata addresses while processing a document. Mitigates SSRF via malicious `.docx` files that embed `TargetMode="External"` references. Requires Gotenberg 8.32.0. | +| `--api-timeout=500s` | Server-side request ceiling. Documenso aborts at 30 s by default, so this is a safety net for very large documents. | +| `--libreoffice-auto-start` | Starts LibreOffice at container boot so the first request is not slow. | +| `--libreoffice-start-timeout=300s`| Allows LibreOffice up to 5 minutes to come up under load. | +| `--pdfengines-disable-routes` | Disables the PDF engines routes Documenso does not use. Shrinks the attack surface. | +| `--webhook-disable` | Disables webhook callbacks. Documenso uses synchronous requests only. | + +## Configure Documenso + +Set the following environment variables on the Documenso container and restart it. + +### Required + +| Variable | Description | +| ------------------------------------- | ---------------------------------------------------------------------- | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL`| Base URL of the Gotenberg service (e.g., `http://gotenberg:3000`). Leave unset to disable the feature. | + +### Optional + +| Variable | Default | Description | +| ------------------------------------------- | ------- | -------------------------------------------------------------------------------------------- | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME` | | HTTP Basic auth username. Set when Gotenberg runs with `--api-enable-basic-auth`. | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD` | | HTTP Basic auth password. Set together with the username. | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_TIMEOUT_MS`| `30000` | Per-request timeout in milliseconds. Increase for very large documents. | + + + When `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL` is set, the public flag + `NEXT_PUBLIC_DOCUMENT_CONVERSION_ENABLED` is derived automatically on server start. You do not + need to set it yourself, and setting it manually has no effect. + + +### Example `.env` Snippet + +```bash +# Document conversion (DOCX -> PDF) +NEXT_PRIVATE_DOCUMENT_CONVERSION_URL=http://gotenberg:3000 +NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME=documenso +NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD=replace-me-with-a-strong-password +# NEXT_PRIVATE_DOCUMENT_CONVERSION_TIMEOUT_MS=60000 +``` + +## Verify the Setup + +{/* prettier-ignore */} + + +### Restart the Documenso container + +Restart so the new environment variables are picked up. + + + +### Confirm Gotenberg is healthy + +From a shell inside the Documenso container or another container on the same network: + +```bash +curl -fsS http://gotenberg:3000/health +``` + +The endpoint is exempt from basic auth and should return `200 OK`. + + + +### Upload a test DOCX + +In the Documenso web UI, open **Documents** and try uploading a small `.docx` file. The upload dropzone should accept it, and after a few seconds the editor should open with the converted PDF. + + + +### Check the server logs + +Successful conversions log a `document_conversion_attempt` event with `result: "success"`, the duration, and the file size. Failures log the same event with `result: "error"` and an error code (`CONVERSION_SERVICE_UNAVAILABLE`, `CONVERSION_FAILED`, or `UNSUPPORTED_FILE_TYPE`). + + + + +## Security Considerations + +- **Treat the conversion service as untrusted internal infrastructure.** Documents pass through Gotenberg in plain form. Run it on a private network and require HTTP Basic auth. +- **Run with `--libreoffice-deny-private-ips`.** Without this flag, a malicious `.docx` can trigger LibreOffice to fetch URLs from your internal network (SSRF). +- **Disable unused routes.** `--pdfengines-disable-routes` and `--webhook-disable` reduce attack surface. Documenso only uses the LibreOffice convert route. +- **Do not expose Gotenberg to the public internet.** Even with basic auth, this is a document-processing service with a non-trivial CPU and memory footprint; exposing it invites abuse. +- **Rotate credentials.** Rotating the basic auth secret is a config change in both Gotenberg and Documenso, followed by a restart of each. + +## Resource Sizing + +Conversion is CPU- and memory-bound on LibreOffice. As a starting point: + +| Workload | Suggested resources | +| ----------------------------- | ------------------------------------ | +| Light (a few DOCX per minute) | 1 vCPU, 1 GB RAM | +| Moderate (sustained uploads) | 2 vCPU, 2 GB RAM | +| Heavy / multi-tenant | Horizontally scale Gotenberg replicas behind a load balancer | + +Gotenberg is stateless. Each container handles one or more concurrent requests independently. Scale horizontally rather than vertically once a single replica is saturated. + +## Troubleshooting + + + + The Documenso server does not see `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL`. Check the value is set + on the running container (`docker exec documenso printenv | grep DOCUMENT_CONVERSION`) and + restart after changing it. + + + + Documenso could not reach Gotenberg. Verify: + + - The URL in `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL` is resolvable from the Documenso container + (use the Docker service name or in-cluster DNS, not `localhost`). + - Gotenberg's `/health` endpoint returns `200`. + - Basic auth credentials match between the two services. + + After repeated failures, an internal circuit breaker opens for 30 seconds. Subsequent uploads + will fail fast during that window; this is intentional and self-recovers. + + + + + Gotenberg was reachable but returned a non-2xx response. Check the Gotenberg container logs: + + ```bash + docker compose logs -f gotenberg + ``` + + Common causes: corrupted `.docx` file, exotic embedded objects LibreOffice cannot render, or a + file that genuinely exceeded the conversion timeout. Increase + `NEXT_PRIVATE_DOCUMENT_CONVERSION_TIMEOUT_MS` for very large documents. + + + + + LibreOffice is not byte-identical to Microsoft Word. Layout, font metrics, and complex elements + (Charts, SmartArt, ActiveX controls) may differ. To improve fidelity: + + - Use the custom Dockerfile in this guide to install Microsoft Core Fonts and additional + language fonts. + - Make sure any custom fonts referenced by your documents are installed in the Gotenberg image. + - For pixel-perfect output, ask users to export to PDF from Word before uploading. + + + + + Documenso disables Gotenberg's `exportFormFields` flag during conversion. Word content controls + (``) become static graphics in the output PDF, which prevents Documenso's later + flattening step from making them invisible. This is intentional. Use Documenso fields + (signature, text, date, etc.) for anything that needs to be filled in by signers. + + + + LibreOffice starts lazily by default. Pass `--libreoffice-auto-start` to Gotenberg so it warms + up at container boot. Allow up to a minute on first start before considering the service + unhealthy. + + + + Repeated failures open an in-process circuit breaker for 30 seconds. If you see this in + production, the underlying problem is the Gotenberg service. Check its logs, resource usage, + and connectivity. The breaker is per-process and resets on restart. + + + +--- + +## See Also + +- [Upload Documents (User Guide)](/docs/users/documents/upload) - End-user view of DOCX uploads +- [Environment Variables](/docs/self-hosting/configuration/environment) - Full configuration reference +- [Docker Compose Deployment](/docs/self-hosting/deployment/docker-compose) - Compose-based deployment patterns +- [Gotenberg Documentation](https://gotenberg.dev/docs/getting-started/introduction) - Upstream Gotenberg docs diff --git a/apps/docs/content/docs/self-hosting/configuration/advanced/index.mdx b/apps/docs/content/docs/self-hosting/configuration/advanced/index.mdx index e00698d9d..0abf9d293 100644 --- a/apps/docs/content/docs/self-hosting/configuration/advanced/index.mdx +++ b/apps/docs/content/docs/self-hosting/configuration/advanced/index.mdx @@ -1,6 +1,6 @@ --- title: Advanced -description: Optional configuration for OAuth providers, AI features, and other advanced settings. +description: Optional configuration for OAuth providers, AI features, document conversion, and other advanced settings. --- @@ -14,4 +14,9 @@ description: Optional configuration for OAuth providers, AI features, and other description="Enable AI-powered recipient and field detection." href="/docs/self-hosting/configuration/advanced/ai-features" /> + diff --git a/apps/docs/content/docs/self-hosting/configuration/advanced/meta.json b/apps/docs/content/docs/self-hosting/configuration/advanced/meta.json index 5278c14a3..8b61e1758 100644 --- a/apps/docs/content/docs/self-hosting/configuration/advanced/meta.json +++ b/apps/docs/content/docs/self-hosting/configuration/advanced/meta.json @@ -1,4 +1,4 @@ { "title": "Advanced", - "pages": ["oauth-providers", "ai-features"] + "pages": ["oauth-providers", "document-conversion", "ai-features"] } diff --git a/apps/docs/content/docs/self-hosting/configuration/environment.mdx b/apps/docs/content/docs/self-hosting/configuration/environment.mdx index 699e54ede..1d57b5062 100644 --- a/apps/docs/content/docs/self-hosting/configuration/environment.mdx +++ b/apps/docs/content/docs/self-hosting/configuration/environment.mdx @@ -244,7 +244,7 @@ You can control who is allowed to create accounts on your instance with the foll - **`NEXT_PUBLIC_DISABLE_GOOGLE_SIGNUP`**, **`NEXT_PUBLIC_DISABLE_MICROSOFT_SIGNUP`**, **`NEXT_PUBLIC_DISABLE_OIDC_SIGNUP`**: Set to `true` to block brand-new account creation through the matching SSO provider. Existing users with the provider already linked can still sign in, and existing users can still link the provider to their account. `NEXT_PUBLIC_DISABLE_OIDC_SIGNUP` also blocks new-account creation through the organisation authentication portal. - **`NEXT_PRIVATE_ALLOWED_SIGNUP_DOMAINS`**: Restrict signups to specific email domains. When set, only users whose email address matches one of the listed domains can create an account. Leave empty to allow all domains. -Sign-in for existing users is never affected — only the creation of brand-new accounts. +Sign-in for existing users is never affected, only the creation of brand-new accounts. Both the master switch and the domain allowlist apply to email/password registration and OAuth (Google, Microsoft, OIDC). If a user attempts to sign up via OAuth with a disallowed domain, they are redirected to the sign-in page with an error. @@ -279,6 +279,23 @@ AI features must also be enabled in organisation/team settings after configurati --- +## Document Conversion + +Documenso can accept `.docx` uploads by sending them to a [Gotenberg](https://gotenberg.dev) service that converts them to PDF. When `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL` is unset, DOCX uploads are rejected and only PDFs are accepted. + +| Variable | Description | Default | +| --------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------- | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL` | Base URL of the Gotenberg service (e.g., `http://gotenberg:3000`). Unset disables the feature. | | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_USERNAME` | HTTP Basic auth username. Required when Gotenberg runs with `--api-enable-basic-auth`. | | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_PASSWORD` | HTTP Basic auth password. Set together with the username. | | +| `NEXT_PRIVATE_DOCUMENT_CONVERSION_TIMEOUT_MS` | Per-request timeout in milliseconds. Increase for very large documents. | `30000` | + +The public flag `NEXT_PUBLIC_DOCUMENT_CONVERSION_ENABLED` is derived automatically from `NEXT_PRIVATE_DOCUMENT_CONVERSION_URL` on server start. Do not set it manually. + +For setup, image-build instructions, and security recommendations, see [Document Conversion](/docs/self-hosting/configuration/advanced/document-conversion). + +--- + ## Background Jobs Documenso supports multiple background job providers for processing emails, documents, webhooks, and scheduled tasks. @@ -342,7 +359,7 @@ Telemetry collects only: app version, installation ID, and node ID. No personal ## Enterprise Features -These variables require an active [Enterprise Edition](/docs/policies/enterprise-edition) license. Obtain a license key from [license.documenso.com](https://license.documenso.com) and set it below to unlock enterprise features such as SSO, embed authoring, and 21 CFR Part 11 compliance. +These variables require an active [Enterprise Edition](/docs/policies/enterprise-edition) license. Obtain a license key from [license.documenso.com](https://license.documenso.com) and set it below to unlock enterprise features such as SSO, embed editor, and 21 CFR Part 11 compliance. | Variable | Description | | ------------------------------------ | ------------------------------------------------ | diff --git a/apps/docs/content/docs/self-hosting/deployment/docker.mdx b/apps/docs/content/docs/self-hosting/deployment/docker.mdx index 90159be0f..7d6d4b9cd 100644 --- a/apps/docs/content/docs/self-hosting/deployment/docker.mdx +++ b/apps/docs/content/docs/self-hosting/deployment/docker.mdx @@ -26,8 +26,14 @@ docker --version ## Pulling the Docker Image +The Documenso image is available on both DockerHub and GitHub Container Registry: + ```bash +# DockerHub docker pull documenso/documenso:latest + +# GitHub Container Registry +docker pull ghcr.io/documenso/documenso:latest ``` ### Available Tags @@ -196,6 +202,14 @@ Documenso provides health check endpoints for monitoring: | `/api/health` | Checks database connectivity and certificate status | | `/api/certificate-status` | Returns whether a signing certificate is configured and usable | +Both endpoints return a JSON response with a `status` field: + +| Status | Meaning | +| ----------- | -------------------------------------------------------------------- | +| `"ok"` | Everything is working properly | +| `"warning"` | Application is running but there are certificate issues | +| `"error"` | Critical issues (database unreachable, missing configuration, etc.) | + ### Docker Health Check Add a health check to your container: diff --git a/apps/docs/content/docs/self-hosting/getting-started/index.mdx b/apps/docs/content/docs/self-hosting/getting-started/index.mdx index edd614ff8..33fd7a321 100644 --- a/apps/docs/content/docs/self-hosting/getting-started/index.mdx +++ b/apps/docs/content/docs/self-hosting/getting-started/index.mdx @@ -3,6 +3,8 @@ title: Getting Started description: Requirements and quick start guide for self-hosting Documenso. --- +import { Callout } from 'fumadocs-ui/components/callout'; + + + + **You must generate a signing certificate.** Documenso does not ship with one. Without a + certificate, the application starts normally but document signing will fail on completion with + errors. + + Please see all the [requirements](/docs/self-hosting/getting-started/requirements) before proceeding. + diff --git a/apps/docs/content/docs/self-hosting/getting-started/requirements.mdx b/apps/docs/content/docs/self-hosting/getting-started/requirements.mdx index e4ca2a71f..13b2b75ab 100644 --- a/apps/docs/content/docs/self-hosting/getting-started/requirements.mdx +++ b/apps/docs/content/docs/self-hosting/getting-started/requirements.mdx @@ -7,14 +7,29 @@ import { Callout } from 'fumadocs-ui/components/callout'; ## What You Need -Documenso requires the following external services: +Documenso requires the following items and external services: | Service | Purpose | Minimum Version | | ------------- | ---------------------------- | --------------- | +| Signing certificate | Digital signature for documents | N/A | | PostgreSQL | Primary database | 14+ | | SMTP server | Sending emails to recipients | Any | | Reverse proxy | SSL termination, routing | Any | + +### Signing Certificate + + + Documenso does not ship with a signing certificate. Without one, the application starts normally + but all document signing will fail. You must generate or provide a `.p12` certificate before going + to production. + + +Every completed document is digitally signed using an X.509 certificate. You can generate a self-signed certificate for free or use one from a Certificate Authority (CA). + +- [Generate a local certificate](/docs/self-hosting/configuration/signing-certificate/local) — step-by-step instructions to create a `.p12` certificate +- [All certificate options](/docs/self-hosting/configuration/signing-certificate) — self-signed, CA-issued, and Google Cloud HSM + ### PostgreSQL Database Documenso uses PostgreSQL for all data storage including documents, users, and audit logs. You cannot use MySQL, SQLite, or other databases. diff --git a/apps/docs/content/docs/self-hosting/getting-started/tips.mdx b/apps/docs/content/docs/self-hosting/getting-started/tips.mdx index 4673ae6aa..7fe640ab3 100644 --- a/apps/docs/content/docs/self-hosting/getting-started/tips.mdx +++ b/apps/docs/content/docs/self-hosting/getting-started/tips.mdx @@ -154,6 +154,34 @@ See [Background Jobs Configuration](/docs/self-hosting/configuration/background- --- +## IPv6-Only Deployments + +If you are deploying to an environment that uses only IPv6, set the `HOST` environment variable to `::` so the application binds to all IPv6 addresses: + +**Docker:** + +```bash +docker run -it -e HOST=:: documenso/documenso:latest npm run start +``` + +**Kubernetes or Docker Compose:** + +```yaml +containers: + - name: documenso + image: documenso/documenso:latest + command: + - npm + args: + - run + - start + env: + - name: HOST + value: '::' +``` + +--- + ## Docker File Permissions The Documenso container runs as a non-root user (UID 1001). If you mount files into the container (certificates, configuration), ensure they're readable: diff --git a/apps/docs/content/docs/self-hosting/index.mdx b/apps/docs/content/docs/self-hosting/index.mdx index 9876c800d..4263d30d4 100644 --- a/apps/docs/content/docs/self-hosting/index.mdx +++ b/apps/docs/content/docs/self-hosting/index.mdx @@ -3,6 +3,8 @@ title: Self-Hosting description: Deploy and manage your own Documenso instance for complete control over your data, compliance, and customization. --- +import { Callout } from 'fumadocs-ui/components/callout'; + ## Getting Started @@ -18,6 +20,13 @@ description: Deploy and manage your own Documenso instance for complete control /> + + **You must generate a signing certificate.** Documenso does not ship with one. Without a + certificate, the application starts normally but document signing will fail. + + Please see all the [requirements](/docs/self-hosting/getting-started/requirements) before proceeding. + + --- ## Deployment Options @@ -122,7 +131,7 @@ See the [Quick Start guide](/docs/self-hosting/getting-started/quick-start) for ## Enterprise Edition -Self-hosted Documenso includes full core functionality under the AGPL-3.0 license. If you need enterprise features such as SSO, embed authoring white label, or 21 CFR Part 11 compliance, you can activate them with a license key. +Self-hosted Documenso includes full core functionality under the AGPL-3.0 license. If you need enterprise features such as SSO, embed editor white label, or 21 CFR Part 11 compliance, you can activate them with a license key. See [Enterprise Edition](/docs/policies/enterprise-edition) for details and [Licenses](/docs/policies/licenses) for a comparison. diff --git a/apps/docs/content/docs/users/documents/upload.mdx b/apps/docs/content/docs/users/documents/upload.mdx index bf5f863dd..cd79b7904 100644 --- a/apps/docs/content/docs/users/documents/upload.mdx +++ b/apps/docs/content/docs/users/documents/upload.mdx @@ -11,16 +11,41 @@ import { Step, Steps } from 'fumadocs-ui/components/steps'; | Limitation | Value | | ----------------------- | ----------------------------------- | -| Supported format | PDF only | +| Supported formats | PDF, DOCX | | Maximum file size | 50MB (configurable for self-hosted) | | Encrypted PDFs | Not supported | | Password-protected PDFs | Not supported | +| Legacy `.doc` files | Not supported (convert to DOCX) | Documenso does not support password-protected or encrypted PDF files. Remove encryption before uploading. +## Supported Formats + +Documenso accepts two file formats: + +- **PDF** (`.pdf`): used as-is. **Recommended.** +- **Word** (`.docx`): converted to PDF on the server during upload. The converted PDF is what recipients sign. + +Other formats (`.doc`, `.odt`, `.rtf`, images) are not supported. Convert them to PDF or DOCX before uploading. + + + **Upload a PDF whenever you can.** DOCX files are converted to PDF using LibreOffice, which is not + byte-identical to Microsoft Word. Spacing, line breaks, fonts, and complex elements (tables, + charts, headers, footers) can shift in the converted PDF. For the final document to look exactly + the way you designed it, export to PDF from Word, Google Docs, or Pages and upload the PDF + directly. + + + + DOCX support requires the document conversion service. It is enabled on + [documenso.com](https://app.documenso.com). Self-hosted instances must + [configure it](/docs/self-hosting/configuration/advanced/document-conversion) before DOCX uploads + are accepted. + + ## Upload Methods ![Documents dashboard](/document-signing/documenso-documents-dashboard.webp) @@ -38,15 +63,15 @@ You can upload documents in two ways: - ### Drag and drop your PDF + ### Drag and drop your file - Drag a PDF file from your computer and drop it anywhere on the page. + Drag a PDF or DOCX file from your computer and drop it anywhere on the page. ### Wait for the upload to complete - The document will process and the editor will open when ready. + The document will process and the editor will open when ready. DOCX files take a few extra seconds while they are converted to PDF. @@ -70,7 +95,7 @@ You can upload documents in two ways: ### Select your file - Choose a PDF file from your computer. + Choose a PDF or DOCX file from your computer. @@ -81,16 +106,32 @@ You can upload documents in two ways: +## DOCX Conversion + +We always recommend uploading a PDF rather than a DOCX. If you have the original document open in Word, Google Docs, or Pages, export to PDF from there and upload the PDF. The result is guaranteed to match what you see on screen. + +If you do upload a `.docx` file, Documenso converts it to PDF before adding it to the envelope. The original `.docx` is discarded. Only the converted PDF is stored, signed, and downloaded. + +Things to keep in mind when uploading DOCX: + +- **The converted PDF will not be pixel-identical to your Word document.** Conversion uses LibreOffice, which renders most documents faithfully but differs from Microsoft Word in subtle ways. Spacing, font metrics, line breaks, and complex layout features can shift. +- **Always review the converted PDF before adding fields or sending.** Open the document in the editor and scroll through every page to confirm it looks the way you expect. +- **Form controls are flattened.** Word content controls (drop-downs, date pickers, checkboxes) become static text or graphics. Use Documenso fields for anything that needs to be filled in. +- **Fonts not installed on the server fall back to substitutes.** On documenso.com, common fonts (Calibri, Arial, Times New Roman, etc.) are installed. On self-hosted instances, font fidelity depends on the operator's setup. +- **Tracked changes and comments are preserved as they appear in Word.** Accept or reject changes and remove comments before uploading if you do not want them in the final document. + +If the converted PDF does not match what you expect, export the document to PDF from Word, Google Docs, or another tool and upload the PDF directly. + ## Uploading Multiple Documents -You can upload multiple PDF files at once to create a single envelope containing multiple documents. The number of files you can upload per envelope depends on your plan. +You can upload multiple files at once to create a single envelope containing multiple documents. The number of files you can upload per envelope depends on your plan. To upload multiple files: -- Select multiple PDF files when using the file picker, or -- Drag and drop multiple PDF files at once +- Select multiple PDF or DOCX files when using the file picker, or +- Drag and drop multiple files at once -All files in the same upload become part of the same envelope and share the same recipients and signing workflow. +You can mix PDF and DOCX files in the same upload. All files become part of the same envelope and share the same recipients and signing workflow. If you need separate signing workflows for each document, upload them individually. @@ -114,15 +155,37 @@ The document remains in `Draft` status until you send it. You can close the edit Reduce the file size before uploading: - - Compress images within the PDF + - Compress images within the document - Remove unnecessary pages - - Use a PDF compression tool + - Use a PDF compression tool (for PDFs) or save with images downsampled (for DOCX) - - Convert your document to PDF before uploading. Most applications (Word, Google Docs, etc.) can - export to PDF format. + + Documenso accepts PDF and DOCX. For other formats (`.doc`, `.odt`, `.rtf`, etc.), export to PDF + from your editor (Word, Google Docs, Pages) and upload the PDF. + +If you are self-hosted and DOCX is rejected, the [document conversion +service](/docs/self-hosting/configuration/advanced/document-conversion) is not configured on your +instance. + + + + + The document conversion service was reachable but could not convert the file. Common causes: + +- The `.docx` file is corrupted. Open it in Word, save a new copy, and try again. +- The file uses very unusual fonts or embedded objects that LibreOffice cannot render. +- The file is unusually large or complex and exceeded the conversion timeout. + +If the problem persists, export the document to PDF from Word and upload the PDF directly. + + + + + The document conversion service is down or temporarily unreachable. Try again in a minute. If you + self-host, check the [document conversion + service](/docs/self-hosting/configuration/advanced/document-conversion) logs. diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index 73242d16c..2eb734c8d 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -296,12 +296,27 @@ const config = { }, { source: '/developers/embedding/authoring', - destination: '/docs/developers/embedding/authoring', + destination: '/docs/developers/embedding/editor', + permanent: true, + }, + { + source: '/developers/embedding/authoring/:path*', + destination: '/docs/developers/embedding/editor/:path*', permanent: true, }, { source: '/developers/embedded-authoring', - destination: '/docs/developers/embedding/authoring', + destination: '/docs/developers/embedding/editor', + permanent: true, + }, + { + source: '/docs/developers/embedding/authoring', + destination: '/docs/developers/embedding/editor', + permanent: true, + }, + { + source: '/docs/developers/embedding/authoring/:path*', + destination: '/docs/developers/embedding/editor/:path*', permanent: true, }, diff --git a/apps/remix/app/components/dialogs/admin-organisation-delete-dialog.tsx b/apps/remix/app/components/dialogs/admin-organisation-delete-dialog.tsx new file mode 100644 index 000000000..5ae6f1c0a --- /dev/null +++ b/apps/remix/app/components/dialogs/admin-organisation-delete-dialog.tsx @@ -0,0 +1,188 @@ +import { AppError } from '@documenso/lib/errors/app-error'; +import { trpc } from '@documenso/trpc/react'; +import { Alert, AlertDescription } from '@documenso/ui/primitives/alert'; +import { Button } from '@documenso/ui/primitives/button'; +import { Checkbox } from '@documenso/ui/primitives/checkbox'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@documenso/ui/primitives/dialog'; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form'; +import { Input } from '@documenso/ui/primitives/input'; +import { useToast } from '@documenso/ui/primitives/use-toast'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Trans, useLingui } from '@lingui/react/macro'; +import { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; + +export type AdminOrganisationDeleteDialogProps = { + organisationId: string; + organisationName: string; + trigger?: React.ReactNode; +}; + +export const AdminOrganisationDeleteDialog = ({ + organisationId, + organisationName, + trigger, +}: AdminOrganisationDeleteDialogProps) => { + const [open, setOpen] = useState(false); + + const { t } = useLingui(); + const { toast } = useToast(); + + const deleteMessage = t`delete ${organisationName}`; + + const ZAdminDeleteOrganisationFormSchema = z.object({ + organisationName: z.literal(deleteMessage, { + errorMap: () => ({ message: t`You must enter '${deleteMessage}' to proceed` }), + }), + sendEmailToOwner: z.boolean(), + }); + + type TAdminDeleteOrganisationFormSchema = z.infer; + + const form = useForm({ + resolver: zodResolver(ZAdminDeleteOrganisationFormSchema), + defaultValues: { + organisationName: '', + sendEmailToOwner: true, + }, + }); + + const { mutateAsync: deleteOrganisation } = trpc.admin.organisation.delete.useMutation(); + + const onFormSubmit = async (values: TAdminDeleteOrganisationFormSchema) => { + try { + await deleteOrganisation({ + organisationId, + organisationName, + sendEmailToOwner: values.sendEmailToOwner, + }); + + toast({ + title: t`Deletion scheduled`, + description: t`The organisation will be deleted in the background. Documents will be orphaned, not deleted.`, + duration: 7500, + }); + + setOpen(false); + } catch (err) { + const error = AppError.parseError(err); + console.error(error); + + toast({ + title: t`An error occurred`, + description: t`We encountered an error while attempting to delete this organisation. Please try again later.`, + variant: 'destructive', + duration: 10000, + }); + } + }; + + useEffect(() => { + if (!open) { + form.reset(); + } + }, [open, form]); + + return ( + !form.formState.isSubmitting && setOpen(value)}> + + {trigger ?? ( + + )} + + + + + + Delete organisation + + + + + You are about to delete {organisationName}. This action is not + reversible. All teams will be removed and all documents will be orphaned to the deleted-account service + account. + + + + + + + + The deletion will run in the background, and can take up to a few minutes to complete. Do not re-run this + deletion. + + + + +
+ +
+ ( + + + + Confirm by typing {deleteMessage} + + + + + + + + )} + /> + + ( + + + + + + + + )} + /> + + + + + + +
+
+ +
+
+ ); +}; diff --git a/apps/remix/app/components/filters/date-range-filter.tsx b/apps/remix/app/components/filters/date-range-filter.tsx index a4ec394c5..03eb2fccf 100644 --- a/apps/remix/app/components/filters/date-range-filter.tsx +++ b/apps/remix/app/components/filters/date-range-filter.tsx @@ -14,10 +14,10 @@ export const DateRangeFilter = ({ currentRange }: DateRangeFilterProps) => { const [isPending, startTransition] = useTransition(); const updateSearchParams = useUpdateSearchParams(); - const handleRangeChange = (value: string) => { + const handleRangeChange = (value: DateRange) => { startTransition(() => { updateSearchParams({ - dateRange: value as DateRange, + dateRange: value, page: 1, }); }); diff --git a/apps/remix/app/components/forms/branding-preferences-form.tsx b/apps/remix/app/components/forms/branding-preferences-form.tsx index fa83eb20c..e420fdae7 100644 --- a/apps/remix/app/components/forms/branding-preferences-form.tsx +++ b/apps/remix/app/components/forms/branding-preferences-form.tsx @@ -512,7 +512,7 @@ export function BrandingPreferencesForm({