Skip to content
richaferryPublic

About

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

35 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Docket

A self-hosted CRM + invoicing workspace for freelancers and small studios. Track clients, log activity, create and send invoices, get paid — all in one place, running on your own machine or server.

License: MIT

Features

  • Clients — contact details, status (lead/active/archived), and a running activity timeline (notes, calls, meetings, plus auto-logged invoice events)
  • Invoices — line items, tax and discount, payment terms (Net 7/14/30/45/60, due on receipt, or custom) that auto-calculate the due date, multi-currency support, draft → sent → overdue → paid lifecycle, PDF export, and a no-login shareable link for clients to view and download their invoice
  • Dashboard — outstanding balance, overdue amount, revenue this month, recent activity
  • Settings — business profile, invoice numbering, default tax rate, and payment instructions

Getting started

git clone https://github.com/richaferry/docket.git
cd docket
npm install
npm run dev

Open http://localhost:3000. On first run you're sent to /setup to create your admin account and business name.

Data lives in a local SQLite file at data/app.db, created and migrated automatically on startup — nothing to run by hand. It's gitignored; back it up however you like.

Running with Docker

The project ships with Docker files for both production and local development. Requires Docker (or Docker Desktop).

Development (hot reload)

cp .env.example .env
docker compose -f docker-compose.dev.yml up --build

Open http://localhost:3000. Your host source is mounted into the container, so edits hot-reload via next dev.

Production

cp .env.example .env   # then set PUBLIC_URL to your real URL
docker compose up -d --build

Where your data lives

The SQLite database is stored in a Docker named volume (docket-data, mounted at /app/data), so it survives container restarts and rebuilds.

Back it up and restore it like any other file:

# backup
docker compose cp docket:/app/data/app.db ./app.db.backup

# restore
docker compose cp ./app.db.backup docket:/app/data/app.db

For self-hosting, put a reverse proxy (Caddy, nginx, or Cloudflare Tunnel) in front of the container for TLS — don't expose the port directly.

Configuration

Everything workspace-related (business profile, invoice defaults, email provider) is configured through the Settings page. One exception is the Public URL — where this app is reachable from the internet, used to build the client-facing invoice link in emails — which is deployment config, not workspace data, so it's set via an environment variable instead:

# .env
PUBLIC_URL=https://invoices.yourdomain.com

Sending invoices is disabled with a clear error until this is set. Restart the server after changing it.

Sending invoices by email

Invoice email is sent through a shared sender configured by the superadmin at /admin/email (MailAnvil key or SMTP + from-name/from-email). Every tenant uses that sender; tenants don't enter mail-server credentials or a custom From: address. Client replies are routed to the tenant's business email from Settings → Business profile.

The shared sender also powers platform auth emails (verification, password reset). Env vars (MAILANVIL_KEY, SMTP_*, PLATFORM_EMAIL) act as a fallback when the DB config isn't set.

See docs/paid-email-sending.md for the deliverability rationale and future options (domain verification or a locked platform subdomain).

Stack

Next.js (App Router) · TypeScript · Tailwind CSS v4 · Drizzle ORM + Postgres · @react-pdf/renderer · Nodemailer · Server Actions for all mutations. Containerised with Docker (Dockerfile, Dockerfile.dev, docker-compose.yml).

Scripts

  • npm run dev — start the dev server
  • npm run build / npm start — production build & serve
  • npm run db:generate — generate a Drizzle migration after changing src/db/schema.ts
  • npm run db:migrate — apply pending migrations
  • npm run admin:create -- you@docket.app — create/update a platform superadmin (accesses the /admin area; prompts for a password)
  • npm run db:studio — browse the local database
  • npm test / npm run test:watch — run the test suite once / in watch mode
  • npm run test:coverage — run tests with a coverage report

Testing & coverage

The project uses Vitest with the V8 coverage provider. Core logic — invoicing math, payment terms, formatting utilities, the mailer, and the client server actions — is unit-tested with the database, auth, and next/navigation mocked out, so the suite runs without a database or server.

npm test               # run all tests once
npm run test:watch     # run tests in watch mode
npm run test:coverage  # run tests + coverage report

Running npm run test:coverage writes a browsable HTML report to coverage/ (gitignored) plus a machine-readable coverage-summary.json.

Coverage is enforced in CI — the test job fails if thresholds aren't met:

Metric Threshold
Statements 100%
Functions 100%
Lines 100%
Branches ≥95%

Current measured coverage: 100% statements / functions / lines and 95.4% branches across src/lib/mailer.ts, src/lib/invoices.ts, src/lib/utils.ts, src/lib/payment-terms.ts, and src/actions/clients.ts (63 tests).

The coverage report is also uploaded as a downloadable coverage-report artifact on every push/PR to main (GitHub Actions → the test job → summary).

Contributing

Contributions are welcome — see CONTRIBUTING.md.

Security

See SECURITY.md for how to report a vulnerability.

License

MIT

About

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages