Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

snippet-share

GitHub stars Demo License: MIT Node

Live demo

A self-hosted pastebin/snippet sharing service. Web UI to paste and share a link, a CLI to pipe text straight in from the terminal. File-based storage by default, optional Postgres for multi-instance setups.

Features

  • Web UI — paste, set an optional expiry, optionally burn-after-read, get a shareable link
  • Drag & drop a file straight into the paste box — language auto-fills from the file extension
  • Language dropdown with auto-detect (via highlight.js) as the default, or pick one manually
  • Live character/line counter as you type
  • Syntax highlighting on the view page (via highlight.js), matched to the paste's language
  • Dark/light theme toggle, remembered across visits, mobile-friendly layout throughout
  • CLI client — cat file.txt | node cli/pastebin.js and get a URL back
  • Raw text endpoint for any paste (/:id/raw) — good for curl-ing into scripts
  • Expiry options: never, 10 minutes, 1 hour, 1 day, 7 days
  • Burn-after-read — paste is deleted the moment it's viewed once
  • Optional password protection — set a password at creation, viewers need it to read the paste (works with the web UI, the raw endpoint, and the JSON API)
  • Early deletion — every paste gets a one-time delete token at creation, so you can remove it before it naturally expires (via the web UI's "Delete now" button, the CLI, or the API)
  • File-based storage by default, one JSON file per paste — no database required to get started
  • XSS-safe rendering (content is HTML-escaped before display, even with syntax highlighting layered on top)

Setup

git clone https://github.com/unitedevz/snippet-share.git
cd snippet-share
npm install
cp .env.example .env
npm start

Open http://localhost:3000. By default it runs with zero setup — file-based storage, no database needed.

Deploying to Vercel

Vercel needs two things this repo includes: vercel.json (rewrites every request to the serverless function, since paste routes like /:id aren't under /api/) and api/index.js (wraps the Express app as that function).

File storage works there with zero configuration — DATA_DIR automatically defaults to /tmp when it detects it's running on Vercel (only /tmp is writable on their platform; everywhere else is read-only). The tradeoff: /tmp isn't guaranteed to persist. It survives for the life of a warm container — often several minutes — but a cold start or redeploy can wipe it, so pastes can occasionally disappear unpredictably.

For anything you actually need to persist reliably, set STORAGE_DRIVER=postgres and DATABASE_URL in your Vercel project's environment variables — that's an upgrade you can make whenever it's convenient, not a requirement just to get the site working.

Storage: file (default) or Postgres (optional)

Set STORAGE_DRIVER in .env:

STORAGE_DRIVER Setup required Notes
file (default) None One JSON file per paste in data/. Fine for personal use or a single instance.
postgres DATABASE_URL Works with any managed Postgres — Neon, Supabase, Railway, RDS, or your own. Table is created automatically on first use.

To use Postgres:

# .env
STORAGE_DRIVER=postgres
DATABASE_URL=postgres://user:password@host:5432/dbname

Both backends implement the exact same interface, so nothing else about the app changes — same routes, same CLI, same behavior. Postgres is worth it once you're running more than one instance (e[...]

CLI

# from a file
node cli/pastebin.js notes.txt

# from stdin
cat error.log | node cli/pastebin.js

# with options
cat secret.txt | node cli/pastebin.js --expires 1h --burn --lang text

# pointing at a deployed instance instead of localhost
SERVER_URL=https://paste.yoursite.com node cli/pastebin.js notes.txt

# delete a paste early, using the token printed alongside its URL
node cli/pastebin.js delete <id> <deleteToken>

# password-protect a paste
cat secret.txt | node cli/pastebin.js --password hunter2

Every create prints the URL to stdout and the delete command to stderr, so pastebin file.txt > urls.txt still captures just the URL — the delete token is right there in your terminal if you want it.

Fetching a password-protected paste programmatically (curl, scripts, etc.) needs the password in an X-Paste-Password header — never a query string, since those end up in server access logs and browser history:

curl -H "X-Paste-Password: hunter2" https://paste.yoursite.com/<id>/raw

Link it globally with npm link to use it as a plain pastebin command.

Docker

docker build -t snippet-share .
docker run -p 3000:3000 -v $(pwd)/data:/app/data snippet-share

The volume mount matters — without it, all pastes vanish when the container is removed.

Tests

npm test

Two layers of coverage:

  • tests/store.test.js — file backend against real file I/O in a temp directory
  • tests/postgres-store.mock.test.js — postgres backend's query construction and logic against a mocked pg client (no live database needed)
  • tests/postgres-store.integration.test.js — the same postgres backend against a real Postgres. Skipped locally unless TEST_DATABASE_URL is set; CI always runs it against a real Postgres[...]

How it works

  • server/store.js — dispatcher that picks the active backend based on STORAGE_DRIVER
  • server/stores/file-store.js / server/stores/postgres-store.js — the two storage backends, same interface
  • server/render.js — HTML rendering with escaping (no XSS from pasted content)
  • server/index.js — Express app: API routes (/api/pastes) and human-facing routes (/:id, /:id/raw)
  • public/ — the web UI (vanilla HTML/CSS/JS, no build step)
  • cli/pastebin.js — standalone CLI client, talks to the same API

A background sweep runs every 10 minutes to remove expired pastes even if nobody visits them (both backends).

Notes

  • Max paste size is 200,000 characters — adjust the limit in server/store.js if needed.
  • IDs are 8 hex characters (32 bits) — fine for a personal/small-team tool, not meant to resist a determined ID-guessing attacker at scale.
  • No auth on paste creation by design (keeps the CLI simple). If you deploy this publicly, consider putting it behind a reverse proxy with rate limiting.

License

MIT — see LICENSE.

Releases

Packages

Contributors

Languages