Skip to content

eli7pm/document-engine-nestjs

Repository files navigation

Document Engine with NestJS and React

This project demonstrates how to integrate Document Engine with a NestJS backend and React frontend.

Setup Prerequisites

Document Engine Setup

For setting up Document Engine's Docker container and initial configuration, please follow steps 1-4 in the Document Engine Getting Started Guide.

Prerequisites

  • Node.js (v14 or later)
  • Docker and Docker Compose
  • A Document Engine license (for production use)
  • OpenSSL for generating key pairs

Generating JWT Key Pair

Before setting up the application, you need to generate your own RSA key pair for JWT authentication:

  1. Generate the private key:
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
  1. Extract the public key from the private key:
openssl rsa -pubout -in private.pem -out public.pem

Project Structure

document-engine/
├── document-engine-backend/     # NestJS backend
│   ├── src/
│   │   ├── app.controller.ts
│   │   ├── app.module.ts
│   │   ├── documents.controller.ts
│   │   ├── documents.service.ts
│   │   └── main.ts
│   └── package.json
│
├── document-engine-frontend/    # React frontend
│   ├── src/
│   │   ├── App.tsx
│   │   └── DocumentViewer.tsx
│   ├── index.html
│   └── package.json
│
└── docker-compose.yml          # Document Engine setup

Installation

  1. Clone the repository:
git clone https://github.com/eli7pm/document-engine-nestjs.git
cd document-engine-nestjs
  1. Create docker-compose.yml:
version: "3.8"

services:
  document_engine:
    image: pspdfkit/document-engine:1.5.5
    environment:
      PGUSER: de-user
      PGPASSWORD: password
      PGDATABASE: document-engine
      PGHOST: db
      PGPORT: 5432
      API_AUTH_TOKEN: secret
      SECRET_KEY_BASE: secret-key-base
      JWT_PUBLIC_KEY: |
        -----BEGIN PUBLIC KEY-----
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gzhmJ9TDanEzWdP1WG+
        0Ecwbe7f3bv6e5UUpvcT5q68IQJKP47AQdBAnSlFVi4X9SaurbWoXdS6jpmPpk24
        QvitzLNFphHdwjFBelTAOa6taZrSusoFvrtK9x5xsW4zzt/bkpUraNx82Z8MwLwr
        t6HlY7dgO9+xBAabj4t1d2t+0HS8O/ed3CB6T2lj6S8AbLDSEFc9ScO6Uc1XJlSo
        rgyJJSPCpNhSq3AubEZ1wMS1iEtgAzTPRDsQv50qWIbn634HLWxTP/UH6YNJBwzt
        3O6q29kTtjXlMGXCvin37PyX4Jy1IiPFwJm45aWJGKSfVGMDojTJbuUtM+8P9Rrn
        AwIDAQAB
        -----END PUBLIC KEY-----
      JWT_ALGORITHM: RS256
      DASHBOARD_USERNAME: dashboard
      DASHBOARD_PASSWORD: secret
    ports:
      - 5000:5000
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: de-user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: document-engine
      POSTGRES_INITDB_ARGS: --data-checksums
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

⚠️ Note: The JWT keys shown above are Nutrient's example keys. Generate and use your own keys for production use.

  1. Set up the backend:
cd document-engine-backend
npm install
  1. Set up the frontend:
cd ../document-engine-frontend
npm install

Configuration

Backend Configuration

Create a .env file in the backend directory with your private key (example shown, replace with your own):

JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA2gzhmJ9TDanEzWdP1WG+0Ecwbe7f3bv6e5UUpvcT5q68IQJK
P47AQdBAnSlFVi4X9SaurbWoXdS6jpmPpk24QvitzLNFphHdwjFBelTAOa6taZrS
usoFvrtK9x5xsW4zzt/bkpUraNx82Z8MwLwrt6HlY7dgO9+xBAabj4t1d2t+0HS8
O/ed3CB6T2lj6S8AbLDSEFc9ScO6Uc1XJlSorgyJJSPCpNhSq3AubEZ1wMS1iEtg
AzTPRDsQv50qWIbn634HLWxTP/UH6YNJBwzt3O6q29kTtjXlMGXCvin37PyX4Jy1
IiPFwJm45aWJGKSfVGMDojTJbuUtM+8P9RrnAwIDAQABAoIBAQDSKxhGw0qKINhQ
IwQP5+bDWdqUG2orjsQf2dHOHNhRwJoUNuDZ4f3tcYzV7rGmH0d4Q5CaXj2qMyCd
0eVjpgW0h3z9kM3RA+d7BX7XKlkdQABliZUT9SUUcfIPvohXPKEzBRHed2kf6WVt
XKAuJTD+Dk3LjzRygWldOAE4mnLeZjU61kxPYriynyre+44Gpsgy37Tj25MAmVCY
Flotr/1WZx6bg3HIyFRGxnoJ1zU1MkGxwS4IsrQwOpWEHBiD5nvo54hF5I00NHj/
ccz+MwpgGdjyl02IGCy1fF+Q5SYyH86DG52Mgn8VI9dseGmanLGcgNvrdJFILoJR
SZW7gQoBAoGBAP+D6ZmRF7EqPNMypEHQ5qHHDMvil3mhNQJyIC5rhhl/nn063wnm
zhg96109hVh4zUAj3Rmjb9WqPiW7KBMJJdnEPjmZ/NOXKmgjs2BF+c8oiLQyTQml
xB7LnptvBDi8MnEd3uemfxNuZc+2siuSzgditshNru8xPG2Sn99JC271AoGBANp2
xj5EfdlqNLd11paLOtJ7dfREgc+8FxQCiKSxbaOlVXNk0DW1w4+zLnFohj2m/wRr
bBIzSL+eufoQ9y4BT/AA+ln4qxOpC0isOGK5SxwIjB6OHhCuP8L3anj1IFYM+NX0
Xr1/qdZHKulgbS49cq+TDpB74WyKLLnsvQFyINMXAoGABR5+cp4ujFUdTNnp4out
4zXasscCY+Rv7HGe5W8wC5i78yRXzZn7LQ8ohQCziDc7XXqadmYI2o4DmrvqLJ91
S6yb1omYQCD6L4XvlREx1Q2p13pegr/4cul/bvvFaOGUXSHNEnUKfLgsgAHYBfl1
+T3oDZFI3O/ulv9mBpIvEXUCgYEApeRnqcUM49o4ac/7wZm8czT5XyHeiUbFJ5a8
+IMbRJc6CkRVr1N1S1u/OrMqrQpwwIRqLm/vIEOB6hiT+sVYVGIJueSQ1H8baHYO
4zjdhk4fSNyWjAgltwF2Qp+xjGaRVrcYckHNUD/+n/VvMxvKSPUcrC7GAUvzpsPU
ypJFxsUCgYEA6GuP6M2zIhCYYeB2iLRD4ZHw92RfjikaYmB0++T0y2TVrStlzXHl
c8H6tJWNchtHH30nfLCj9WIMb/cODpm/DrzlSigHffo3+5XUpD/2nSrcFKESw4Xs
a4GXoAxqU44w4Mckg2E19b2MrcNkV9eWAyTACbEO4oFcZcSZOCKj8Fw=
-----END RSA PRIVATE KEY-----"

Frontend Files

  1. App.tsx:
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { DocumentViewer } from './DocumentViewer';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/documents/:documentId" element={<DocumentViewer />} />
      </Routes>
    </Router>
  );
}

export default App;
  1. DocumentViewer.tsx:
import { useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';

type DocumentView = {
  documentId: string;
  jwt: string;
};

export function DocumentViewer() {
  const { documentId } = useParams<{ documentId: string }>();
  const [viewerConfig, setViewerConfig] = useState<DocumentView | null>(null);
  const [error, setError] = useState<string | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const loadDocument = async () => {
      try {
        console.log('Fetching document:', documentId);
        const response = await fetch(`http://localhost:3000/api/documents/${documentId}/view`);
        
        if (!response.ok) {
          throw new Error('Failed to load document configuration');
        }
        
        const data = await response.json();
        console.log('Received config:', data);
        setViewerConfig(data);
      } catch (err) {
        console.error('Error loading document:', err);
        setError(err instanceof Error ? err.message : 'An error occurred');
      }
    };

    if (documentId) {
      loadDocument();
    }
  }, [documentId]);

  useEffect(() => {
    if (viewerConfig && containerRef.current && window.PSPDFKit) {
      console.log('Initializing viewer with config:', viewerConfig);
      
      window.PSPDFKit.load({
        serverUrl: 'http://localhost:5000/',
        container: containerRef.current,
        documentId: viewerConfig.documentId,
        authPayload: { jwt: viewerConfig.jwt },
        instant: true
      }).catch((err: Error) => {
        console.error('PSPDFKit error:', err);
        setError(`Failed to load PDF viewer: ${err.message}`);
      });
    }
  }, [viewerConfig]);

  if (error) return <div className="error">Error: {error}</div>;
  if (!viewerConfig) return <div>Loading...</div>;

  return (
    <div className="document-viewer">
      <h1>Document Viewer</h1>
      <div ref={containerRef} style={{ width: '100%', height: '100vh' }} />
    </div>
  );
}
  1. index.html:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document Viewer</title>
    <script src="https://cdn.cloud.pspdfkit.com/pspdfkit-web@2024.8.1/pspdfkit.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

Running the Application

  1. Start Document Engine:
docker-compose up
  1. Start the backend:
cd document-engine-backend
npm run start:dev
  1. Start the frontend:
cd document-engine-frontend
npm run dev

Usage

  1. Access the Document Engine dashboard at http://localhost:5000/dashboard
  2. Log in with the default credentials:
    • Username: dashboard
    • Password: secret
  3. Upload a document and note its ID
  4. View the document at http://localhost:5173/documents/{documentId}

Building for Production

Backend:

cd document-engine-backend
npm run build

Frontend:

cd document-engine-frontend
npm run build

Support

For support, please visit Nutrient Support

Acknowledgments

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published