Each office sets VITE_CLOUDFLARE_HOST and CLOUDFLARE_HOST in their local .env files instead of hardcoding the subdomain in source code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
git clone <your-repo-url>
cd DentalManagementMHAprilgg
Step 2 — Install Node.js
Required to run the Backend and Frontend.
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.
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.
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-managerpackage (included inrequirements.txt) automatically downloads the matching ChromeDriver — no manual driver setup needed.
Step 5 — Install PostgreSQL
Primary database for the application.
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:
sudo sed -i 's/scram-sha-256/md5/g' /etc/postgresql/*/main/pg_hba.conf
sudo systemctl restart postgresql
Verify
psql -U postgres -d dentalapp -h 127.0.0.1 -W
# Enter: mypassword
# You should see: dentalapp=#
The
DATABASE_URLinpackages/db/.envis 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.
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
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.
npm run setup:env
Step 10 — Set up the database
# 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:
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 includeproxy_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:
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:
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:
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 — the first step, to run this app in environment.
- Development Hosts & Ports — which app runs on which host/port, and how to configure
.envfor LAN or single-device access
This is a Turborepo. What's inside?
Apps and Packages
apps/Backend— Express.js API serverapps/Frontend— React + Vite frontendapps/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— sharedtsconfig.jsons
Each package/app is 100% TypeScript (except the Python services).
Utilities
- Tailwind CSS for styles
- TypeScript for static type checking
- ESLint for code linting
- Prettier 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.
- Go to
dash.cloudflare.com→ Add a site → entermydentalofficemanagement.com(free plan) - Cloudflare scans existing DNS records — review and keep them
- Cloudflare gives you 2 nameservers (e.g.
holly.ns.cloudflare.com,amir.ns.cloudflare.com) - Log into Ionos → replace the domain's nameservers with Cloudflare's two
- Wait 10–30 min → Cloudflare emails you when active
- 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:
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
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:
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
sudo mkdir -p /etc/cloudflared
sudo nano /etc/cloudflared/config.yml
Paste (replace tunnel ID, credentials path, and subdomain for this office):
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
cloudflared tunnel route dns <office-tunnel-name> <subdomain>.mydentalofficemanagement.com
Step 7 — Install as a system service (auto-starts on boot)
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:
cloudflared tunnel create summit-dental-app
# Example output: Created tunnel summit-dental-app with id a1b2c3d4-...
Step 5 — /etc/cloudflared/config.yml:
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:
cloudflared tunnel route dns summit-dental-app summitdentalcare.mydentalofficemanagement.com
Step 7:
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 |