diff --git a/local-transcription.spec b/local-transcription.spec index dfca379..fd5da2e 100644 --- a/local-transcription.spec +++ b/local-transcription.spec @@ -137,7 +137,7 @@ exe = EXE( bootloader_ignore_signals=False, strip=False, upx=True, - console=True, # Set to False to hide console window + console=False, # Hide console window for GUI application disable_windowed_traceback=False, argv_emulation=False, target_arch=None, diff --git a/server/nodejs/DEPLOY.md b/server/nodejs/DEPLOY.md new file mode 100644 index 0000000..8aa78c1 --- /dev/null +++ b/server/nodejs/DEPLOY.md @@ -0,0 +1,477 @@ +# Deploying Node.js Transcription Server + +## Quick Deploy with PM2 + +### Step 1: Upload Files to Server + +```bash +# From your local machine: +rsync -avz --exclude 'node_modules' --exclude 'data' \ + server/nodejs/ shadowdao@your-server:/home/shadowdao/local-transcription/server/nodejs/ +``` + +Or manually upload: +- `server.js` +- `package.json` +- `ecosystem.config.js` + +### Step 2: Install Dependencies + +```bash +# SSH into your server +ssh shadowdao@your-server + +# Navigate to directory +cd /home/shadowdao/local-transcription/server/nodejs + +# Install dependencies +npm install --production +``` + +### Step 3: Create Log Directory + +```bash +# Create log directory if it doesn't exist +mkdir -p /home/shadowdao/logs/transcription +chmod 755 /home/shadowdao/logs/transcription +``` + +### Step 4: Create Data Directory + +```bash +# Create data directory for room storage +mkdir -p /home/shadowdao/local-transcription/server/nodejs/data +chmod 755 /home/shadowdao/local-transcription/server/nodejs/data +``` + +### Step 5: Update ecosystem.config.js Paths + +Edit `ecosystem.config.js` and update these paths: + +```javascript +cwd: '/home/shadowdao/local-transcription/server/nodejs', // ← Your actual path +DATA_DIR: '/home/shadowdao/local-transcription/server/nodejs/data', +log_file: '/home/shadowdao/logs/transcription/app.log', // ← Your log path +``` + +### Step 6: Start with PM2 + +```bash +# Start the server +pm2 start ecosystem.config.js --env production + +# Save PM2 configuration +pm2 save + +# Set up PM2 to start on boot (first time only) +pm2 startup +# Follow the instructions it prints + +# Check status +pm2 status +``` + +--- + +## PM2 Management Commands + +### Check Status +```bash +pm2 status # List all apps +pm2 show transcription-server # Detailed info +``` + +### View Logs +```bash +pm2 logs transcription-server # Live logs +pm2 logs transcription-server --lines 100 # Last 100 lines +pm2 logs transcription-server --err # Errors only +``` + +### Restart/Stop +```bash +pm2 restart transcription-server +pm2 stop transcription-server +pm2 delete transcription-server # Remove from PM2 +``` + +### Monitor +```bash +pm2 monit # Real-time monitoring (CPU, memory, logs) +``` + +### Update Code +```bash +# After uploading new code: +pm2 restart transcription-server +``` + +--- + +## Configuration Options + +### Change Port + +Edit `ecosystem.config.js`: +```javascript +env_production: { + PORT: 3001, // ← Change port here +} +``` + +Then restart: +```bash +pm2 restart transcription-server +``` + +### Increase Memory Limit + +If you get "JavaScript heap out of memory" errors: + +```javascript +max_memory_restart: '1G', // ← Increase from 512M +env_production: { + NODE_OPTIONS: '--max-old-space-size=800' // ← Increase from 400 +} +``` + +### Enable File Watching (Development) + +```javascript +watch: true, +ignore_watch: ['node_modules', 'data', 'logs'], +``` + +**Warning:** Don't use in production (unnecessary restarts) + +--- + +## Nginx Reverse Proxy (Optional) + +To serve on port 80/443 with SSL: + +### Create Nginx Config + +```bash +sudo nano /etc/nginx/sites-available/transcription +``` + +Add: +```nginx +server { + listen 80; + server_name transcription.yourdomain.com; + + location / { + proxy_pass http://127.0.0.1:3000; + 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; + + # WebSocket support + proxy_read_timeout 86400; + } +} +``` + +Enable: +```bash +sudo ln -s /etc/nginx/sites-available/transcription /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +### Add SSL with Let's Encrypt + +```bash +sudo apt install certbot python3-certbot-nginx +sudo certbot --nginx -d transcription.yourdomain.com +``` + +--- + +## Firewall Configuration + +### Allow Port 3000 + +```bash +# UFW +sudo ufw allow 3000/tcp + +# iptables +sudo iptables -A INPUT -p tcp --dport 3000 -j ACCEPT +sudo iptables-save +``` + +### Or Use Nginx (Port 80/443) + +```bash +sudo ufw allow 'Nginx Full' +``` + +--- + +## Environment Variables + +You can add environment variables in `ecosystem.config.js`: + +```javascript +env_production: { + NODE_ENV: 'production', + PORT: 3000, + + // Custom variables + MAX_ROOMS: 100, + ROOM_CLEANUP_INTERVAL: 7200000, // 2 hours in ms + MAX_CLIENTS_PER_ROOM: 50, + + // Data storage + DATA_DIR: '/home/shadowdao/transcription-data', +} +``` + +Access in `server.js`: +```javascript +const PORT = process.env.PORT || 3000; +const MAX_ROOMS = process.env.MAX_ROOMS || 100; +``` + +--- + +## Monitoring & Maintenance + +### Check Server is Running + +```bash +curl http://localhost:3000 +# Should see HTML response + +curl http://localhost:3000/api/list?room=test +# Should see: {"transcriptions":[]} +``` + +### Check WebSocket + +```bash +# Install wscat +npm install -g wscat + +# Test WebSocket connection +wscat -c ws://localhost:3000/ws?room=test +``` + +### Monitor Resources + +```bash +pm2 monit # PM2 built-in monitor + +# Or use htop +htop # Look for "node" process +``` + +### Log Rotation + +PM2 handles log rotation automatically, but you can configure it: + +```bash +pm2 install pm2-logrotate + +pm2 set pm2-logrotate:max_size 10M +pm2 set pm2-logrotate:retain 7 +pm2 set pm2-logrotate:compress true +``` + +--- + +## Troubleshooting + +### Server Won't Start + +```bash +# Check logs +pm2 logs transcription-server --err + +# Common issues: +# 1. Port already in use +sudo lsof -i :3000 +# Kill the process or change port + +# 2. Missing dependencies +cd /home/shadowdao/local-transcription/server/nodejs +npm install + +# 3. Permission issues +chmod 755 server.js +``` + +### WebSocket Not Working + +```bash +# Check if server is listening +netstat -tlnp | grep 3000 + +# Check Nginx config if using reverse proxy +sudo nginx -t + +# Check firewall +sudo ufw status +``` + +### Out of Memory + +```bash +# Check memory usage +pm2 show transcription-server + +# Increase in ecosystem.config.js: +max_memory_restart: '1G', +NODE_OPTIONS: '--max-old-space-size=800' + +# Restart +pm2 restart transcription-server +``` + +### Too Many Rooms (Disk Full) + +```bash +# Check data directory size +du -sh /home/shadowdao/local-transcription/server/nodejs/data + +# Clean old rooms manually +cd /home/shadowdao/local-transcription/server/nodejs/data +find . -name "room_*.json" -mtime +1 -delete # Delete files older than 1 day +``` + +--- + +## Production Checklist + +- [ ] Dependencies installed (`npm install --production`) +- [ ] Log directory created and writable +- [ ] Data directory created and writable +- [ ] Paths updated in `ecosystem.config.js` +- [ ] PM2 started (`pm2 start ecosystem.config.js --env production`) +- [ ] PM2 saved (`pm2 save`) +- [ ] PM2 startup configured (`pm2 startup`) +- [ ] Firewall configured (port 3000 or Nginx) +- [ ] Nginx reverse proxy configured (optional) +- [ ] SSL certificate installed (optional) +- [ ] Test with `curl http://your-server:3000` +- [ ] Test WebSocket with browser display page +- [ ] Monitor for 24 hours to ensure stability + +--- + +## Backup & Restore + +### Backup Room Data + +```bash +# Backup data directory +tar -czf transcription-backup-$(date +%Y%m%d).tar.gz \ + /home/shadowdao/local-transcription/server/nodejs/data + +# Move to backup location +mv transcription-backup-*.tar.gz /home/shadowdao/backups/ +``` + +### Automated Backup (Cron) + +```bash +crontab -e +``` + +Add: +```cron +# Backup transcription data daily at 2 AM +0 2 * * * tar -czf /home/shadowdao/backups/transcription-$(date +\%Y\%m\%d).tar.gz /home/shadowdao/local-transcription/server/nodejs/data + +# Delete backups older than 7 days +0 3 * * * find /home/shadowdao/backups/transcription-*.tar.gz -mtime +7 -delete +``` + +--- + +## Performance Tuning + +### For High Traffic (100+ concurrent users) + +```javascript +// In ecosystem.config.js: +max_memory_restart: '2G', +env_production: { + NODE_OPTIONS: '--max-old-space-size=1600' +} +``` + +### Cluster Mode (Multiple CPU Cores) + +```javascript +exec_mode: 'cluster', +instances: 2, // Use 2 CPU cores +``` + +**Warning:** Cluster mode requires sticky sessions for WebSockets. Use single instance unless you implement Redis for session sharing. + +--- + +## Getting Help + +If issues persist: + +1. Check logs: `pm2 logs transcription-server` +2. Check server status: `pm2 show transcription-server` +3. Test manually: `node server.js` (see direct output) +4. Check Node.js version: `node --version` (needs 14+) +5. Check port availability: `sudo lsof -i :3000` + +--- + +## Example Deploy Script + +Create `deploy.sh`: + +```bash +#!/bin/bash +# Deploy transcription server + +SERVER="shadowdao@your-server" +REMOTE_PATH="/home/shadowdao/local-transcription/server/nodejs" + +echo "Uploading files..." +rsync -avz --exclude 'node_modules' --exclude 'data' \ + ./ $SERVER:$REMOTE_PATH/ + +echo "Installing dependencies..." +ssh $SERVER "cd $REMOTE_PATH && npm install --production" + +echo "Restarting server..." +ssh $SERVER "pm2 restart transcription-server" + +echo "Checking status..." +ssh $SERVER "pm2 status transcription-server" + +echo "Deployment complete!" +``` + +Make executable: +```bash +chmod +x deploy.sh +``` + +Use: +```bash +./deploy.sh +``` + +--- + +**Server should now be accessible at:** +- Homepage: `http://your-server:3000` +- API: `http://your-server:3000/api/send` +- Display: `http://your-server:3000/display?room=test` diff --git a/server/nodejs/QUICK_DEPLOY.md b/server/nodejs/QUICK_DEPLOY.md new file mode 100644 index 0000000..926d2a7 --- /dev/null +++ b/server/nodejs/QUICK_DEPLOY.md @@ -0,0 +1,92 @@ +# Quick Deploy Guide + +## TL;DR - Copy & Paste These Commands + +```bash +# 1. Upload files to server +cd /home/jknapp/code/local-transcription +rsync -avz --exclude 'node_modules' --exclude 'data' \ + server/nodejs/ shadowdao@YOUR_SERVER:/home/shadowdao/local-transcription/server/nodejs/ + +# 2. SSH into server +ssh shadowdao@YOUR_SERVER + +# 3. Install dependencies +cd /home/shadowdao/local-transcription/server/nodejs +npm install --production + +# 4. Create directories +mkdir -p /home/shadowdao/logs/transcription +mkdir -p data + +# 5. Start with PM2 +pm2 start ecosystem.config.js --env production +pm2 save +pm2 startup # Run the command it prints + +# 6. Check status +pm2 status +pm2 logs transcription-server +``` + +## Test It Works + +```bash +# Test HTTP +curl http://localhost:3000 + +# Test API +curl http://localhost:3000/api/list?room=test + +# View in browser +# http://YOUR_SERVER_IP:3000 +``` + +## PM2 Cheat Sheet + +```bash +pm2 status # Check status +pm2 logs transcription-server # View logs +pm2 restart transcription-server # Restart +pm2 stop transcription-server # Stop +pm2 monit # Monitor resources +``` + +## Firewall (if needed) + +```bash +sudo ufw allow 3000/tcp +``` + +## Update After Code Changes + +```bash +# On your local machine: +rsync -avz --exclude 'node_modules' --exclude 'data' \ + server/nodejs/ shadowdao@YOUR_SERVER:/home/shadowdao/local-transcription/server/nodejs/ + +# On server: +ssh shadowdao@YOUR_SERVER +pm2 restart transcription-server +``` + +## Troubleshooting + +**Server won't start?** +```bash +pm2 logs transcription-server --err +``` + +**Port already in use?** +```bash +sudo lsof -i :3000 +# Kill process or change port in ecosystem.config.js +``` + +**Out of memory?** +Edit `ecosystem.config.js`: +```javascript +max_memory_restart: '1G', // Increase from 512M +``` + +See [DEPLOY.md](DEPLOY.md) for complete guide. diff --git a/server/nodejs/ecosystem.config.js b/server/nodejs/ecosystem.config.js new file mode 100644 index 0000000..e82b780 --- /dev/null +++ b/server/nodejs/ecosystem.config.js @@ -0,0 +1,37 @@ +module.exports = { + apps: [{ + name: 'transcription-server', + script: 'server.js', + cwd: '/home/shadowdao/local-transcription/server/nodejs', // ← Update this path + exec_mode: 'fork', + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: '512M', // ← Increased (transcription data in memory) + kill_timeout: 5000, // ← Give time for WebSocket cleanup + wait_ready: false, + listen_timeout: 5000, + env: { + NODE_ENV: 'development', + PORT: 3000, + DATA_DIR: '/home/shadowdao/local-transcription/server/nodejs/data', // ← Data directory + NODE_OPTIONS: '--max-old-space-size=400' // ← Increased memory + }, + env_production: { + NODE_ENV: 'production', + PORT: 3000, + DATA_DIR: '/home/shadowdao/local-transcription/server/nodejs/data', + NODE_OPTIONS: '--max-old-space-size=400' + }, + log_file: '/home/shadowdao/logs/transcription/app.log', + error_file: '/home/shadowdao/logs/transcription/error.log', + out_file: '/home/shadowdao/logs/transcription/out.log', + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + log_type: 'json', + merge_logs: true, + max_restarts: 10, // ← More retries (WebSocket can be finicky) + min_uptime: '10s', + // Graceful shutdown for WebSocket connections + shutdown_with_message: true + }] +}; diff --git a/server/nodejs/server.js b/server/nodejs/server.js index 5ad76de..fb4463d 100644 --- a/server/nodejs/server.js +++ b/server/nodejs/server.js @@ -687,9 +687,11 @@ app.get('/display', (req, res) => { background: rgba(0, 0, 0, 0.8); border-radius: 5px; font-size: 0.9em; + transition: opacity 2s ease-out; } #status.connected { color: #4CAF50; } #status.disconnected { color: #f44336; } + #status.hidden { opacity: 0; pointer-events: none; } @keyframes slideIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } @@ -761,6 +763,8 @@ app.get('/display', (req, res) => { } } + let statusHideTimeout = null; + function connect() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const ws = new WebSocket(\`\${protocol}//\${window.location.host}/ws?room=\${encodeURIComponent(room)}\`); @@ -768,6 +772,16 @@ app.get('/display', (req, res) => { ws.onopen = () => { statusEl.textContent = '🟢 Connected'; statusEl.className = 'connected'; + + // Clear any existing timeout + if (statusHideTimeout) { + clearTimeout(statusHideTimeout); + } + + // Fade out after 20 seconds + statusHideTimeout = setTimeout(() => { + statusEl.classList.add('hidden'); + }, 20000); }; ws.onmessage = (event) => { @@ -780,6 +794,12 @@ app.get('/display', (req, res) => { }; ws.onclose = () => { + // Clear hide timeout on disconnect + if (statusHideTimeout) { + clearTimeout(statusHideTimeout); + statusHideTimeout = null; + } + statusEl.textContent = '🔴 Disconnected'; statusEl.className = 'disconnected'; setTimeout(connect, 3000);