Initial commit — eLegal Software monorepo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user