8.9 KiB
Deployment Guide — boha-app-ts (Ubuntu Server)
Migration from PHP boha-app to TypeScript boha-app-ts on the same Ubuntu server. Both apps share the same MySQL database. The PHP app stays running during migration.
Prerequisites
- Ubuntu server with the PHP boha-app already running
- nginx with SSL (Let's Encrypt) already configured
- MySQL database already running with production data
- NAS storage mounted (e.g.,
/mnt/nas/02_PROJEKTY) - SSH access to the server
Phase 1: Prepare on Dev Machine (Windows)
1.1 Create Prisma migration baseline
cd D:\cortex\boha-app-ts
mkdir -p prisma/migrations/0_init
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
npx prisma migrate resolve --applied 0_init
git add prisma/migrations/
git commit -m "chore: create Prisma migration baseline"
1.2 Build the application
npm run build
This creates:
dist/— compiled server (Node.js)dist-client/— compiled frontend (static files)
1.3 Generate new production secrets
openssl rand -hex 32
# Save this as JWT_SECRET
openssl rand -hex 32
# Save this as TOTP_ENCRYPTION_KEY (only if you want a new key)
1.4 Test the production build locally (optional)
APP_ENV=production JWT_SECRET=<your-dev-key> TOTP_ENCRYPTION_KEY=<your-dev-key> DATABASE_URL=<your-dev-db> node dist/server.js
Verify it starts and responds at http://localhost:3001/api/health.
Phase 2: Prepare Ubuntu Server
2.1 Install Node.js 22
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v
npm -v
2.2 Install PM2
sudo npm install -g pm2
2.3 Create application directory
sudo mkdir -p /var/www/boha-app-ts
sudo chown $USER:$USER /var/www/boha-app-ts
Phase 3: Transfer Files to Server
3.1 Copy built files
From your Windows machine (Git Bash or PowerShell with SCP):
scp -r dist/ dist-client/ package.json package-lock.json prisma/ scripts/ .env.example user@server:/var/www/boha-app-ts/
Or use rsync if available:
rsync -avz --exclude node_modules --exclude .env dist/ dist-client/ package.json package-lock.json prisma/ scripts/ .env.example user@server:/var/www/boha-app-ts/
3.2 Install production dependencies on server
ssh user@server
cd /var/www/boha-app-ts
npm install --production
npx prisma generate
Phase 4: Configure Environment
4.1 Create production .env
cd /var/www/boha-app-ts
cp .env.example .env
nano .env
Fill in:
# Database — same as the PHP app
DATABASE_URL=mysql://user:password@localhost:3306/your_db_name
# Server
PORT=3001
HOST=127.0.0.1
APP_ENV=production
# Auth — use the NEW secrets generated in step 1.3
JWT_SECRET=<paste-new-jwt-secret>
ACCESS_TOKEN_EXPIRY=900
REFRESH_TOKEN_SESSION_EXPIRY=3600
REFRESH_TOKEN_REMEMBER_EXPIRY=2592000
# TOTP — use SAME key as PHP app (unless you want to re-encrypt)
TOTP_ENCRYPTION_KEY=<same-key-as-php-app>
# NAS — Linux mount point
NAS_PATH=/mnt/nas/02_PROJEKTY
MAX_UPLOAD_SIZE=52428800
# Email
CONTACT_EMAIL_TO=manager@boha-automation.cz
CONTACT_EMAIL_FROM=web@boha-automation.cz
SMTP_FROM=noreply@boha-automation.cz
# CORS — your production domain(s)
CORS_ORIGINS=https://app.boha-automation.cz,https://www.boha-automation.cz
Important decisions:
| Setting | Recommendation |
|---|---|
JWT_SECRET |
New key. All PHP sessions will be invalid — users re-login. This is expected. |
TOTP_ENCRYPTION_KEY |
Same key as PHP app. Avoids re-encrypting all TOTP secrets. |
DATABASE_URL |
Same database as PHP app. Both apps share it. |
NAS_PATH |
Linux mount point instead of Windows drive letter. |
Phase 5: Database Setup
5.1 Mark Prisma baseline as applied
cd /var/www/boha-app-ts
npx prisma migrate resolve --applied 0_init
This tells Prisma the database already has all tables. No SQL is executed.
5.2 Verify database connection
npx prisma db pull --print | head -20
Should show your existing tables.
Phase 6: TOTP Key Rotation (only if using new encryption key)
Skip this section if you're using the same TOTP_ENCRYPTION_KEY as the PHP app.
If you generated a new encryption key:
cd /var/www/boha-app-ts
# Dry run — verify all secrets can be decrypted and re-encrypted
npx tsx scripts/rotate-totp-key.ts <old-key> <new-key> --dry-run
# If all [OK], run for real
npx tsx scripts/rotate-totp-key.ts <old-key> <new-key>
After this, the PHP app's TOTP verification will break (secrets are now encrypted with the new key). Only do this when you're ready to cut over.
Phase 7: Start Application with PM2
7.1 Create PM2 config
cd /var/www/boha-app-ts
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'boha-app-ts',
script: 'dist/server.js',
cwd: '/var/www/boha-app-ts',
instances: 1,
env: {
NODE_ENV: 'production',
},
}]
};
EOF
7.2 Start the app
pm2 start ecosystem.config.js
pm2 save
pm2 startup
# Follow the printed command to enable auto-start on boot
7.3 Verify
pm2 status
pm2 logs boha-app-ts --lines 20
curl http://localhost:3001/api/health
Expected: {"status":"ok","timestamp":"..."}
Phase 8: Nginx Configuration
8.1 Create nginx config
sudo nano /etc/nginx/sites-available/boha-app-ts
Paste:
server {
listen 443 ssl http2;
server_name app.boha-automation.cz;
ssl_certificate /etc/letsencrypt/live/app.boha-automation.cz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.boha-automation.cz/privkey.pem;
client_max_body_size 55M;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 80;
server_name app.boha-automation.cz;
return 301 https://$host$request_uri;
}
Adjust server_name and SSL paths to match your setup.
8.2 Enable and reload
sudo ln -s /etc/nginx/sites-available/boha-app-ts /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Phase 9: Testing
9.1 Verify in browser
Open https://app.boha-automation.cz and test:
- Login page loads
- Login works (users will need to re-login due to new JWT_SECRET)
- TOTP verification works (if using same encryption key)
- Dashboard loads with data
- Offers — list, create, edit, PDF export
- Orders — list, create from offer, status transitions
- Invoices — list, create, PDF with QR code
- Projects — list, create, file manager (upload/download)
- Attendance — clock in/out, admin view, print
- Trips — list, history
- Settings — company settings, users, roles
9.2 Check logs for errors
pm2 logs boha-app-ts --lines 50
Phase 10: Cutover Strategy
Both apps run simultaneously on the same database. Recommended approach:
- Week 1: Run both apps. Use the TS app for daily work. Fall back to PHP if issues arise.
- Week 2: If stable, redirect the main domain to the TS app.
- Week 3: Stop the PHP app.
To redirect the PHP domain to the TS app, update the nginx config for the PHP domain to proxy to port 3001 instead.
Future Updates
When you push code changes:
# On dev machine
npm run build
git push
# On server
cd /var/www/boha-app-ts
git pull
npm install --production
npx prisma generate
npx prisma migrate deploy # runs any new migrations
pm2 restart boha-app-ts
Troubleshooting
| Problem | Solution |
|---|---|
EADDRINUSE: port 3001 |
pm2 stop boha-app-ts then pm2 start |
| Prisma connection error | Check DATABASE_URL in .env |
| TOTP verification fails | Verify TOTP_ENCRYPTION_KEY matches the key used to encrypt secrets |
| NAS files not accessible | Check mount: ls /mnt/nas/02_PROJEKTY, verify permissions |
| 502 Bad Gateway | App not running: pm2 status, check logs: pm2 logs |
| CSS/JS not loading | Verify dist-client/ was copied, check APP_ENV=production |
| CORS errors | Check CORS_ORIGINS in .env matches your domain exactly |
Quick Reference
| Command | Purpose |
|---|---|
pm2 start ecosystem.config.js |
Start the app |
pm2 restart boha-app-ts |
Restart after update |
pm2 stop boha-app-ts |
Stop the app |
pm2 logs boha-app-ts |
View logs |
pm2 monit |
Live monitoring |
npx prisma migrate deploy |
Apply database migrations |
npx prisma studio |
Database GUI (dev only) |