Server deploy script
This commit is contained in:
parent
9c82c677ce
commit
4c2a2f6aca
|
|
@ -0,0 +1,465 @@
|
|||
# Deployment Guide: Farm Auth Service on AWS Lightsail
|
||||
|
||||
This guide will help you deploy the Farm Auth Service on AWS Lightsail Ubuntu server with the domain `auth.livingai.app`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- AWS Lightsail Ubuntu server (at least 1GB RAM recommended)
|
||||
- Domain name `auth.livingai.app` pointing to your Lightsail server IP
|
||||
- Access to AWS Console (for database if using AWS RDS)
|
||||
- SSH access to your Lightsail server
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Set Up AWS Lightsail Instance
|
||||
|
||||
1. **Create Lightsail Instance**
|
||||
- Go to AWS Lightsail Console
|
||||
- Click "Create instance"
|
||||
- Choose:
|
||||
- Platform: Linux/Unix
|
||||
- Blueprint: Ubuntu 22.04 LTS (or latest)
|
||||
- Instance plan: At least $5/month (1GB RAM)
|
||||
- Name: `auth-service-server`
|
||||
- Click "Create instance"
|
||||
|
||||
2. **Configure Static IP**
|
||||
- In Lightsail, go to Networking → Static IPs
|
||||
- Click "Create static IP"
|
||||
- Attach to your instance
|
||||
- **Note the static IP address** - you'll need it for DNS configuration
|
||||
|
||||
3. **Configure Firewall (Security Groups)**
|
||||
- Go to Networking → Firewall
|
||||
- Add rules:
|
||||
- HTTP (port 80) - Allow from Anywhere
|
||||
- HTTPS (port 443) - Allow from Anywhere
|
||||
- SSH (port 22) - Allow from Your IP (for security)
|
||||
- Custom TCP 3000 - Allow from localhost only (for Node.js app)
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Configure DNS
|
||||
|
||||
1. **Point Domain to Lightsail IP**
|
||||
- Go to your domain registrar (where you manage livingai.app)
|
||||
- Add/Update A record:
|
||||
- Type: A
|
||||
- Name: auth (or @ if using subdomain)
|
||||
- Value: Your Lightsail static IP address
|
||||
- TTL: 300 (5 minutes)
|
||||
- Wait for DNS propagation (can take up to 48 hours, usually faster)
|
||||
|
||||
2. **Verify DNS**
|
||||
```bash
|
||||
# On your local machine
|
||||
nslookup auth.livingai.app
|
||||
# Should return your Lightsail IP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Initial Server Setup (SSH into server)
|
||||
|
||||
1. **Connect to Server**
|
||||
```bash
|
||||
# Download SSH key from Lightsail or use existing key
|
||||
ssh -i /path/to/your-key.pem ubuntu@your-lightsail-ip
|
||||
```
|
||||
|
||||
2. **Update System**
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||
3. **Install Node.js (v18 or higher)**
|
||||
```bash
|
||||
# Install Node.js using NodeSource repository
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
|
||||
# Verify installation
|
||||
node --version
|
||||
npm --version
|
||||
```
|
||||
|
||||
4. **Install Nginx**
|
||||
```bash
|
||||
sudo apt install -y nginx
|
||||
sudo systemctl start nginx
|
||||
sudo systemctl enable nginx
|
||||
```
|
||||
|
||||
5. **Install PM2 (Process Manager)**
|
||||
```bash
|
||||
sudo npm install -g pm2
|
||||
```
|
||||
|
||||
6. **Install Git**
|
||||
```bash
|
||||
sudo apt install -y git
|
||||
```
|
||||
|
||||
7. **Install Certbot (for SSL)**
|
||||
```bash
|
||||
sudo apt install -y certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Deploy Application Code
|
||||
|
||||
1. **Clone Repository**
|
||||
```bash
|
||||
# Create application directory
|
||||
cd /home/ubuntu
|
||||
mkdir -p apps
|
||||
cd apps
|
||||
|
||||
# Clone your repository (adjust URL as needed)
|
||||
git clone <your-repository-url> farm-auth-service
|
||||
# OR if you need to upload manually:
|
||||
# scp -r /path/to/farm-auth-service ubuntu@your-ip:/home/ubuntu/apps/
|
||||
```
|
||||
|
||||
2. **Navigate to Project Directory**
|
||||
```bash
|
||||
cd farm-auth-service
|
||||
```
|
||||
|
||||
3. **Install Dependencies**
|
||||
```bash
|
||||
npm install --production
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Configure Environment Variables
|
||||
|
||||
1. **Create .env File**
|
||||
```bash
|
||||
cp example.env .env
|
||||
nano .env
|
||||
```
|
||||
|
||||
2. **Configure .env File** (Update these values)
|
||||
```env
|
||||
# Database Mode
|
||||
DATABASE_MODE=aws
|
||||
|
||||
# AWS Configuration (for SSM Parameter Store)
|
||||
AWS_REGION=ap-south-1
|
||||
AWS_ACCESS_KEY_ID=your_aws_access_key_here
|
||||
AWS_SECRET_ACCESS_KEY=your_aws_secret_key_here
|
||||
DB_USE_READONLY=false
|
||||
|
||||
# Database connection (auto-detected from SSM if not set)
|
||||
# DB_HOST=db.livingai.app
|
||||
# DB_PORT=5432
|
||||
# DB_NAME=livingai_test_db
|
||||
|
||||
# JWT Configuration (IMPORTANT: Generate new secrets for production!)
|
||||
JWT_ACCESS_SECRET=your_strong_random_secret_here_min_32_chars
|
||||
JWT_REFRESH_SECRET=your_strong_random_secret_here_min_32_chars
|
||||
JWT_ACCESS_TTL=15m
|
||||
JWT_REFRESH_TTL=7d
|
||||
|
||||
# Redis Configuration (Optional - for rate limiting)
|
||||
# REDIS_URL=redis://your-redis-host:6379
|
||||
# Or if password protected:
|
||||
# REDIS_URL=redis://:password@your-redis-host:6379
|
||||
|
||||
# Application Configuration
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
TRUST_PROXY=true
|
||||
|
||||
# CORS Configuration (REQUIRED for production)
|
||||
CORS_ALLOWED_ORIGINS=https://your-mobile-app-domain.com,https://app.livingai.app
|
||||
# Add all domains that will access this API
|
||||
|
||||
# Twilio Configuration (if using SMS OTP)
|
||||
# TWILIO_ACCOUNT_SID=your_twilio_account_sid
|
||||
# TWILIO_AUTH_TOKEN=your_twilio_auth_token
|
||||
# TWILIO_PHONE_NUMBER=+1234567890
|
||||
|
||||
# Admin Dashboard (optional)
|
||||
# ENABLE_ADMIN_DASHBOARD=false
|
||||
```
|
||||
|
||||
3. **Generate Strong JWT Secrets** (Do this on your local machine)
|
||||
```bash
|
||||
# Generate random secrets (64 characters each)
|
||||
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
```
|
||||
Copy the output to `JWT_ACCESS_SECRET` and `JWT_REFRESH_SECRET`
|
||||
|
||||
4. **Secure .env File**
|
||||
```bash
|
||||
chmod 600 .env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Configure Nginx Reverse Proxy
|
||||
|
||||
1. **Create Nginx Configuration**
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/auth.livingai.app
|
||||
```
|
||||
|
||||
2. **Add Configuration** (Before SSL)
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name auth.livingai.app;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost: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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Enable Site**
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/auth.livingai.app /etc/nginx/sites-enabled/
|
||||
sudo nginx -t # Test configuration
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Set Up SSL Certificate (Let's Encrypt)
|
||||
|
||||
1. **Obtain SSL Certificate**
|
||||
```bash
|
||||
sudo certbot --nginx -d auth.livingai.app
|
||||
```
|
||||
- Follow prompts (enter email, agree to terms)
|
||||
- Choose redirect HTTP to HTTPS (option 2)
|
||||
|
||||
2. **Verify Auto-renewal**
|
||||
```bash
|
||||
sudo certbot renew --dry-run
|
||||
```
|
||||
|
||||
3. **Update Nginx Config** (Certbot should have done this automatically)
|
||||
Your config should now include SSL and redirect HTTP to HTTPS.
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Start Application with PM2
|
||||
|
||||
1. **Start Application**
|
||||
```bash
|
||||
cd /home/ubuntu/apps/farm-auth-service
|
||||
pm2 start src/index.js --name "auth-service"
|
||||
```
|
||||
|
||||
2. **Save PM2 Configuration**
|
||||
```bash
|
||||
pm2 save
|
||||
pm2 startup
|
||||
# Copy and run the command it outputs (starts PM2 on boot)
|
||||
```
|
||||
|
||||
3. **PM2 Useful Commands**
|
||||
```bash
|
||||
pm2 list # View running processes
|
||||
pm2 logs auth-service # View logs
|
||||
pm2 restart auth-service # Restart service
|
||||
pm2 stop auth-service # Stop service
|
||||
pm2 monit # Monitor resources
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 9: Configure Firewall (UFW)
|
||||
|
||||
1. **Set Up Firewall Rules**
|
||||
```bash
|
||||
sudo ufw allow OpenSSH
|
||||
sudo ufw allow 'Nginx Full'
|
||||
sudo ufw enable
|
||||
sudo ufw status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 10: Test Deployment
|
||||
|
||||
1. **Check Application Status**
|
||||
```bash
|
||||
pm2 status
|
||||
pm2 logs auth-service --lines 50
|
||||
```
|
||||
|
||||
2. **Test API Endpoint**
|
||||
```bash
|
||||
# From server
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# From your local machine (should work via domain)
|
||||
curl https://auth.livingai.app/health
|
||||
```
|
||||
|
||||
3. **Check Nginx Logs**
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/access.log
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 11: Database Setup (if using AWS RDS)
|
||||
|
||||
1. **Ensure Database is Accessible**
|
||||
- Your Lightsail instance should be able to connect to your AWS RDS instance
|
||||
- Check security groups allow connection from Lightsail IP
|
||||
- Verify SSM Parameter Store has correct credentials
|
||||
|
||||
2. **Test Database Connection**
|
||||
```bash
|
||||
cd /home/ubuntu/apps/farm-auth-service
|
||||
node -e "require('./src/db').then(() => console.log('DB Connected')).catch(e => console.error(e))"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 12: Optional - Set Up Redis (for Rate Limiting)
|
||||
|
||||
If you want to use Redis for rate limiting:
|
||||
|
||||
1. **Option A: Install Redis on Same Server**
|
||||
```bash
|
||||
sudo apt install -y redis-server
|
||||
sudo systemctl start redis-server
|
||||
sudo systemctl enable redis-server
|
||||
```
|
||||
Then update `.env`:
|
||||
```env
|
||||
REDIS_URL=redis://localhost:6379
|
||||
```
|
||||
|
||||
2. **Option B: Use AWS ElastiCache**
|
||||
- Create ElastiCache Redis cluster
|
||||
- Update `.env` with ElastiCache endpoint:
|
||||
```env
|
||||
REDIS_URL=redis://your-elasticache-endpoint:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring and Maintenance
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
pm2 logs auth-service
|
||||
pm2 logs auth-service --err # Error logs only
|
||||
pm2 logs auth-service --out # Output logs only
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
```bash
|
||||
pm2 restart auth-service
|
||||
```
|
||||
|
||||
### Update Application
|
||||
```bash
|
||||
cd /home/ubuntu/apps/farm-auth-service
|
||||
git pull # or upload new files
|
||||
npm install --production
|
||||
pm2 restart auth-service
|
||||
```
|
||||
|
||||
### Monitor Resources
|
||||
```bash
|
||||
pm2 monit
|
||||
htop # System resources
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Application Not Starting
|
||||
1. Check logs: `pm2 logs auth-service`
|
||||
2. Verify .env file exists and has correct values
|
||||
3. Check database connectivity
|
||||
4. Verify all dependencies installed: `npm list`
|
||||
|
||||
### 502 Bad Gateway
|
||||
- Check if app is running: `pm2 status`
|
||||
- Check app logs: `pm2 logs auth-service`
|
||||
- Verify port 3000 is listening: `netstat -tlnp | grep 3000`
|
||||
- Check Nginx error logs: `sudo tail -f /var/log/nginx/error.log`
|
||||
|
||||
### SSL Certificate Issues
|
||||
- Verify DNS points to correct IP: `nslookup auth.livingai.app`
|
||||
- Check certificate: `sudo certbot certificates`
|
||||
- Renew manually: `sudo certbot renew`
|
||||
|
||||
### Database Connection Issues
|
||||
- Verify AWS SSM parameters are correct
|
||||
- Check AWS credentials in .env
|
||||
- Test database connectivity from server
|
||||
- Check security group allows connection
|
||||
|
||||
---
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] Strong JWT secrets generated and stored securely
|
||||
- [ ] .env file has 600 permissions
|
||||
- [ ] Firewall configured (UFW)
|
||||
- [ ] SSL certificate installed and auto-renewal working
|
||||
- [ ] CORS configured with specific allowed origins (not *)
|
||||
- [ ] AWS credentials have minimal required permissions
|
||||
- [ ] Regular security updates: `sudo apt update && sudo apt upgrade`
|
||||
- [ ] PM2 logs are monitored
|
||||
- [ ] Database credentials stored in SSM (not in .env)
|
||||
|
||||
---
|
||||
|
||||
## Backup Strategy
|
||||
|
||||
1. **Database Backups**
|
||||
- Set up automated RDS snapshots in AWS Console
|
||||
- Or use pg_dump for manual backups
|
||||
|
||||
2. **Application Code**
|
||||
- Code is in Git repository (already backed up)
|
||||
- Keep .env file backed up securely (encrypted)
|
||||
|
||||
3. **Configuration**
|
||||
- Document all configuration changes
|
||||
- Keep .env template versioned (without secrets)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Set up monitoring/alerting (e.g., CloudWatch, PM2 Plus)
|
||||
2. Configure log rotation for PM2
|
||||
3. Set up CI/CD pipeline for automated deployments
|
||||
4. Configure health checks and auto-restart on failure
|
||||
5. Set up database backups schedule
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues, check:
|
||||
- Application logs: `pm2 logs auth-service`
|
||||
- Nginx logs: `/var/log/nginx/`
|
||||
- System logs: `journalctl -xe`
|
||||
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
# AWS Database Migration - Implementation Summary
|
||||
|
||||
## ✅ Completed Changes
|
||||
|
||||
All code changes have been implemented to migrate from local Docker PostgreSQL to AWS PostgreSQL using AWS SSM Parameter Store for secure credential management.
|
||||
|
||||
## 📁 Files Modified
|
||||
|
||||
### 1. `src/utils/awsSsm.js`
|
||||
**Changes:**
|
||||
- ✅ Updated to use correct SSM parameter paths:
|
||||
- Read-Write: `/test/livingai/db/app`
|
||||
- Read-Only: `/test/livingai/db/app/readonly`
|
||||
- ✅ Added support for `DB_USE_READONLY` environment variable
|
||||
- ✅ Improved error handling with detailed error messages
|
||||
- ✅ Added `buildDatabaseConfig()` function for SSL support
|
||||
- ✅ Updated credential validation and parsing
|
||||
|
||||
### 2. `src/db.js`
|
||||
**Changes:**
|
||||
- ✅ Added SSL configuration for self-signed certificates
|
||||
- ✅ Updated to use `buildDatabaseConfig()` instead of connection string
|
||||
- ✅ Improved error handling and logging
|
||||
- ✅ Auto-detection of AWS database when `DB_HOST=db.livingai.app`
|
||||
- ✅ Connection pool configuration (max: 20, idleTimeout: 30s)
|
||||
|
||||
### 3. `src/config.js`
|
||||
**Changes:**
|
||||
- ✅ Updated to require AWS credentials when using SSM
|
||||
- ✅ Removed `DATABASE_URL` from required env vars when `USE_AWS_SSM=true`
|
||||
- ✅ Added validation for AWS credentials
|
||||
|
||||
### 4. Documentation
|
||||
**New Files:**
|
||||
- ✅ `docs/getting-started/AWS_DATABASE_MIGRATION.md` - Complete migration guide
|
||||
- ✅ `docs/getting-started/ENV_VARIABLES_REFERENCE.md` - Environment variables reference
|
||||
- ✅ `MIGRATION_SUMMARY.md` - This file
|
||||
|
||||
## 🔒 Security Implementation
|
||||
|
||||
### ✅ Credentials Management
|
||||
- **NO database credentials in `.env` files**
|
||||
- Credentials fetched from AWS SSM Parameter Store at runtime
|
||||
- Only AWS credentials (for SSM access) in `.env`
|
||||
- Supports both read-write and read-only users
|
||||
|
||||
### ✅ SSL Configuration
|
||||
- SSL enabled with `rejectUnauthorized: false` for self-signed certificates
|
||||
- Connection string includes `?sslmode=require`
|
||||
- Proper SSL configuration in connection pool
|
||||
|
||||
## 📋 Required Environment Variables
|
||||
|
||||
### For AWS Database (Production)
|
||||
```env
|
||||
# AWS Configuration (for SSM access)
|
||||
AWS_REGION=ap-south-1
|
||||
AWS_ACCESS_KEY_ID=your_aws_access_key
|
||||
AWS_SECRET_ACCESS_KEY=your_aws_secret_key
|
||||
USE_AWS_SSM=true
|
||||
|
||||
# JWT Configuration
|
||||
JWT_ACCESS_SECRET=your_secret
|
||||
JWT_REFRESH_SECRET=your_secret
|
||||
```
|
||||
|
||||
### Optional
|
||||
```env
|
||||
DB_USE_READONLY=false # false = read_write_user, true = read_only_user
|
||||
DB_HOST=db.livingai.app
|
||||
DB_PORT=5432
|
||||
DB_NAME=livingai_test_db
|
||||
```
|
||||
|
||||
## 🔄 Migration Steps
|
||||
|
||||
1. **Set up AWS SSM Parameters:**
|
||||
- Create `/test/livingai/db/app` with read-write user credentials (JSON format)
|
||||
- Create `/test/livingai/db/app/readonly` with read-only user credentials (optional)
|
||||
|
||||
2. **Update `.env` file:**
|
||||
- Add AWS credentials (AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
|
||||
- Set `USE_AWS_SSM=true`
|
||||
- Remove any database credentials (DB_USER, DB_PASSWORD, DATABASE_URL)
|
||||
|
||||
3. **Verify IAM Permissions:**
|
||||
- Ensure IAM user/role has `ssm:GetParameter` permission for both SSM parameter paths
|
||||
|
||||
4. **Test Connection:**
|
||||
- Start application: `npm start`
|
||||
- Verify logs show successful SSM credential fetch and database connection
|
||||
|
||||
## 🧪 Testing Checklist
|
||||
|
||||
- [ ] AWS SSM parameters created with correct paths and JSON format
|
||||
- [ ] IAM user has SSM read permissions
|
||||
- [ ] `.env` file has AWS credentials (no DB credentials)
|
||||
- [ ] `USE_AWS_SSM=true` in `.env`
|
||||
- [ ] Application starts without errors
|
||||
- [ ] Database connection established successfully
|
||||
- [ ] SSL connection working (no SSL errors)
|
||||
- [ ] API endpoints respond correctly
|
||||
- [ ] Database queries execute successfully
|
||||
- [ ] All business logic works as before
|
||||
|
||||
## 🔍 Verification Commands
|
||||
|
||||
### Check AWS SSM Parameter
|
||||
```bash
|
||||
aws ssm get-parameter --name "/test/livingai/db/app" --with-decryption --region ap-south-1
|
||||
```
|
||||
|
||||
### Test Database Connection
|
||||
```bash
|
||||
npm start
|
||||
# Look for these log messages:
|
||||
# ✅ Successfully fetched DB credentials from SSM: /test/livingai/db/app (read-write user)
|
||||
# ✅ Using database credentials from AWS SSM Parameter Store
|
||||
# ✅ Database connection established successfully
|
||||
```
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
1. **No Breaking Changes**: All business logic remains unchanged. Only database connection configuration was updated.
|
||||
|
||||
2. **Backward Compatibility**: Local development still works with `DATABASE_URL` when `USE_AWS_SSM=false`.
|
||||
|
||||
3. **Security**: Database credentials are never stored in files. They are fetched from AWS SSM at runtime.
|
||||
|
||||
4. **SSL**: Self-signed certificates are supported via `rejectUnauthorized: false` configuration.
|
||||
|
||||
5. **Connection Pooling**: Configured with sensible defaults (max 20 connections, 30s idle timeout).
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
For detailed information, see:
|
||||
- `docs/getting-started/AWS_DATABASE_MIGRATION.md` - Complete migration guide
|
||||
- `docs/getting-started/ENV_VARIABLES_REFERENCE.md` - Environment variables reference
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
Common issues and solutions are documented in `docs/getting-started/AWS_DATABASE_MIGRATION.md` under the "Troubleshooting" section.
|
||||
|
||||
## ✨ Next Steps
|
||||
|
||||
1. Review the changes in the modified files
|
||||
2. Set up AWS SSM parameters with your database credentials
|
||||
3. Update your `.env` file with AWS credentials
|
||||
4. Test the connection
|
||||
5. Deploy to your AWS environment
|
||||
|
||||
---
|
||||
|
||||
**Migration Status**: ✅ Complete
|
||||
**All Requirements Met**: ✅ Yes
|
||||
**Security Requirements Met**: ✅ Yes
|
||||
**Backward Compatibility**: ✅ Maintained
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# Quick Deployment Reference - auth.livingai.app
|
||||
|
||||
## TL;DR - Quick Steps
|
||||
|
||||
### 1. Lightsail Setup
|
||||
- Create Ubuntu 22.04 instance
|
||||
- Attach static IP
|
||||
- Open ports: 80, 443, 22
|
||||
|
||||
### 2. DNS Configuration
|
||||
- Point `auth.livingai.app` A record to Lightsail static IP
|
||||
|
||||
### 3. Server Setup (SSH into server)
|
||||
```bash
|
||||
# Run setup script
|
||||
cd ~ && wget https://your-repo/setup-server.sh
|
||||
bash setup-server.sh
|
||||
|
||||
# OR manually:
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt install -y nodejs nginx git certbot python3-certbot-nginx
|
||||
sudo npm install -g pm2
|
||||
```
|
||||
|
||||
### 4. Deploy Application
|
||||
```bash
|
||||
cd ~/apps
|
||||
git clone <your-repo-url> farm-auth-service
|
||||
cd farm-auth-service
|
||||
npm install --production
|
||||
cp example.env .env
|
||||
nano .env # Configure environment variables
|
||||
```
|
||||
|
||||
### 5. Configure Nginx
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/auth.livingai.app
|
||||
# Add configuration (see DEPLOYMENT_GUIDE.md)
|
||||
sudo ln -s /etc/nginx/sites-available/auth.livingai.app /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 6. SSL Certificate
|
||||
```bash
|
||||
sudo certbot --nginx -d auth.livingai.app
|
||||
```
|
||||
|
||||
### 7. Start Application
|
||||
```bash
|
||||
cd ~/apps/farm-auth-service
|
||||
pm2 start ecosystem.config.js
|
||||
pm2 save
|
||||
pm2 startup # Follow instructions
|
||||
```
|
||||
|
||||
### 8. Configure Firewall
|
||||
```bash
|
||||
sudo ufw allow OpenSSH
|
||||
sudo ufw allow 'Nginx Full'
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Critical Configuration (.env)
|
||||
|
||||
**Required Settings:**
|
||||
```env
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
TRUST_PROXY=true
|
||||
DATABASE_MODE=aws
|
||||
AWS_REGION=ap-south-1
|
||||
AWS_ACCESS_KEY_ID=your_key
|
||||
AWS_SECRET_ACCESS_KEY=your_secret
|
||||
JWT_ACCESS_SECRET=generate_strong_secret_min_32_chars
|
||||
JWT_REFRESH_SECRET=generate_strong_secret_min_32_chars
|
||||
CORS_ALLOWED_ORIGINS=https://your-app-domain.com,https://app.livingai.app
|
||||
```
|
||||
|
||||
**Generate JWT Secrets:**
|
||||
```bash
|
||||
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
pm2 logs auth-service
|
||||
|
||||
# Restart
|
||||
pm2 restart auth-service
|
||||
|
||||
# Status
|
||||
pm2 status
|
||||
|
||||
# Nginx logs
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
sudo tail -f /var/log/nginx/access.log
|
||||
|
||||
# Test API
|
||||
curl https://auth.livingai.app/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**502 Bad Gateway?**
|
||||
- Check PM2: `pm2 status`
|
||||
- Check logs: `pm2 logs auth-service`
|
||||
- Check Nginx: `sudo nginx -t`
|
||||
|
||||
**Can't connect to database?**
|
||||
- Verify AWS credentials in .env
|
||||
- Check SSM Parameter Store access
|
||||
- Test connection: `node -e "require('./src/db')"`
|
||||
|
||||
**SSL issues?**
|
||||
- Verify DNS: `nslookup auth.livingai.app`
|
||||
- Renew cert: `sudo certbot renew`
|
||||
|
||||
---
|
||||
|
||||
For detailed instructions, see `DEPLOYMENT_GUIDE.md`
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// PM2 Ecosystem Configuration File
|
||||
// Usage: pm2 start ecosystem.config.js
|
||||
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'auth-service',
|
||||
script: 'src/index.js',
|
||||
instances: 1, // For single instance, use 1. For cluster mode, use 'max' or number
|
||||
exec_mode: 'fork', // 'fork' for single instance, 'cluster' for multiple instances
|
||||
watch: false, // Set to true for development
|
||||
max_memory_restart: '500M', // Restart if memory exceeds 500MB
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3000
|
||||
},
|
||||
error_file: './logs/pm2-error.log',
|
||||
out_file: './logs/pm2-out.log',
|
||||
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
|
||||
merge_logs: true,
|
||||
autorestart: true,
|
||||
max_restarts: 10,
|
||||
min_uptime: '10s',
|
||||
listen_timeout: 10000,
|
||||
kill_timeout: 5000
|
||||
}]
|
||||
};
|
||||
|
||||
|
|
@ -1,231 +0,0 @@
|
|||
# Kotlin OTP Verification Fix
|
||||
|
||||
## Issue Analysis
|
||||
|
||||
The backend `/auth/verify-otp` endpoint expects:
|
||||
```json
|
||||
{
|
||||
"phone_number": "+919876543210", // Must be E.164 format with +
|
||||
"code": "123456", // String, not number
|
||||
"device_id": "optional-device-id",
|
||||
"device_info": {
|
||||
"platform": "android",
|
||||
"model": "device-model",
|
||||
"os_version": "Android 13",
|
||||
"app_version": "1.0.0",
|
||||
"language_code": "en",
|
||||
"timezone": "Asia/Kolkata"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Issues in Kotlin Implementation
|
||||
|
||||
1. **Phone Number Format**: Must include `+` prefix (E.164 format)
|
||||
2. **OTP Code Type**: Must be sent as string, not integer
|
||||
3. **Request Body**: Must match exact field names (`phone_number`, `code`)
|
||||
4. **Missing Device Info**: Backend accepts but doesn't require device_info
|
||||
|
||||
## Fixed Kotlin Code
|
||||
|
||||
### Option 1: Update AuthManager.login() method
|
||||
|
||||
```kotlin
|
||||
// In AuthManager.kt or AuthApiClient.kt
|
||||
suspend fun login(phoneNumber: String, otpCode: String): Result<LoginResponse> {
|
||||
return try {
|
||||
// Ensure phone number has + prefix (E.164 format)
|
||||
val normalizedPhone = if (phoneNumber.startsWith("+")) {
|
||||
phoneNumber
|
||||
} else if (phoneNumber.length == 10) {
|
||||
"+91$phoneNumber" // Add +91 for 10-digit Indian numbers
|
||||
} else {
|
||||
phoneNumber // Keep as is if already formatted
|
||||
}
|
||||
|
||||
// Ensure OTP is string (not integer)
|
||||
val otpString = otpCode.toString().trim()
|
||||
|
||||
// Get device info
|
||||
val deviceId = getDeviceId() // Your method to get device ID
|
||||
val deviceInfo = getDeviceInfo() // Your method to get device info
|
||||
|
||||
val requestBody = mapOf(
|
||||
"phone_number" to normalizedPhone,
|
||||
"code" to otpString,
|
||||
"device_id" to deviceId,
|
||||
"device_info" to deviceInfo
|
||||
)
|
||||
|
||||
val response = apiClient.post("/auth/verify-otp", requestBody)
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val loginResponse = response.body() // Parse your LoginResponse
|
||||
Result.success(loginResponse)
|
||||
} else {
|
||||
// Handle error response
|
||||
val errorBody = response.errorBody()?.string()
|
||||
Result.failure(Exception("OTP verification failed: $errorBody"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get device info
|
||||
private fun getDeviceInfo(): Map<String, String?> {
|
||||
return mapOf(
|
||||
"platform" to "android",
|
||||
"model" to android.os.Build.MODEL,
|
||||
"os_version" to android.os.Build.VERSION.RELEASE,
|
||||
"app_version" to getAppVersion(), // Your method
|
||||
"language_code" to Locale.getDefault().language,
|
||||
"timezone" to TimeZone.getDefault().id
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDeviceId(): String {
|
||||
// Use your existing device ID logic
|
||||
// Could be Android ID, UUID, etc.
|
||||
return Settings.Secure.getString(
|
||||
context.contentResolver,
|
||||
Settings.Secure.ANDROID_ID
|
||||
) ?: UUID.randomUUID().toString()
|
||||
}
|
||||
```
|
||||
|
||||
### Option 2: Quick Fix in OtpScreen.kt
|
||||
|
||||
Update your `OtpScreen.kt` to ensure proper formatting:
|
||||
|
||||
```kotlin
|
||||
@Composable
|
||||
fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
|
||||
val otp = remember { mutableStateOf("") }
|
||||
val context = LocalContext.current.applicationContext
|
||||
val scope = rememberCoroutineScope()
|
||||
val authManager = remember { AuthManager(context, AuthApiClient(context), TokenManager(context)) }
|
||||
|
||||
val isSignInFlow = name == "existing_user"
|
||||
|
||||
// Normalize phone number to ensure it has + prefix
|
||||
val normalizedPhone = remember(phoneNumber) {
|
||||
if (phoneNumber.startsWith("+")) {
|
||||
phoneNumber
|
||||
} else if (phoneNumber.length == 10) {
|
||||
"+91$phoneNumber"
|
||||
} else {
|
||||
phoneNumber
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 36.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(200.dp))
|
||||
|
||||
Text("Enter OTP", fontSize = 24.sp, fontWeight = FontWeight.Medium, color = Color(0xFF364153))
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
TextField(
|
||||
value = otp.value,
|
||||
onValueChange = { if (it.length <= 6 && it.all { char -> char.isDigit() }) otp.value = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(60.dp)
|
||||
.shadow(elevation = 1.dp, shape = RoundedCornerShape(16.dp)),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.White.copy(alpha = 0.9f),
|
||||
unfocusedContainerColor = Color.White.copy(alpha = 0.9f),
|
||||
disabledContainerColor = Color.White.copy(alpha = 0.9f),
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
),
|
||||
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center, fontSize = 24.sp),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
// Ensure OTP is not empty and is 6 digits
|
||||
if (otp.value.length != 6) {
|
||||
Toast.makeText(context, "Please enter a valid 6-digit OTP", Toast.LENGTH_SHORT).show()
|
||||
return@launch
|
||||
}
|
||||
|
||||
// Use normalized phone number
|
||||
authManager.login(normalizedPhone, otp.value.trim())
|
||||
.onSuccess { response ->
|
||||
if (isSignInFlow) {
|
||||
navController.navigate("success") { popUpTo("login") { inclusive = true } }
|
||||
} else {
|
||||
if (response.needsProfile) {
|
||||
navController.navigate("create_profile/$name")
|
||||
} else {
|
||||
navController.navigate("success") { popUpTo("login") { inclusive = true } }
|
||||
}
|
||||
}
|
||||
}
|
||||
.onFailure { error ->
|
||||
// More detailed error handling
|
||||
val errorMessage = error.message ?: "Invalid or expired OTP"
|
||||
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
|
||||
Log.e("OtpScreen", "OTP verification failed", error)
|
||||
}
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFFE9A00)),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.shadow(elevation = 4.dp, shape = RoundedCornerShape(16.dp))
|
||||
) {
|
||||
Text("Continue", color = Color.White, fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key Changes to Check in Your AuthManager/AuthApiClient
|
||||
|
||||
1. **Phone Number Normalization**: Ensure `+` prefix is present
|
||||
2. **OTP as String**: Send OTP as string, not integer
|
||||
3. **Request Body Format**: Use exact field names from backend
|
||||
4. **Error Handling**: Check response status and error body
|
||||
|
||||
## Debugging Steps
|
||||
|
||||
1. **Add Logging**: Log the exact request being sent
|
||||
```kotlin
|
||||
Log.d("AuthManager", "Sending verify-otp: phone=$normalizedPhone, code=$otpString")
|
||||
Log.d("AuthManager", "Request body: $requestBody")
|
||||
```
|
||||
|
||||
2. **Check Response**: Log the response
|
||||
```kotlin
|
||||
Log.d("AuthManager", "Response code: ${response.code()}")
|
||||
Log.d("AuthManager", "Response body: ${response.body()?.string()}")
|
||||
```
|
||||
|
||||
3. **Compare with HTML**: Use the same phone number and OTP in HTML test page to verify backend is working
|
||||
|
||||
## Most Likely Issues
|
||||
|
||||
1. **Phone number missing `+` prefix** - Backend normalizes but expects E.164 format
|
||||
2. **OTP sent as number instead of string** - Backend expects string
|
||||
3. **Wrong field names** - Must be `phone_number` and `code` (with underscores)
|
||||
4. **Request body not properly serialized** - Check your JSON serialization
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#!/bin/bash
|
||||
# Quick Setup Script for AWS Lightsail Ubuntu Server
|
||||
# Run this script on your Lightsail server after initial SSH connection
|
||||
# Usage: bash setup-server.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting Farm Auth Service Server Setup..."
|
||||
|
||||
# Update system
|
||||
echo "📦 Updating system packages..."
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
# Install Node.js
|
||||
echo "📦 Installing Node.js..."
|
||||
if ! command -v node &> /dev/null; then
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
else
|
||||
echo "✅ Node.js already installed: $(node --version)"
|
||||
fi
|
||||
|
||||
# Install Nginx
|
||||
echo "📦 Installing Nginx..."
|
||||
if ! command -v nginx &> /dev/null; then
|
||||
sudo apt install -y nginx
|
||||
sudo systemctl start nginx
|
||||
sudo systemctl enable nginx
|
||||
else
|
||||
echo "✅ Nginx already installed"
|
||||
fi
|
||||
|
||||
# Install PM2
|
||||
echo "📦 Installing PM2..."
|
||||
if ! command -v pm2 &> /dev/null; then
|
||||
sudo npm install -g pm2
|
||||
else
|
||||
echo "✅ PM2 already installed: $(pm2 --version)"
|
||||
fi
|
||||
|
||||
# Install Git
|
||||
echo "📦 Installing Git..."
|
||||
sudo apt install -y git
|
||||
|
||||
# Install Certbot
|
||||
echo "📦 Installing Certbot..."
|
||||
sudo apt install -y certbot python3-certbot-nginx
|
||||
|
||||
# Create application directory
|
||||
echo "📁 Creating application directory..."
|
||||
mkdir -p ~/apps
|
||||
cd ~/apps
|
||||
|
||||
# Create logs directory
|
||||
echo "📁 Creating logs directory..."
|
||||
mkdir -p ~/apps/farm-auth-service/logs
|
||||
|
||||
echo "✅ Server setup completed!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Clone your repository: cd ~/apps && git clone <your-repo-url> farm-auth-service"
|
||||
echo "2. Install dependencies: cd farm-auth-service && npm install --production"
|
||||
echo "3. Configure .env file: cp example.env .env && nano .env"
|
||||
echo "4. Configure Nginx (see DEPLOYMENT_GUIDE.md)"
|
||||
echo "5. Set up SSL: sudo certbot --nginx -d auth.livingai.app"
|
||||
echo "6. Start application: pm2 start ecosystem.config.js"
|
||||
|
||||
Loading…
Reference in New Issue