387 lines
11 KiB
Markdown
387 lines
11 KiB
Markdown
# Dental Manager - Starter
|
||
|
||
A monorepo setup to manage both Backend and Frontend of the Dental Manager application.
|
||
|
||
## 🖥️ Setup Guide (Fresh Machine)
|
||
|
||
Follow these steps in order after cloning the repository.
|
||
|
||
### Step 1 — Clone the repository
|
||
|
||
```sh
|
||
git clone <your-repo-url>
|
||
cd DentalManagementMHAprilgg
|
||
```
|
||
|
||
### Step 2 — Install Node.js
|
||
|
||
Required to run the Backend and Frontend.
|
||
|
||
```sh
|
||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||
sudo apt-get install -y nodejs
|
||
|
||
# Verify
|
||
node -v # should print v20.x.x
|
||
npm -v
|
||
```
|
||
|
||
### Step 3 — Install Python
|
||
|
||
Required to run the Selenium and OCR services.
|
||
|
||
```sh
|
||
sudo apt-get install -y python3 python3-pip python3-venv
|
||
|
||
# Verify
|
||
python3 --version # should print 3.10 or higher
|
||
```
|
||
|
||
### Step 4 — Install Chrome
|
||
|
||
Required for the Selenium service to control a browser.
|
||
|
||
```sh
|
||
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
|
||
sudo apt-get update
|
||
sudo apt-get install -y google-chrome-stable
|
||
|
||
# Verify
|
||
google-chrome --version
|
||
```
|
||
|
||
> The `webdriver-manager` package (included in `requirements.txt`) automatically downloads the matching ChromeDriver — no manual driver setup needed.
|
||
|
||
### Step 5 — Install PostgreSQL
|
||
|
||
Primary database for the application.
|
||
|
||
```sh
|
||
sudo apt-get install -y postgresql postgresql-contrib
|
||
sudo systemctl enable postgresql
|
||
sudo systemctl start postgresql
|
||
|
||
# Create the database the app uses
|
||
sudo -u postgres psql -c "CREATE DATABASE dentalapp OWNER postgres;"
|
||
|
||
# Set the postgres user password to match packages/db/.env
|
||
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD 'mypassword';"
|
||
```
|
||
|
||
#### Enable password authentication over TCP
|
||
|
||
By default Debian uses `scram-sha-256` or `peer` auth for local connections, which blocks password login. Switch to `md5`:
|
||
|
||
```sh
|
||
sudo sed -i 's/scram-sha-256/md5/g' /etc/postgresql/*/main/pg_hba.conf
|
||
sudo systemctl restart postgresql
|
||
```
|
||
|
||
#### Verify
|
||
|
||
```sh
|
||
psql -U postgres -d dentalapp -h 127.0.0.1 -W
|
||
# Enter: mypassword
|
||
# You should see: dentalapp=#
|
||
```
|
||
|
||
> The `DATABASE_URL` in `packages/db/.env` is already set to:
|
||
> ```
|
||
> DATABASE_URL="postgresql://postgres:mypassword@localhost:5432/dentalapp"
|
||
> ```
|
||
|
||
### Step 6 — Install Redis
|
||
|
||
Used as the job queue for Selenium and OCR background tasks.
|
||
|
||
```sh
|
||
sudo apt-get install -y redis-server
|
||
sudo systemctl enable redis-server
|
||
sudo systemctl start redis-server
|
||
|
||
# Verify
|
||
redis-cli ping # should print: PONG
|
||
```
|
||
|
||
### Step 7 — Install Node.js dependencies
|
||
|
||
```sh
|
||
npm install
|
||
```
|
||
|
||
### Step 8 — Install Python dependencies
|
||
|
||
Python dependencies are installed automatically by `npm install` (Step 7) via each service's `postinstall` script. Each service creates its own `.venv` virtual environment — no manual pip commands needed.
|
||
|
||
> This approach is required on Debian 13+ where system-wide pip installs are blocked (PEP 668).
|
||
|
||
### Step 9 — Set up environment variables
|
||
|
||
Copy the `.env.example` files and fill in the required values.
|
||
|
||
```sh
|
||
npm run setup:env
|
||
```
|
||
|
||
### Step 9a — Set the Cloudflare subdomain for this office
|
||
|
||
After running `npm run setup:env`, open the two `.env` files and fill in this office's Cloudflare subdomain.
|
||
|
||
**`apps/Frontend/.env`**
|
||
```env
|
||
VITE_CLOUDFLARE_HOST=yoursubdomain.mydentalofficemanagement.com
|
||
```
|
||
|
||
**`apps/Backend/.env`**
|
||
```env
|
||
CLOUDFLARE_HOST=yoursubdomain.mydentalofficemanagement.com
|
||
FRONTEND_URLS=http://localhost:3000,http://yoursubdomain.mydentalofficemanagement.com,https://yoursubdomain.mydentalofficemanagement.com
|
||
```
|
||
|
||
Replace `yoursubdomain` with this office's actual subdomain (e.g. `summitdentalcare`).
|
||
|
||
> If you skip this step, Cloudflare tunnel access will not work. LAN access still works without it.
|
||
|
||
### Step 10 — Set up the database
|
||
|
||
```sh
|
||
# Run migrations
|
||
npm run db:migrate
|
||
|
||
# Generate Prisma types
|
||
npm run db:generate
|
||
|
||
# Insert seed data
|
||
npm run db:seed
|
||
```
|
||
|
||
### Step 11 — Configure nginx
|
||
|
||
The repo includes `nginx.conf` in the project root. Install it as the active site config:
|
||
|
||
```sh
|
||
sudo cp nginx.conf /etc/nginx/sites-available/dental-app
|
||
sudo ln -sf /etc/nginx/sites-available/dental-app /etc/nginx/sites-enabled/dental-app
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
```
|
||
|
||
> **Important:** The `/api/` location block must include `proxy_set_header Authorization $http_authorization;`
|
||
> Without it, nginx strips the Authorization header and the backend returns "Access denied. No token provided."
|
||
|
||
### Step 12 — Run the app
|
||
|
||
Open two terminals:
|
||
|
||
**Terminal 1** — Backend + Frontend:
|
||
```sh
|
||
npm run dev
|
||
```
|
||
|
||
> On first boot the server automatically seeds all AI chat templates, SMS templates, and greeting messages for every user — no manual configuration needed.
|
||
|
||
**Terminal 2** — Selenium service:
|
||
```sh
|
||
cd apps/SeleniumService
|
||
.venv/bin/python3 agent.py
|
||
```
|
||
|
||
### Step 13 — Create desktop shortcut (optional)
|
||
|
||
Instead of opening two terminals manually every time, you can create a desktop shortcut that starts both services with a single double-click.
|
||
|
||
Run this once after cloning and installing:
|
||
|
||
```sh
|
||
bash ~/Desktop/DentalManagementMH06/setup-desktop-shortcut.sh
|
||
```
|
||
|
||
A **Dental App** shortcut will appear on your desktop. Double-clicking it opens:
|
||
- A terminal running `npm run dev` (Backend + Frontend)
|
||
- A terminal running the Selenium service
|
||
|
||
> No username or path editing needed — the script automatically detects the current user's home folder.
|
||
|
||
---
|
||
|
||
## 📖 Developer Documentation
|
||
|
||
- [Setting up server environment](docs/server-setup.md) — the first step, to run this app in environment.
|
||
- [Development Hosts & Ports](docs/ports.md) — which app runs on which host/port, and how to configure `.env` for LAN or single-device access
|
||
|
||
---
|
||
|
||
## This is a Turborepo. What's inside?
|
||
|
||
### Apps and Packages
|
||
|
||
- `apps/Backend` — Express.js API server
|
||
- `apps/Frontend` — React + Vite frontend
|
||
- `apps/SeleniumService` — Python FastAPI service for browser automation (insurance eligibility, claims)
|
||
- `apps/PaymentOCRService` — Python service for payment OCR extraction
|
||
- `@repo/eslint-config` — shared ESLint configuration
|
||
- `@repo/typescript-config` — shared `tsconfig.json`s
|
||
|
||
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/) (except the Python services).
|
||
|
||
### Utilities
|
||
|
||
- [Tailwind CSS](https://tailwindcss.com/) for styles
|
||
- [TypeScript](https://www.typescriptlang.org/) for static type checking
|
||
- [ESLint](https://eslint.org/) for code linting
|
||
- [Prettier](https://prettier.io) for code formatting
|
||
|
||
---
|
||
|
||
## Cloudflare Tunnel Setup (Remote Access per Office)
|
||
|
||
This connects each office's local app to a public subdomain via a Cloudflare Tunnel — no port forwarding needed, local network access is unchanged.
|
||
|
||
**How it works:**
|
||
- Local network: other office PCs reach the app directly at `http://<machine-ip>:3000`
|
||
- Internet: anyone reaches the app via `https://<subdomain>.mydentalofficemanagement.com`
|
||
- Both paths hit the same app simultaneously with no conflict
|
||
|
||
---
|
||
|
||
### Step 1 — Add domain to Cloudflare (done once for all offices)
|
||
|
||
> Skip this step if the domain is already on Cloudflare.
|
||
|
||
1. Go to `dash.cloudflare.com` → **Add a site** → enter `mydentalofficemanagement.com` (free plan)
|
||
2. Cloudflare scans existing DNS records — review and keep them
|
||
3. Cloudflare gives you 2 nameservers (e.g. `holly.ns.cloudflare.com`, `amir.ns.cloudflare.com`)
|
||
4. Log into **Ionos** → replace the domain's nameservers with Cloudflare's two
|
||
5. Wait 10–30 min → Cloudflare emails you when active
|
||
6. DNSSEC: if not purchased on Ionos, it was never enabled — nothing to turn off
|
||
|
||
### Step 2 — Install `cloudflared` on the office PC
|
||
|
||
Run this in a terminal on the office machine:
|
||
|
||
```bash
|
||
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
|
||
sudo dpkg -i cloudflared.deb
|
||
cloudflared --version
|
||
```
|
||
|
||
### Step 3 — Authenticate with Cloudflare
|
||
|
||
```bash
|
||
cloudflared tunnel login
|
||
```
|
||
|
||
A browser window opens → click `mydentalofficemanagement.com` → terminal shows success and saves a certificate to `~/.cloudflared/cert.pem`.
|
||
|
||
### Step 4 — Create a tunnel for this office
|
||
|
||
Use a unique tunnel name per office:
|
||
|
||
```bash
|
||
cloudflared tunnel create <office-tunnel-name>
|
||
# Example: cloudflared tunnel create summit-dental-app
|
||
```
|
||
|
||
Note the **tunnel ID** (UUID) printed — you need it in Step 5.
|
||
|
||
### Step 5 — Create the config file
|
||
|
||
```bash
|
||
sudo mkdir -p /etc/cloudflared
|
||
sudo nano /etc/cloudflared/config.yml
|
||
```
|
||
|
||
Paste (replace tunnel ID, credentials path, and subdomain for this office):
|
||
|
||
```yaml
|
||
tunnel: <tunnel-ID>
|
||
credentials-file: /home/ee/.cloudflared/<tunnel-ID>.json
|
||
|
||
ingress:
|
||
- hostname: <subdomain>.mydentalofficemanagement.com
|
||
service: http://localhost:3000
|
||
- service: http_status:404
|
||
```
|
||
|
||
Save: `Ctrl+O` → Enter → `Ctrl+X`.
|
||
|
||
### Step 6 — Route DNS for this office's subdomain
|
||
|
||
```bash
|
||
cloudflared tunnel route dns <office-tunnel-name> <subdomain>.mydentalofficemanagement.com
|
||
```
|
||
|
||
### Step 7 — Install as a system service (auto-starts on boot)
|
||
|
||
```bash
|
||
sudo cloudflared service install
|
||
sudo systemctl enable cloudflared
|
||
sudo systemctl start cloudflared
|
||
sudo systemctl status cloudflared
|
||
```
|
||
|
||
### Step 8 — Allow the subdomain in Vite
|
||
|
||
In `apps/Frontend/vite.config.js`, add the subdomain to `server.allowedHosts` so Vite does not block external requests.
|
||
|
||
### Step 9 — Allow the subdomain in backend CORS
|
||
|
||
In `apps/Backend`, add the subdomain URL to the allowed CORS origins so login and API calls work from the public URL.
|
||
|
||
---
|
||
|
||
### Example — Adding Summit Dental Care
|
||
|
||
**Office subdomain:** `summitdentalcare.mydentalofficemanagement.com`
|
||
|
||
**Step 1:** Already done (domain is on Cloudflare).
|
||
|
||
**Step 2:** Install `cloudflared` on Summit Dental's PC.
|
||
|
||
**Step 3:** Run `cloudflared tunnel login` on Summit Dental's PC.
|
||
|
||
**Step 4:**
|
||
```bash
|
||
cloudflared tunnel create summit-dental-app
|
||
# Example output: Created tunnel summit-dental-app with id a1b2c3d4-...
|
||
```
|
||
|
||
**Step 5 — `/etc/cloudflared/config.yml`:**
|
||
```yaml
|
||
tunnel: a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||
credentials-file: /home/ee/.cloudflared/a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json
|
||
|
||
ingress:
|
||
- hostname: summitdentalcare.mydentalofficemanagement.com
|
||
service: http://localhost:3000
|
||
- service: http_status:404
|
||
```
|
||
|
||
**Step 6:**
|
||
```bash
|
||
cloudflared tunnel route dns summit-dental-app summitdentalcare.mydentalofficemanagement.com
|
||
```
|
||
|
||
**Step 7:**
|
||
```bash
|
||
sudo cloudflared service install
|
||
sudo systemctl enable cloudflared
|
||
sudo systemctl start cloudflared
|
||
```
|
||
|
||
**Step 8:** Add `summitdentalcare.mydentalofficemanagement.com` to `allowedHosts` in `vite.config.js`.
|
||
|
||
**Step 9:** Add `https://summitdentalcare.mydentalofficemanagement.com` to backend CORS allowed origins.
|
||
|
||
---
|
||
|
||
### Multi-office overview
|
||
|
||
Each office runs its own `cloudflared` tunnel on its own PC. Ports never conflict because each PC is a separate machine.
|
||
|
||
| Office | Local access | Public access | Tunnel name |
|
||
|---|---|---|---|
|
||
| Community Dentists of Lowell | `http://192.168.1.236:3000` | `https://communitydentistsoflowell.mydentalofficemanagement.com` | `dental-app` |
|
||
| Summit Dental Care | `http://<its-ip>:3000` | `https://summitdentalcare.mydentalofficemanagement.com` | `summit-dental-app` |
|
||
| Next office | `http://<its-ip>:3000` | `https://<subdomain>.mydentalofficemanagement.com` | `<office>-app` |
|