A full-stack legal practice management system built with Next.js 15, Go (Gin), PostgreSQL, and AI-powered chat. Manage clients, matters, billing, documents, and time tracking from a single dashboard.
If encountering issues contacting on LinkedIn at https://www.linkedin.com/in/raph-mwanza-1b1203151/ or on discord add my username @t-paine
- How the App Works
- Tech Stack
- Prerequisites
- Installation & Running with Docker Desktop
- Default Login Credentials
- Environment Variables Reference
- Project Structure
- Security Features
- Production Infrastructure
- API Endpoints
Lumina Legal is a three-tier web application:
Browser (React) ──► Next.js Frontend (port 3000) ──► Go API Backend (port 8080) ──► PostgreSQL
| Layer | Technology | Role |
|---|---|---|
| Frontend | Next.js 15 + React 19 | Server-side renders pages, proxies API calls to Go backend |
| Backend API | Go + Gin framework | REST API, JWT authentication, business logic, AI chat orchestration |
| Database | PostgreSQL 15 | Persistent storage for all entities |
| Auth | NextAuth.js (JWT strategy) | Handles login flow; frontend obtains a JWT from the Go backend and injects it into all subsequent API calls |
| Module | What It Does |
|---|---|
| Dashboard | Real-time overview — billable hours, active matters, trust balance, revenue chart, upcoming tasks |
| Clients | CRUD for client records with search, conflict-check tracking, and trust balance management |
| Matters | Legal case management — status tracking (INTAKE → ACTIVE → PENDING → CLOSED → ARCHIVED), attorney assignment, statute of limitations dates |
| Time Tracking | Log billable/non-billable hours per matter; weekly & daily analytics |
| Billing & Invoices | Create/send/track invoices (DRAFT → SENT → PAID → OVERDUE → VOID); revenue statistics |
| Documents | Upload, categorize, and tag legal documents (PDF, DOCX, XLSX, images); attached to matters |
| Analytics | Audit logs, daily activity tracking, compliance overview |
| Admin Panel | User management (create/edit/delete users), role assignment, system stats; restricted to ADMIN role |
| AI Chat Assistant | Multi-provider AI (OpenAI, Gemini, Anthropic, DeepSeek) with full CRM context injection — the AI knows your matters, clients, invoices, and uploaded documents |
- User enters email/password on the login page
- Next.js sends credentials to the Go backend (
POST /api/auth/login) - Go backend validates against bcrypt-hashed password in PostgreSQL
- On success, Go backend returns a signed JWT token
- NextAuth stores the JWT in the browser session
- Next.js middleware injects the JWT into every subsequent API request via
Authorization: Bearer <token> - Go middleware validates the JWT on every protected route
| Role | Permissions |
|---|---|
| ADMIN | Full access + user management + audit logs + system stats |
| ATTORNEY | Full access to all CRM features |
| PARALEGAL | Full access to all CRM features |
| CLERK | Full access to all CRM features (can be restricted in future) |
Frontend:
- Next.js 15.1.0, React 19, TypeScript
- Tailwind CSS 3.4, Framer Motion (animations)
- Zustand (state), Recharts (charts), Zod (validation)
- NextAuth v4 (JWT-based sessions)
- Prisma 5.22 (schema management + NextAuth adapter)
Backend:
- Go 1.24, Gin web framework
- GORM ORM, golang-jwt/v5
- bcrypt password hashing, UUID primary keys
- Multi-provider AI integration (OpenAI, Anthropic, Gemini, DeepSeek)
Infrastructure:
- PostgreSQL 15 (Alpine)
- Docker multi-stage builds
- Docker Compose orchestration
- Docker Desktop — Download here
- During installation, keep the default WSL 2 backend option checked
- After installation, Docker Desktop must be running (look for the whale icon in the system tray)
- Git — Download here (to clone the repo)
Note: You do NOT need to install Node.js, Go, or PostgreSQL locally — Docker handles everything.
git clone https://github.com/raphmwanza/Law_CRM.git
cd Law_CRMCopy the example and fill in your secrets:
cp .env.example .envOpen .env in a text editor and set at minimum these two values:
# Generate a strong random password (32+ characters)
POSTGRES_PASSWORD=your_super_secret_database_password_here
# Generate with: openssl rand -base64 32
# Or just use any long random string (32+ chars)
NEXTAUTH_SECRET=your_super_secret_auth_key_hereOptional — Enable AI Chat: Add at least one AI provider API key:
GEMINI_API_KEY=your_gemini_api_key_here
# or
OPENAI_API_KEY=your_openai_api_key_here
# or
ANTHROPIC_API_KEY=your_anthropic_api_key_hereOpen a terminal in the Law_CRM directory and run:
docker compose up --build -dThis will:
- Build the Go backend (compiles the API server)
- Build the Next.js frontend (compiles the React app)
- Start PostgreSQL, the API, and the frontend
- Auto-migrate the database schema on first boot
First build takes 3–5 minutes. Subsequent builds are faster due to Docker layer caching.
docker compose psYou should see all three services (api, app, db) with status Up (healthy).
docker compose exec api ./server --seedThis populates the database with sample users, clients, matters, invoices, time entries, documents, and audit logs.
Navigate to http://localhost:3000 in your browser.
docker compose downTo also delete the database data:
docker compose down -vdocker compose up --build -d# All services
docker compose logs -f
# Specific service
docker compose logs -f api
docker compose logs -f app
docker compose logs -f db- Open Docker Desktop
- Go to the Containers tab
- You'll see
law_crmwith sub-containers forapi,app, anddb - Click any container to view logs, inspect env vars, or open a terminal
- Use the
▶️ / ⏹️ buttons to start/stop containers - The
appcontainer's port3000is clickable — it opens the app in your browser
After running docker compose exec api ./server --seed:
| Password | Role | |
|---|---|---|
admin@lumina.law |
password123 |
ADMIN |
j.patterson@lumina.law |
password123 |
ATTORNEY |
s.williams@lumina.law |
password123 |
ATTORNEY |
l.chen@lumina.law |
password123 |
ATTORNEY |
r.martinez@lumina.law |
password123 |
PARALEGAL |
⚠️ Change these passwords immediately in a real deployment via the Admin panel.
| Variable | Required | Default | Description |
|---|---|---|---|
POSTGRES_PASSWORD |
Yes | — | Database password |
NEXTAUTH_SECRET |
Yes | — | JWT signing secret (32+ chars) |
POSTGRES_USER |
No | postgres |
Database username |
POSTGRES_DB |
No | lumina_legal |
Database name |
NEXTAUTH_URL |
No | http://localhost:3000 |
Canonical frontend URL |
AI_PROVIDER |
No | auto-detect | Default AI: openai, gemini, anthropic, deepseek |
OPENAI_API_KEY |
No | — | OpenAI API key |
GEMINI_API_KEY |
No | — | Google Gemini API key |
ANTHROPIC_API_KEY |
No | — | Anthropic API key |
DEEPSEEK_API_KEY |
No | — | DeepSeek API key |
CORS_ORIGINS |
No | localhost:3000 |
Additional allowed CORS origins |
GIN_MODE |
No | release |
Go API mode (debug / release) |
ENABLE_HSTS |
No | false |
Set true to send HSTS header (when behind TLS) |
BACKUP_RETENTION_DAYS |
No | 30 |
Days to keep database backups |
Law_CRM/
├── docker-compose.yml # Orchestrates all services (app, api, db, caddy, backup)
├── Dockerfile # Frontend (Next.js) multi-stage build
├── Caddyfile # Reverse proxy config (auto-TLS in production)
├── .env.example # Template for required environment variables
│
├── .github/workflows/
│ └── ci.yml # CI/CD pipeline (lint, build, test, Docker, security)
│
├── backend/
│ ├── Dockerfile # Backend (Go) multi-stage build
│ ├── main.go # Entry point, route definitions, env validation
│ ├── config/database.go # PostgreSQL connection + auto-migration
│ ├── middleware/middleware.go # JWT auth, token blacklist, rate limiter, security headers, structured JSON logging
│ ├── models/models.go # GORM models (User, Client, Matter, Invoice, etc.)
│ ├── handlers/ # REST API handlers (one file per resource)
│ │ ├── helpers.go # Shared utilities: pagination, audit logging, input validation
│ │ ├── auth.go # Login/logout with account lockout
│ │ ├── admin.go # User management, password reset, unlock, audit logs
│ │ ├── clients.go # Client CRUD + conflict checking
│ │ ├── matters.go # Matter CRUD with data-level authorization
│ │ ├── invoices.go # Invoice CRUD + CSV export + revenue stats
│ │ ├── time_entries.go # Time entry CRUD + CSV export
│ │ ├── documents.go # Document CRUD + file upload
│ │ └── ... # Dashboard, analytics, chat, users
│ ├── ai/ # Multi-provider AI integration
│ └── seed/seed.go # Database seeder with sample data
│
├── src/
│ ├── app/ # Next.js App Router pages
│ ├── components/ # React components (dashboard, billing, clients, etc.)
│ ├── lib/ # Auth config, API hooks, Prisma client
│ └── middleware.ts # Next.js middleware (JWT injection into API calls)
│
├── prisma/
│ └── schema.prisma # Database schema (source of truth for models)
│
└── public/ # Static assets
The following security measures are implemented:
- No hardcoded secrets — all sensitive values (DB password, JWT secret) come from environment variables; app refuses to start if not set
- Bcrypt password hashing — cost factor 12
- JWT authentication — HS256 signed tokens with 24-hour expiry
- Role-based access control — ADMIN, ATTORNEY, PARALEGAL, CLERK roles with admin-only routes
- Account lockout — 5 failed login attempts locks the account for 15 minutes; admins can unlock via API
- Token blacklist — logout endpoint revokes JWT tokens; revoked tokens are rejected immediately
- CSRF/cookie hardening — NextAuth cookies use
httpOnly,sameSite: lax, andsecureflag in production with__Secure-/__Host-cookie prefixes - Data-level authorization — non-ADMIN users can only access matters assigned to them
- Input validation — all API endpoints validate required fields, max lengths, email format, and numeric ranges
- Audit logging — all CRUD operations, login/logout, account lockouts, and admin actions are recorded with user ID, IP address, and timestamp
- Security headers —
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,X-XSS-Protection,Referrer-Policy,Permissions-Policy,Cache-Control: no-store - HSTS support — opt-in via
ENABLE_HSTS=truewhen behind TLS - Thread-safe rate limiting — login endpoint rate-limited to 20 requests/minute per IP
- File upload validation — whitelist of allowed extensions (PDF, DOCX, XLSX, images, etc.), filename sanitization to prevent path traversal
- Uploads behind auth — uploaded files are served only to authenticated users
- Structured JSON logging — every request logged as JSON with request ID, method, path, status, latency, client IP, user agent, and user ID
- Non-root containers — both frontend and backend Docker images run as non-root users
- No exposed internal ports — PostgreSQL is not accessible from the host
- Resource limits — Docker containers have memory and CPU limits to prevent resource exhaustion
- Health checks — all services have health checks for automatic restart and dependency ordering
- 50MB upload size limit — prevents resource exhaustion from oversized uploads
- CORS whitelist — only explicitly allowed origins can make API calls
The docker-compose.yml includes a Caddy reverse proxy that:
- Routes
/api/*to the Go backend and everything else to Next.js - Provides automatic HTTPS with Let's Encrypt when configured with a domain
- Adds supplementary security headers
To enable auto-TLS, edit the Caddyfile and replace :80 with your domain:
crm.yourdomain.com {
...
}
An automated backup service runs daily at 2:00 AM via pg_dump:
- Backups stored in the
db_backupsDocker volume - Retention: 30 days (configurable via
BACKUP_RETENTION_DAYS) - Format: PostgreSQL custom dump (
.dump) - Restore:
pg_restore -d lumina_legal backup_YYYYMMDD_HHMMSS.dump
The CI pipeline (.github/workflows/ci.yml) runs on every push/PR to main:
- Backend:
go vet,go build,go test -race - Frontend:
npm run lint,tsc --noEmit,npm run build - Docker: builds both images
- Security:
npm auditandgovulncheck
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/auth/login |
No | Login (rate-limited) |
| POST | /api/auth/logout |
Yes | Logout (revokes token) |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/clients?page=1&limit=50 |
Yes | List with pagination |
| POST | /api/clients |
Yes | Create client |
| PATCH | /api/clients/:id |
Yes | Update client |
| DELETE | /api/clients/:id |
Yes | Delete client |
| POST | /api/clients/conflict-check |
Yes | Check for conflicts |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/matters?page=1&limit=50 |
Yes | List (scoped by user role) |
| POST | /api/matters |
Yes | Create matter |
| GET | /api/matters/:id |
Yes | Get matter details |
| PATCH | /api/matters/:id |
Yes | Update matter |
| DELETE | /api/matters/:id |
Yes | Delete matter |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/invoices?page=1&limit=50 |
Yes | List with pagination |
| POST | /api/invoices |
Yes | Create invoice |
| PATCH | /api/invoices/:id |
Yes | Update invoice |
| DELETE | /api/invoices/:id |
Yes | Delete invoice |
| GET | /api/invoices/export |
Yes | Export as CSV |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/time-entries?page=1&limit=50 |
Yes | List with pagination |
| POST | /api/time-entries |
Yes | Create time entry |
| PATCH | /api/time-entries/:id |
Yes | Update time entry |
| DELETE | /api/time-entries/:id |
Yes | Delete time entry |
| GET | /api/time-entries/export |
Yes | Export as CSV |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/documents?page=1&limit=50 |
Yes | List with pagination |
| POST | /api/documents |
Yes | Create document record |
| POST | /api/documents/upload |
Yes | Upload file (50MB max) |
| DELETE | /api/documents/:id |
Yes | Delete document |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/admin/users?page=1&limit=50 |
Admin | List users |
| POST | /api/admin/users |
Admin | Create user |
| PATCH | /api/admin/users/:id |
Admin | Update user |
| DELETE | /api/admin/users/:id |
Admin | Delete user |
| POST | /api/admin/users/:id/unlock |
Admin | Unlock locked account |
| POST | /api/admin/users/:id/reset-password |
Admin | Reset user password |
| GET | /api/admin/audit-logs?page=1&limit=50&action=LOGIN&resource=User |
Admin | Audit logs (filterable) |
| GET | /api/admin/system-stats |
Admin | System statistics |




