Site-Snap - Website to PDF Capture & Annotation
Version 1.0.0
Introduction
Support: SakuraLeaf
Site-Snap is a full-featured SaaS application for capturing website snapshots as PDFs, editing them with annotations, and managing projects with subscription billing. Users can enter any URL, generate a high-quality PDF, annotate it with drawing tools, and save and share or download the result.
This documentation covers everything you need to install, configure, and deploy Site-Snap as your own SaaS platform.
Tech Stack
- Framework: Next.js (App Router) with TypeScript
- Database: PostgreSQL via Prisma ORM
- Hosting DB: Supabase (free tier works)
- Authentication: NextAuth.js (Credentials + Google OAuth)
- Payments: Paddle (subscriptions)
- Email: Resend (verification & password reset)
- Storage: AWS S3 (PDFs & thumbnails) or local filesystem
- CAPTCHA: Cloudflare Turnstile
- PDF Editor: Konva.js canvas for annotations
- Internationalization: next-intl (RTL support also)
Requirements
- Node.js 18+ and npm (or yarn)
- A Supabase account (free tier works) for the PostgreSQL database
- A Vercel account (for production deployment) or any Node.js hosting
Supabase Database Setup
Site-Snap uses PostgreSQL as its database. Supabase provides a free hosted PostgreSQL instance that works for both local development and production.
-
1. Create a Supabase account
Visit supabase.com and sign up for a free account.
-
2. Create a new project
- Click New Project in your Supabase dashboard.
- Choose a name (e.g. site-snap), set a strong database password, and select a region close to your users.
- Click "Generate a password". It will generate a URL-friendly password. Otherwise, if your password contains special characters (@ : / ? # & %), it needs to be URL-encoded.
- Click Create new project and wait for provisioning to complete.
-
3. Get the PostgreSQL connection string
- In your project dashboard, go to Settings > Database.
- Scroll to Connection string and select the URI tab.
- The connection string format looks like:
postgresql://postgres:[YOUR-PASSWORD]@db.zdzrufvqywoxerasdwji.supabase.co:5432/postgres
- Severless DB connection URL:
Note! When using in production on a serverless platform like Vercel, use the "Transaction pooler" URL (only in production, use the default above for migrations from localhost). The DB connection URL looks like:postgresql://postgres.ciglkovgphuywwtdlwmy:[YOUR-PASSWORD]@aws-1-eu-west-1.pooler.supabase.com:6543/postgres
- Replace [YOUR-PASSWORD] with the database password you set when creating the project.

Local Installation
Follow these steps to get Site-Snap running on your local machine.
-
1. Extract the project
Unzip the downloaded archive (All files). There are two folders: documentation and the site-snap project.
-
2. Setup Git and GitHub
While Git and GitHub are optional, they are highly recommended for seamless deployment to platforms like Vercel. Using Git also allows you to track custom modifications and manage updates effortlessly. If you're already set up, feel free to skip this step.If you're new to version control, check out this great primer: Git & GitHub Setup for Beginners
-
3. Install dependencies
Open a terminal in the project root and run:npm installNote! - it might take a while, playwright package is about 100MB+
-
4. Configure environment variables
- Copy .env.example to .env.local:
- Open .env.local and configure the following variables:
cp .env.example .env.localGOOGLE_CLIENT_ID — Google sign-in clientGOOGLE_CLIENT_SECRET — Google sign-in secretDATABASE_URL — Your Supabase connection string (from the previous section)AUTH_SECRET — Generate with: openssl rand -base64 32 (any secret, a sample secret: k1+40yRyv/5kF0Dc6*@nafSS02Px2WCLfE667Jcfg0=)AUTH_URL — http://localhost:3000NEXT_PUBLIC_BASE_URL — http://localhost:3000SITE_NAME — SiteSnap (or your custom brand name)BROWSER_PROVIDER — playwright for local development, sparticuz for VercelPDF_LOAD_IMAGES_STRATEGY — full (loads all images including lazy-loaded) or defaultNEXT_PUBLIC_PDF_DOWNLOAD_FILENAME — Default downloaded PDF filename (e.g. sitesnap-edited.pdf)NEXT_PUBLIC_THEME_STORAGE_KEY — LocalStorage key for theme preference (e.g. sitesnap-theme)NEXT_PUBLIC_CONSENT_COOKIE_KEY — Cookie name for consent banner (e.g. sitesnap-consent) -
5. Generate the Prisma client
npx prisma generateThis generates the Prisma client based on your database schema.

-
5. Run database migrations
npx prisma migrate dev --name initThis creates all required tables in your Supabase database.

-
6. Verify migration
Visit supabase.com, sign in and check the newly generated tables.
-
7. Prisma studio (optional)
npx prisma studioPrisma Studio is a visual editor for your database. It opens a local web interface that allows you to view, explore, and manipulate your data without writing SQL or using a separate database GUI (like pgAdmin or TablePlus).
-
8. Seed the database
npm run seed:plansThis seeds the default plans (Free + Pro). Each new user will be automatically assigned to the free plan. Plans can be edited from the admin dashboard as well.npm run seed:adminCreates an admin user:Email: admin@sitesnap.devPassword: admin123Important!!!: After logging in as admin for the first time, go to Dashboard > My Account and change the default password immediately.Optionally, seed a test customer account:Note! - A customer account is needed to test Paddle integration. For admin roles, the subscription tab is not shown within the admin dashboard.npm run seed:customerEmail: customer@sitesnap.devPassword: customer123
-
7. Start the development server
npm run devThe app will be available at http://localhost:3000.

Database Migrations & Prisma
Site-Snap uses Prisma as its ORM. The database schema is defined in prisma/schema.prisma.
Prisma Workflow
The typical workflow when working with Prisma is: Edit schema → Generate client → Run migration.
Common Commands
Seed Scripts
The project includes several seed scripts to populate the database with initial data:
Admin Settings (App Settings)
Once logged in as an admin, go to Dashboard > App Settings. Most third-party integrations are configured from this page rather than environment variables. This makes it easy to update settings without redeploying.
Configurable Settings
The App Settings page contains the following configuration sections:
Google Sign-In Setup
To enable Google Sign-In, you need to create OAuth 2.0 credentials in the Google Cloud Console and enter them in the Admin App Settings.
- 1. Go to Google Cloud Console
-
2. Create or select a project
- At the top-left, click the project dropdown.
- Choose an existing project or click New Project to create one.
- Give it a name (for example Site-Snap Auth) and click Create.
-
3. Enable the OAuth API
- In the sidebar go to APIs & Services → Library.
- Search for Google Identity Services.
- Click the API entry, then click Enable.
-
4. Configure the OAuth consent screen
- Go to APIs & Services → OAuth consent screen.
- Choose External (for users outside your organization) or Internal (G Suite orgs only).
- Fill in the required fields:
App name: your application's display nameUser support email: contact/support emailDeveloper contact email: email for Google to reach you
- Add the scopes: profile and email.
- Click Save and continue.
-
5. Create OAuth 2.0 credentials
- Go to APIs & Services → Credentials.
- Click + Create Credentials → OAuth client ID.
- Choose:
Application type: Web applicationName: e.g., Site-Snap Web Client
- Under Authorized JavaScript origins, add:
- http://localhost:3000 (for local development)
- https://your-domain.com (for production)
- Under Authorized redirect URIs, add:
- http://localhost:3000/api/auth/callback/google (for local development)
- https://your-domain.com/api/auth/callback/google (for production)
-
6. Copy your credentials into App Settings
- When the OAuth client is created you'll see:
Client ID — Copy this into .env GOOGLE_CLIENT_IDClient Secret — Copy this into .env GOOGLE_CLIENT_SECRET

Cloudflare Turnstile Setup
Site-Snap uses Cloudflare Turnstile for bot protection on sign-in, sign-up, and PDF generation forms.
-
1. Create a Cloudflare account
Visit dash.cloudflare.com and sign up or log in.
-
2. Navigate to Turnstile
In the Cloudflare dashboard, go to Application Security > Turnstile.
-
3. Add a site
- Click Add site.
- Enter your site name and domain (e.g. localhost for development, your-domain.com for production).
- Choose a widget type (Managed is recommended).
- Click Create.
-
4. Copy the keys
After creating the site, you'll receive:Site Key — The public key used in the frontend widgetSecret Key — The private key used for server-side verification
-
5. Enter the keys in App Settings
- Go to Admin > App Settings > Cloudflare Turnstile.
- Enter the Site Key and Secret Key.
- Optionally, you can disable Turnstile verification for PDF generation if desired.
- Save the settings.
Resend (Email) Setup
Site-Snap uses Resend for transactional emails, including email verification and password reset.
-
1. Create a Resend account
Visit resend.com and sign up for a free account.
-
2. Get your API key
- In the Resend dashboard, go to API Keys.
- Click Create API Key.
- Give it a name and select the appropriate permissions.
- Copy the generated API key.
-
3. Verify your domain (recommended)
- Go to Domains in the Resend dashboard.
- Add your domain and follow the DNS verification steps.
- This allows you to send from a custom "From" address (e.g. noreply@yourdomain.com).
-
4. Configure in App Settings
- Go to Admin > App Settings > Resend (Email).
- Enter your API Key.
- Enter the From Email address (e.g. noreply@yourdomain.com).
- Save the settings.
Emails sent by Site-Snap
Emails templates
Paddle (Payments) Setup
Site-Snap integrates with Paddle to handle subscription payments. Paddle acts as a Merchant of Record, handling VAT/sales tax, invoicing, and global payments on your behalf.
Paddle supports sellers in over 200 countries and territories, allowing you to sell globally with no additional country-specific setup. Supported countries
Note! When testing with the sandbox, use the test card numbers listed in the "Testing with Sandbox" section below.
Important Notice: Paddle API Key Expiration!!! --- Please be advised that API keys issued by Paddle are valid for a period of 90 days from the date of creation. To ensure uninterrupted service and avoid any potential disruptions to your integrations, we strongly recommend monitoring your API key expiration dates and generating new keys before they expire.
Prerequisites
- Create a Paddle account at paddle.com. For testing, create an account with their sandbox environment at sandbox-vendors.paddle.com.
- Complete your Paddle seller setup and verification.
Configuration Steps
-
1. Create a Product and Price in Paddle
- In the Paddle dashboard, go to Catalog > Products.
- Click + New Product and fill in the product details (e.g. "Site-Snap Pro").
- Under the product, create a Price (e.g. $19/month recurring).
- Copy the Price ID (starts with pri_) — you'll need this later.
-
2. Get API Credentials
- Go to Developer Tools > Authentication in the Paddle dashboard.
- Copy your API Key.
- Go to Developer Tools > Client-side tokens and copy your Client Token.
-
3. Set up the Webhook
- Go to Developer Tools > Notifications in the Paddle dashboard.
- Click + New Destination.
- Set the webhook URL to: {YOUR_DOMAIN}/api/paddle/webhook
- For local testing: http://localhost:3000/api/paddle/webhook (use a tunnel like ngrok)
- For production: https://your-domain.com/api/paddle/webhook
- Select the following events:
- subscription.created
- subscription.updated
- subscription.canceled
- transaction.completed
- Copy the generated Webhook Secret Key.
-
4. Enter credentials in App Settings
- Go to Admin > App Settings > Paddle (Payments).
- Enter your API Key, Client Token, and Webhook Secret.
- Set the Environment to sandbox for testing or production for live payments.
- Save the settings.
-
5. Link the Price ID to your plan
- Go to Admin > Plans.
- Edit the Pro plan (or any paid plan).
- Paste the Paddle Price ID into the Paddle Price ID field.
- Save the plan.
Webhook Events
Site-Snap automatically handles these Paddle webhook events:
Testing with Sandbox
You can test payments using your Paddle sandbox account. Open a checkout, then use these card details to test payment by card:
Use any expiration date in the future.
AWS S3 Storage Setup
https://aws.amazon.com/console/
Mention - that on serverless local storage does not work
Site-Snap can store generated PDFs and project thumbnails on AWS S3. By default, local files are stored locally in the public/uploads/ directory. For production, S3 is recommended.
-
1. Create an AWS account
Visit aws.amazon.com and sign up if you don't have an account.
-
2. Create an S3 bucket
- Go to the S3 service in the AWS console.
- Click Create bucket.
- Enter a unique bucket name (e.g. sitesnap-files).
- Choose a region close to your users.
- Under Block Public Access, uncheck "Block all public access" if you want PDFs to be directly accessible via URL. Acknowledge the warning.
- Click Create bucket.
-
3. Create an IAM user with S3 permissions
- Go to the IAM service in the AWS console.
- Click Users > Create user.
- Enter a username (e.g. sitesnap-s3).
- Attach the AmazonS3FullAccess policy (or create a custom policy scoped to your bucket).
- Create the user and go to Security credentials > Create access key.
- Copy the Access Key ID and Secret Access Key.
-
4. Configure in App Settings
- Go to Admin > App Settings > AWS S3 Storage.
- Enter your Bucket Name, Region, Access Key ID, and Secret Access Key.
- Switch the Storage Mode from local to s3.
- Save the settings.
S3 is used for storing generated PDFs and project thumbnails. Files are stored with the key pattern: pdfs/{projectId}.pdf and thumbnails/{projectId}.jpg.
AWS S3 Storage Setup
https://aws.amazon.com/console/
Note! Serverless local storage does not persist files reliably. Use S3 for production.
Site-Snap can store generated PDFs and project thumbnails on AWS S3. By default, local files are stored in public/uploads/. For production, S3 is recommended.
-
1. Create an AWS account
Visit aws.amazon.com and sign up if you don't have an account.
-
2. Create an S3 bucket
- Go to the S3 service in the AWS console.
- Click Create bucket.
- Enter a unique bucket name (e.g., sitesnap-files).
- Choose a region close to your users.
- Under Object Ownership, select ACLs disabled (Bucket owner enforced).
- Under Block Public Access, uncheck "Block public access to buckets and objects granted through any bucket policy" to allow public reads via a bucket policy.
- Click Create bucket.
-
3. Configure CORS for browser access
- Open your bucket → Permissions → CORS configuration.
- Paste the following JSON to allow your frontend to fetch files:
- Replace "https://site-snap-gilt.vercel.app" with your domain:
Alternatively, you can allow access from all domains.
- Save the CORS configuration.
-
4. Add a bucket policy for public read access
- Open Permissions → Bucket policy.
- Paste the following JSON to allow public access to objects:
- Save the bucket policy.
-
5. Create an IAM user with S3 upload permissions
- Go to the IAM service in the AWS console.
- Click Users > Create user.
- Enter a username (e.g., sitesnap-s3).
- Click "Users" > sitesnap-s3.
- Click - Permissions policies tab.
- Click - Add Permission.
- Click - Attach policies directly.
- Search "AmazonS3FullAccess".
- Check "AmazonS3FullAccess" and click save.
- Create the user.
- Go to Security credentials > Create access key.
- Copy the Access Key ID and Secret Access Key to configure your app.
-
6. Configure in App Settings
- Go to Admin > App Settings > AWS S3 Storage.
- Enter your Bucket Name, Region, Access Key ID, and Secret Access Key.
- Switch the Storage Mode from local to s3.
- Save the settings.
Files are stored in S3 with the key patterns:
- pdfs/{projectId}.pdf
- thumbnails/{projectId}.jpg
Deploy to Vercel
Site-Snap is optimized for deployment on Vercel. Follow these steps to deploy your instance.
Prerequisites
- A Vercel account
- A GitHub repository with the Site-Snap project
- Your Supabase database already set up (same DB for local and production)
-
1. Push your code to GitHub
Create a private GitHub repository and push the Site-Snap project code to it.
-
2. Import the project in Vercel
- Log in to vercel.com.
- Click Add New > Project.
- Import your GitHub repository.
- Select the Next.js framework preset (should be auto-detected).
-
3. Set environment variables
In the Vercel project settings, add the following environment variables with production values:Note!!! You might only get the https://your-domain.vercel.app/ after your first deploy, you will have to re-edit: AUTH_URL and NEXT_PUBLIC_BASE_URL.GOOGLE_CLIENT_ID — Google sign-in clientGOOGLE_CLIENT_SECRET — Google sign-in secretDATABASE_URL — Same Supabase connection string (shared database between local and production)AUTH_SECRET — Generate a new production secret: openssl rand -base64 32AUTH_URL — https://your-domain.vercel.appNEXT_PUBLIC_BASE_URL — https://your-domain.vercel.appSITE_NAME — Your app name (e.g. SiteSnap)BROWSER_PROVIDER — sparticuz (required for Vercel serverless environment)PDF_LOAD_IMAGES_STRATEGY — full or defaultNEXT_PUBLIC_PDF_DOWNLOAD_FILENAME — e.g. sitesnap-edited.pdfNEXT_PUBLIC_THEME_STORAGE_KEY — e.g. sitesnap-themeNEXT_PUBLIC_CONSENT_COOKIE_KEY — e.g. sitesnap-consent
-
4. Deploy
Click Deploy. Vercel will build and deploy the application. The build command automatically runs prisma generate && next build.
-
5. Run database seeds (if needed)
Since the database is shared between local and production, you can run seed scripts from your local terminal if you haven't already:npm run seed:adminAlternatively, you can use the Vercel CLI (vercel env pull + run locally).
-
6. Update third-party configurations for production
After deploying, update the following to use your production domain:Paddle webhook URL — Update to https://your-domain.vercel.app/api/paddle/webhookGoogle OAuth redirect URIs — Add https://your-domain.vercel.app/api/auth/callback/googleCloudflare Turnstile — Add your production domain to the allowed domains list
-
7. Configure a specific production branch in Vercel (optional)
You can configure Vercel to deploy only a specific branch to production. This is the most common setup when you want full control over what goes live.Open your project — Go to your project in the Vercel DashboardNavigate to project settings — Click Settings → DeploymentsSet Production Branch — Under Production Branch, select your desired branch (e.g. main, release, prod)Save changes — Click SaveNow:Selected branch — Deploys to ProductionAll other branches — Create Preview Deployments onlySwith environment to Preview — Branch Tracking > Disable


Deploy to VPS
Deploying Site-Snap to your own VPS (for example a DigitalOcean Droplet) is absolutely possible, but it can be more challenging than deploying to Vercel.
A traditional VPS setup usually requires installing and configuring tools such as:
- Node.js
- Process manager (PM2 or similar)
- Nginx (reverse proxy)
- SSL certificates (Let's Encrypt)
- Firewall and server security
If you prefer a much simpler experience, we highly recommend using Dokploy. It is open-source and provides a deployment experience similar to Vercel, but on your own VPS.
Dokploy handles:
- Automatic deployments from your Git repository
- SSL certificate management
- Reverse proxy configuration
- Application process management
👉 To get started with Dokploy, watch this step-by-step tutorial:
Dokploy Deployment Guide (YouTube)
Prerequisites
- A VPS (e.g. DigitalOcean, Hetzner, AWS, etc.)
- Your GitHub repository with the Site-Snap project
- Your Supabase database already set up (same DB for local and production)
Managing Plans (Admin)
Go to Admin > Plans to manage subscription plans. Site-Snap ships with two default plans: Free and Pro.
Plan Configuration
Each plan has the following settings:
To create additional paid plans, create a new price in your Paddle dashboard first, then create the plan in Site-Snap and link the Paddle Price ID.

Managing Users (Admin)
Go to Admin > Users to manage all registered users.
Features
- View all users — See a list of all registered users with their name, email, role, plan, and registration date.
- Search and filter — Search users by name, email, or filter by plan.
- Create users manually — Create new user accounts with a specific role and plan assignment.
- Assign plans — Change a user's subscription plan.
- Override limits — Set custom project and storage limits for individual users, overriding their plan defaults.
- Delete users — Remove a user account. This cascades to delete all their projects, files, and subscription data.


Using the App (End User Perspective)
This section describes the app from a customer's point of view.
Homepage — Capture a PDF
On the homepage, users enter a website URL and click the capture button. Site-Snap generates a high-quality PDF of the page. No account is required for basic captures, though Turnstile verification is used.

Sign Up / Sign In
Users can create an account with email and password, or sign in with Google (if configured). After signing up, users receive a verification email and must verify their address before they can log in.
Dashboard — My Projects
The dashboard shows all saved projects in a grid view with thumbnails. Users can view, edit, or delete their projects from here.

PDF Editor
The editor lets users annotate PDFs using drawing tools powered by Konva.js canvas. Users can add shapes, text, and freehand drawings on top of the PDF, then save or download the annotated version.

Subscription — Upgrade / Downgrade
Users can view their current plan and upgrade to a paid plan via the Paddle checkout overlay. Downgrading reverts the user to the free plan at the end of the current billing period.

Account Settings
Users can update their profile name, change their password, and delete their account. Deleting an account cancels any active subscription and removes all projects and files.
Internationalization
Site-Snap supports multiple languages out of the box using next-intl.
Supported Languages
Translation Files
Language files are located in the /messages/ directory at the project root. Each language has its own JSON file:
Adding a New Language
- Create a new JSON file in the /messages/ directory (e.g. messages/de.json for German).
- Copy the structure from messages/en.json and translate all values.
- Add the new language metadata (locale code, label, direction) to @/messages/languages-meta.
- RTL languages are supported.
Editing Translations
Open the corresponding JSON file in /messages/ and edit the translation values. The app will use the updated translations after a rebuild or in development mode immediately.
Environment Variables Reference
Below is a full reference of all environment variables used by Site-Snap.
Required Variables
Browser & PDF Settings
Branding Keys
Admin UI Settings (Not env vars)
The following settings are managed via the Admin UI at Dashboard > App Settings, not through environment variables:
Changelog
Version 1.0.0
- Website-to-PDF capture from any URL
- PDF annotation editor with drawing tools (Konva.js canvas)
- Project management (create, view, edit, delete)
- User authentication (email/password + Google OAuth)
- Email verification and password reset (via Resend)
- Subscription billing with Paddle integration
- Free and Pro tier system with configurable limits
- Admin dashboard: manage users, plans, and app settings
- User limit overrides per account
- AWS S3 or local file storage
- Cloudflare Turnstile bot protection
- Multi-language support
- Dark mode support
- Responsive design
- SSRF protection for PDF generation
- Webhook handling for payment events
- Account deletion with cascade cleanup