Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
122 lines
5.1 KiB
Markdown
122 lines
5.1 KiB
Markdown
# eLegal Software
|
|
|
|
All-in-one practice management for law firms. Single Node app that serves both the React SPA and the API on one port — designed to run behind Plesk's Node.js extension on a single domain.
|
|
|
|
## Stack
|
|
|
|
- **Frontend** — Vite + React 18 + TypeScript + Tailwind + Framer Motion + Recharts + lucide-react
|
|
- **API** — Fastify 5 + TypeScript + Zod
|
|
- **DB** — Drizzle ORM → DigitalOcean Managed Postgres (TLS)
|
|
- **Auth** — local: argon2id passwords + Postgres-backed sessions in httpOnly cookies (no third-party auth provider)
|
|
- **Storage** — DigitalOcean Spaces (S3-compatible, presigned uploads)
|
|
- **Email** — Resend
|
|
- **Payments** — Stripe
|
|
- **Hosting** — Plesk + Phusion Passenger (Node 20 LTS)
|
|
|
|
## Repository layout
|
|
|
|
```
|
|
.
|
|
├── apps/
|
|
│ ├── api/ # Fastify server (also serves built web/dist in prod)
|
|
│ └── web/ # Vite + React SPA
|
|
├── packages/
|
|
│ └── db/ # Drizzle schema + migrations (shared)
|
|
├── certs/ # DO Postgres CA cert (do-ca.crt) — not in git
|
|
├── scripts/
|
|
│ └── plesk-deploy.sh
|
|
├── tmp/restart.txt # touched by deploy script to bounce Passenger
|
|
└── app.js # Plesk entrypoint (loads apps/api/dist/server.js)
|
|
```
|
|
|
|
## Local development
|
|
|
|
Prerequisites: Node 20+, pnpm 9+, a Postgres database (managed DO instance, or local).
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# fill in DATABASE_URL, SESSION_SECRET, CSRF_SECRET (generate with `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`)
|
|
|
|
pnpm install
|
|
pnpm db:generate # generate SQL migrations from schema
|
|
pnpm db:migrate # apply to the DB
|
|
|
|
pnpm dev # starts api on :8080 and web on :5173 (proxies /api → :8080)
|
|
```
|
|
|
|
Visit http://localhost:5173.
|
|
|
|
## Production build
|
|
|
|
```bash
|
|
pnpm build # builds packages/db → apps/web → apps/api
|
|
pnpm start # runs node app.js → apps/api/dist/server.js
|
|
```
|
|
|
|
The API serves `apps/web/dist` at `/` with SPA fallback and routes `/api/*` to Fastify handlers.
|
|
|
|
## Plesk deployment (single domain)
|
|
|
|
1. **Create the domain** in Plesk and enable **Let's Encrypt** TLS.
|
|
2. **Install Node.js extension** (Plesk → Extensions → "Node.js"). Set Node version to **20.x** in the domain's Node.js settings.
|
|
3. **Pull the repo** into the domain's document root via Plesk → Git, or `git clone` over SSH into `/var/www/vhosts/yourdomain.com/httpdocs`.
|
|
4. **Node.js settings** in the Plesk panel for that domain:
|
|
- **Application root** → the repo root
|
|
- **Document root** → leave as default; nginx will proxy to Passenger
|
|
- **Application startup file** → `app.js`
|
|
- **Custom environment variables** → set every entry from `.env.example` (Passenger does **not** read `.env` files)
|
|
5. **Add DigitalOcean's Postgres CA** to `certs/do-ca.crt` (download from the DO Postgres dashboard) and set `DATABASE_CA_CERT_PATH=./certs/do-ca.crt`.
|
|
6. **Run the deploy script** over SSH:
|
|
```bash
|
|
bash scripts/plesk-deploy.sh
|
|
```
|
|
This installs deps, builds, runs migrations, then `touch tmp/restart.txt` to bounce Passenger.
|
|
7. **Stripe webhook** — add `https://yourdomain.com/api/stripe/webhook` in the Stripe dashboard. In Plesk → Apache & nginx → "Additional nginx directives" add:
|
|
```nginx
|
|
location /api/stripe/webhook {
|
|
proxy_request_buffering off;
|
|
}
|
|
```
|
|
8. **Auto-deploy on push** (optional) — in Plesk → Git, enable "Enable additional deploy actions" and set the script to `bash scripts/plesk-deploy.sh`.
|
|
|
|
## Environment variables
|
|
|
|
See `.env.example` for the full list. Highlights:
|
|
|
|
| Var | Purpose |
|
|
|---|---|
|
|
| `DATABASE_URL` | DO Managed Postgres connection string (`?sslmode=require`) |
|
|
| `DATABASE_CA_CERT_PATH` | Path to DO CA cert (recommended for `rejectUnauthorized: true`) |
|
|
| `SESSION_SECRET` | 32+ byte hex used to sign cookies and as Fastify cookie secret |
|
|
| `CSRF_SECRET` | 32+ byte hex for CSRF token derivation |
|
|
| `SPACES_*` | DigitalOcean Spaces credentials + bucket |
|
|
| `RESEND_API_KEY` | Transactional email |
|
|
| `STRIPE_*` | Billing |
|
|
| `PORT` | Port for Fastify (Plesk usually injects this; falls back to 8080) |
|
|
| `COOKIE_DOMAIN` | Set to your apex domain in production (e.g. `lawdesk.com`); leave blank in dev |
|
|
|
|
## Database commands
|
|
|
|
```bash
|
|
pnpm db:generate # create a new migration from schema changes
|
|
pnpm db:migrate # apply pending migrations
|
|
pnpm --filter @lawdesk/db studio # open Drizzle Studio
|
|
```
|
|
|
|
## Auth model
|
|
|
|
- Passwords hashed with **argon2id** (64MB memory cost).
|
|
- Cookie holds a 32-byte random token; the DB stores its **SHA-256 hash** (so a DB read can't impersonate users).
|
|
- Sessions are 30-day sliding (touched on every request).
|
|
- Login rate limited: 5 failed attempts per email per 15 minutes.
|
|
- All `/api/*` requests automatically attach `req.user` if a valid session cookie is present. Use `app.requireAuth` / `app.requireFirm` as preHandler guards on protected routes.
|
|
|
|
## What's next
|
|
|
|
- Wire DO Spaces upload routes for documents
|
|
- Build the `/app` dashboard (cases, time tracking, invoices)
|
|
- Free public tools (`/tools/*`)
|
|
- Stripe checkout + webhook
|
|
- Email templates via Resend
|
|
- pg-boss background jobs
|