UnTerraform
This commit is contained in:
41
terraform/.terraform.lock.hcl
generated
41
terraform/.terraform.lock.hcl
generated
@@ -1,41 +0,0 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/google" {
|
||||
version = "7.9.0"
|
||||
hashes = [
|
||||
"h1:xhzErMeJSU7hzQs7Z5MvnGG+cwZA3gxW1tGwRbGjsL8=",
|
||||
"zh:09d6c241edc669a2d5a0ec20ebdc8489995e690ed7b94bb64048f23db589801b",
|
||||
"zh:0aa72a11265db9201464829a31a3d834149cd0aad5bb184a46f08bef4cd56d83",
|
||||
"zh:1bd349373d11ba77bcd6ba389ec94e5116ac6b1c83ea2db85e847af7948d553f",
|
||||
"zh:3ca032646e2d332e48aa46be19b7c30cdadd09e066e2f7c9e0f2022fbc0e0e7a",
|
||||
"zh:481a20133ad3de9ed8a5de5ade5a46fe8f9f9c9f740ad7ebb9c4d7ef914140db",
|
||||
"zh:56f3b7b521fa09dacc94abca7451b075d39793a569b53ef7ebed83fb088f5035",
|
||||
"zh:bde46790fb4e6bf106df0c404aa2f8361651ad9ac70f30faf4bcb55d65e7d38e",
|
||||
"zh:cc8d931e1d45376a421cff84d1223571296724e263ae4ebc021e3bd76bc74b9a",
|
||||
"zh:d6604e6bb61f695e631d8862490389d65895f34d0133c066a81601e5539f0fa7",
|
||||
"zh:d999cf33e1dd8cb3b02a75d079e542d9f3d73bc5756b870e78dfe3bf89535bd9",
|
||||
"zh:e4f38256d1f190f7e04d393ba3e247da7e12f4bcf22c2c3ce1f57b465b4ad8a3",
|
||||
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/google-beta" {
|
||||
version = "6.50.0"
|
||||
constraints = "~> 6.0"
|
||||
hashes = [
|
||||
"h1:uxh4ME3hhSzVjmiWgA1IQqYqg25MV6FMVArHyA6Ki5o=",
|
||||
"zh:18b442bd0a05321d39dda1e9e3f1bdede4e61bc2ac62cc7a67037a3864f75101",
|
||||
"zh:2e387c51455862828bec923a3ec81abf63a4d998da470cf00e09003bda53d668",
|
||||
"zh:3942e708fa84ebe54996086f4b1398cb747fe19cbcd0be07ace528291fb35dee",
|
||||
"zh:496287dd48b34ae6197cb1f887abeafd07c33f389dbe431bb01e24846754cfdd",
|
||||
"zh:6eca885419969ce5c2a706f34dce1f10bde9774757675f2d8a92d12e5a1be390",
|
||||
"zh:710dbef826c3fe7f76f844dae47937e8e4c1279dd9205ec4610be04cf3327244",
|
||||
"zh:777ebf44b24bfc7bdbf770dc089f1a72f143b4718fdedb8c6bd75983115a1ec2",
|
||||
"zh:9c8703bba37b8c7ad857efc3513392c5a096c519397c1cb822d7612f38e4262f",
|
||||
"zh:c4f1d3a73de2702277c99d5348ad6d374705bcfdd367ad964ff4cfd2cf06c281",
|
||||
"zh:eca8df11af3f5a948492d5b8b5d01b4ec705aad10bc30ec1524205508ae28393",
|
||||
"zh:f41e7fd5f2628e8fd6b8ea136366923858f54428d1729898925469b862c275c2",
|
||||
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||
]
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# Use Python 3.11 slim image
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy requirements first for better layer caching
|
||||
COPY requirements.txt .
|
||||
|
||||
# Install dependencies
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create non-root user (security best practice)
|
||||
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
||||
USER appuser
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:5000/ || exit 1
|
||||
|
||||
# Run the application
|
||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--timeout", "120", "app:app"]
|
||||
@@ -1,262 +0,0 @@
|
||||
# Firebase Authentication Automation with Terraform
|
||||
|
||||
This guide explains how Firebase Authentication settings are automated in the Rothbard Law Group deployment.
|
||||
|
||||
## 🚀 What's Automated
|
||||
|
||||
### 1. Authentication Providers
|
||||
- **Email/Password**: Enabled by default
|
||||
- **Google Sign-In**: Optional (controlled by `enable_google_signin` variable)
|
||||
- **Phone, Facebook, Apple**: Disabled for security
|
||||
|
||||
### 2. Email Templates
|
||||
- **Password Reset**: Professional HTML and text templates
|
||||
- **Email Verification**: Welcome templates with branding
|
||||
- **Customizable**: From address, name, and reply-to settings
|
||||
|
||||
### 3. Security Rules
|
||||
- **Firestore Rules**: Users can only access their own data
|
||||
- **Authentication Required**: All database operations require auth
|
||||
- **Profile Access**: Users can read/write their own profile only
|
||||
|
||||
### 4. Firebase Hosting
|
||||
- **Static Asset Hosting**: Optional for CSS, JS, images
|
||||
- **Caching Headers**: Optimized performance
|
||||
- **URL Rewrites**: Proper routing for SPA
|
||||
|
||||
## 📋 Configuration Variables
|
||||
|
||||
Add these to your `terraform.tfvars`:
|
||||
|
||||
```hcl
|
||||
# Authentication Settings
|
||||
enable_google_signin = false # Set to true to enable Google Sign-In
|
||||
|
||||
# Email Configuration
|
||||
auth_from_email = "noreply@rothbardlaw.com"
|
||||
auth_from_name = "Rothbard Law Group"
|
||||
auth_reply_to = "support@rothbardlaw.com"
|
||||
```
|
||||
|
||||
## 🔧 Authentication Provider Setup
|
||||
|
||||
### Email/Password (Default)
|
||||
```hcl
|
||||
sign_in_options {
|
||||
email {
|
||||
enabled = true
|
||||
password_required = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Google Sign-In (Optional)
|
||||
To enable Google Sign-In:
|
||||
|
||||
1. **Set variable**:
|
||||
```hcl
|
||||
enable_google_signin = true
|
||||
```
|
||||
|
||||
2. **Configure OAuth in Google Cloud**:
|
||||
```bash
|
||||
# Enable Google+ API
|
||||
gcloud services enable plus.googleapis.com
|
||||
|
||||
# Create OAuth consent screen
|
||||
gcloud alpha iap oauth-clients create \
|
||||
--display-name="Rothbard Portal" \
|
||||
--brand="Rothbard Law Group"
|
||||
```
|
||||
|
||||
3. **Update Firebase Console**:
|
||||
- Go to Firebase Console → Authentication → Sign-in method
|
||||
- Enable Google provider
|
||||
- Add your OAuth client ID and secret
|
||||
|
||||
## 📧 Email Template Customization
|
||||
|
||||
### Template Files
|
||||
- `templates/reset_password.html` - Password reset HTML
|
||||
- `templates/reset_password.txt` - Password reset text
|
||||
- `templates/email_verification.html` - Email verification HTML
|
||||
- `templates/email_verification.txt` - Email verification text
|
||||
|
||||
### Customization Options
|
||||
- **Branding**: Update colors, logos in HTML templates
|
||||
- **Contact Info**: Change address, phone numbers
|
||||
- **Content**: Modify welcome messages and instructions
|
||||
|
||||
### Email Variables Available
|
||||
- `{{ resetLink }}` - Password reset URL
|
||||
- `{{ verificationLink }}` - Email verification URL
|
||||
- `{{ userEmail }}` - User's email address
|
||||
|
||||
## 🔒 Security Rules Explained
|
||||
|
||||
### Firestore Rules
|
||||
```javascript
|
||||
// Users can only access their own profile
|
||||
match /users/{userId} {
|
||||
allow read, write: if request.auth.uid == userId;
|
||||
}
|
||||
|
||||
// All other collections require authentication
|
||||
match /{collection=**} {
|
||||
allow read, write: if request.auth != null;
|
||||
}
|
||||
```
|
||||
|
||||
### Security Features
|
||||
- **User Isolation**: Users can't access other users' data
|
||||
- **Authentication Required**: No anonymous access
|
||||
- **Self-Service**: Users can only modify their own profiles
|
||||
|
||||
## 🛠️ Advanced Configuration
|
||||
|
||||
### Multi-Factor Authentication
|
||||
Currently disabled for simplicity. To enable:
|
||||
|
||||
```hcl
|
||||
multi_factor_auth {
|
||||
enabled = true
|
||||
provider_configs {
|
||||
phone {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Email Templates
|
||||
For more advanced templates, you can use Firebase Admin SDK:
|
||||
|
||||
```python
|
||||
# In your Flask app
|
||||
from firebase_admin import auth
|
||||
|
||||
def send_custom_email(user_email, template_name):
|
||||
# Custom email sending logic
|
||||
pass
|
||||
```
|
||||
|
||||
### Domain Restrictions
|
||||
To restrict authentication to specific domains:
|
||||
|
||||
```javascript
|
||||
// In Firebase Auth security rules
|
||||
rules_version = '2';
|
||||
service cloud.firestore {
|
||||
match /users/{userId} {
|
||||
allow read, write: if
|
||||
request.auth != null &&
|
||||
request.auth.token.email.matches('.*@rothbardlaw\\.com$');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Monitoring and Analytics
|
||||
|
||||
### Authentication Events
|
||||
Track these events in your application:
|
||||
|
||||
```python
|
||||
# Log authentication events
|
||||
def log_auth_event(event_type, user_id, details=None):
|
||||
db.collection('auth_logs').add({
|
||||
'event_type': event_type,
|
||||
'user_id': user_id,
|
||||
'timestamp': firestore.SERVER_TIMESTAMP,
|
||||
'details': details or {}
|
||||
})
|
||||
```
|
||||
|
||||
### Key Events to Monitor
|
||||
- User registrations
|
||||
- Password resets
|
||||
- Failed login attempts
|
||||
- Email verifications
|
||||
|
||||
## 🔄 Updates and Maintenance
|
||||
|
||||
### Updating Email Templates
|
||||
1. Edit template files in `terraform/templates/`
|
||||
2. Run `terraform apply` to update
|
||||
3. Changes apply to new emails immediately
|
||||
|
||||
### Adding New Providers
|
||||
1. Update `google_identitytoolkit_config` in `main.tf`
|
||||
2. Add provider-specific variables
|
||||
3. Configure OAuth credentials in Google Cloud
|
||||
4. Apply Terraform changes
|
||||
|
||||
### Security Rule Updates
|
||||
1. Modify `google_firestore_security_policy` in `main.tf`
|
||||
2. Test rules in Firebase Console first
|
||||
3. Apply with Terraform
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Email Templates Not Working**
|
||||
- Check template file paths
|
||||
- Verify template syntax
|
||||
- Check email provider settings
|
||||
|
||||
2. **Authentication Provider Not Working**
|
||||
- Verify API credentials
|
||||
- Check provider configuration
|
||||
- Review Firebase Console settings
|
||||
|
||||
3. **Security Rules Blocking Access**
|
||||
- Test rules in Firebase Console
|
||||
- Check user authentication status
|
||||
- Verify collection/document paths
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Check Firebase Auth configuration
|
||||
gcloud auth troubleshoot
|
||||
|
||||
# Test authentication flow
|
||||
curl -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signIn?key=YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"user@example.com","password":"password","returnSecureToken":true}'
|
||||
|
||||
# Check Firestore rules
|
||||
gcloud firestore databases rules describe \
|
||||
--project=your-project-id
|
||||
```
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
- [Firebase Authentication Documentation](https://firebase.google.com/docs/auth)
|
||||
- [Terraform Google Provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs)
|
||||
- [Firestore Security Rules](https://firebase.google.com/docs/firestore/security/get-started)
|
||||
- [Firebase Email Templates](https://firebase.google.com/docs/auth/custom-email-templates)
|
||||
|
||||
## 🎯 Best Practices
|
||||
|
||||
1. **Security First**
|
||||
- Use HTTPS everywhere
|
||||
- Implement proper session management
|
||||
- Regular security audits
|
||||
|
||||
2. **User Experience**
|
||||
- Clear error messages
|
||||
- Professional email templates
|
||||
- Mobile-responsive design
|
||||
|
||||
3. **Maintenance**
|
||||
- Regular backups
|
||||
- Monitoring and alerts
|
||||
- Documentation updates
|
||||
|
||||
4. **Compliance**
|
||||
- GDPR compliance for EU users
|
||||
- Data retention policies
|
||||
- Privacy policy alignment
|
||||
|
||||
This automation ensures your Firebase Authentication is secure, professional, and maintainable while following industry best practices for legal client portals.
|
||||
@@ -1,297 +0,0 @@
|
||||
# Rothbard Law Group - Terraform Deployment
|
||||
|
||||
This Terraform configuration deploys the Rothbard Law Group client portal to Google Cloud Platform with Firebase backend.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Firebase App │ │ Firestore │ │ Cloud Run │
|
||||
│ (Client Auth) │◄──►│ (User Data) │◄──►│ (Flask App) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Filevine API │
|
||||
│ (Case Data) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Hosting Options
|
||||
|
||||
### 1. Cloud Run (Recommended)
|
||||
- **Best for production use**
|
||||
- Container-based deployment
|
||||
- Scales from 0 to N instances
|
||||
- Cost-effective (pay-per-use)
|
||||
- Full control over environment
|
||||
|
||||
### 2. App Engine
|
||||
- Platform-as-a-service
|
||||
- Built-in scaling
|
||||
- Slightly more restrictive
|
||||
- Good for simpler deployments
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Google Cloud SDK**
|
||||
```bash
|
||||
curl https://sdk.cloud.google.com | bash
|
||||
gcloud init
|
||||
```
|
||||
|
||||
2. **Terraform**
|
||||
```bash
|
||||
# macOS
|
||||
brew install terraform
|
||||
|
||||
# Linux
|
||||
sudo apt-get install terraform
|
||||
```
|
||||
|
||||
3. **Docker** (for Cloud Run)
|
||||
```bash
|
||||
# macOS
|
||||
brew install docker
|
||||
|
||||
# Linux
|
||||
sudo apt-get install docker.io
|
||||
```
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Configure Terraform Variables
|
||||
|
||||
Copy the example variables file:
|
||||
```bash
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
```
|
||||
|
||||
Edit `terraform.tfvars` with your values:
|
||||
```hcl
|
||||
gcp_project_id = "your-gcp-project-id"
|
||||
domain_name = "rothbard.yourdomain.com"
|
||||
hosting_option = "cloud_run" # or "app_engine"
|
||||
```
|
||||
|
||||
### 2. Set Environment Variables
|
||||
|
||||
Create a `.env` file for sensitive data:
|
||||
```bash
|
||||
# Flask Configuration
|
||||
FLASK_SECRET_KEY=your-super-secret-key
|
||||
|
||||
# Firebase Configuration
|
||||
FIREBASE_API_KEY=your-firebase-api-key
|
||||
FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
|
||||
FIREBASE_PROJECT_ID=your-gcp-project-id
|
||||
FIREBASE_APP_ID=your-firebase-app-id
|
||||
|
||||
# Service Account (optional - can use gcloud auth)
|
||||
FIREBASE_SERVICE_ACCOUNT_JSON='{"type":"service_account",...}'
|
||||
|
||||
# Filevine API Configuration
|
||||
FILEVINE_CLIENT_ID=your-filevine-client-id
|
||||
FILEVINE_CLIENT_SECRET=your-filevine-client-secret
|
||||
FILEVINE_PERSONAL_ACCESS_TOKEN=your-filevine-pat
|
||||
FILEVINE_ORG_ID=your-filevine-org-id
|
||||
FILEVINE_USER_ID=your-filevine-user-id
|
||||
```
|
||||
|
||||
### 3. Deploy with Cloud Run (Recommended)
|
||||
|
||||
#### Step 1: Build and Push Docker Image
|
||||
```bash
|
||||
# Set your project ID
|
||||
export PROJECT_ID=your-gcp-project-id
|
||||
|
||||
# Build the Docker image
|
||||
docker build -t gcr.io/$PROJECT_ID/rothbard-portal:latest .
|
||||
|
||||
# Push to Google Container Registry
|
||||
docker push gcr.io/$PROJECT_ID/rothbard-portal:latest
|
||||
```
|
||||
|
||||
#### Step 2: Deploy Infrastructure
|
||||
```bash
|
||||
cd terraform
|
||||
|
||||
# Initialize Terraform
|
||||
terraform init
|
||||
|
||||
# Plan the deployment
|
||||
terraform plan
|
||||
|
||||
# Apply the changes
|
||||
terraform apply
|
||||
```
|
||||
|
||||
#### Step 3: Update Container Image Variable
|
||||
After the first deployment, update your `terraform.tfvars`:
|
||||
```hcl
|
||||
container_image = "gcr.io/your-gcp-project-id/rothbard-portal:latest"
|
||||
```
|
||||
|
||||
Then run `terraform apply` again.
|
||||
|
||||
### 4. Deploy with App Engine
|
||||
|
||||
#### Step 1: Create Source Package
|
||||
```bash
|
||||
# Copy app files and create zip
|
||||
cp app.py requirements.txt templates/ app-engine/
|
||||
cd app-engine
|
||||
zip -r ../app-source.zip .
|
||||
cd ..
|
||||
```
|
||||
|
||||
#### Step 2: Deploy Infrastructure
|
||||
```bash
|
||||
cd terraform
|
||||
|
||||
# Set hosting option to app_engine
|
||||
# Edit terraform.tfvars: hosting_option = "app_engine"
|
||||
|
||||
# Initialize and apply
|
||||
terraform init
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## Post-Deployment Setup
|
||||
|
||||
### 1. Firebase Configuration
|
||||
|
||||
After deployment, you'll need to:
|
||||
|
||||
1. **Configure Firebase Authentication**:
|
||||
- Go to Firebase Console → Authentication
|
||||
- Enable Email/Password provider
|
||||
- Add your domain to authorized domains
|
||||
|
||||
2. **Set up Firestore Rules**:
|
||||
```javascript
|
||||
rules_version = '2';
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents {
|
||||
match /users/{userId} {
|
||||
allow read, write: if request.auth != null && request.auth.uid == userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Enable Users
|
||||
|
||||
Users need to be manually enabled in Firestore:
|
||||
|
||||
```javascript
|
||||
// In Firebase Console → Firestore Database
|
||||
// Create document in collection 'users' with document ID = Firebase UID
|
||||
{
|
||||
enabled: true,
|
||||
caseEmail: "user@example.com", // This filters Filevine projects
|
||||
name: "Client Name"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Custom Domain (Optional)
|
||||
|
||||
For Cloud Run with custom domain:
|
||||
|
||||
```bash
|
||||
# Map your domain to the Cloud Run service
|
||||
gcloud run services add-iam-policy-binding rothbard-portal-service \
|
||||
--member="allUsers" \
|
||||
--role="roles/run.invoker"
|
||||
|
||||
# Set up domain mapping
|
||||
gcloud run domain-mappings create \
|
||||
--domain=rothbard.yourdomain.com \
|
||||
--service=rothbard-portal-service
|
||||
```
|
||||
|
||||
## Monitoring and Maintenance
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
# Cloud Run logs
|
||||
gcloud logs read "resource.type=cloud_run_revision" --limit 50
|
||||
|
||||
# App Engine logs
|
||||
gcloud app logs tail -s default
|
||||
```
|
||||
|
||||
### Scale Configuration
|
||||
|
||||
For Cloud Run, you can adjust scaling in `terraform/modules/cloud_run/main.tf`:
|
||||
```hcl
|
||||
metadata {
|
||||
annotations = {
|
||||
"autoscaling.knative.dev/maxScale" = "10" # Max instances
|
||||
"autoscaling.knative.dev/minScale" = "1" # Min instances
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Updates
|
||||
|
||||
To update the application:
|
||||
|
||||
1. **Cloud Run**: Build and push new Docker image, then update the `container_image` variable
|
||||
2. **App Engine**: Update source code, create new zip, and redeploy
|
||||
|
||||
## Costs
|
||||
|
||||
### Cloud Run (Recommended)
|
||||
- **Free tier**: 180,000 vCPU-seconds, 360,000 GiB-seconds memory per month
|
||||
- **Beyond free tier**: ~$0.000024 per vCPU-second, $0.0000025 per GiB-second
|
||||
- **Typical cost**: $5-20/month for low traffic client portal
|
||||
|
||||
### App Engine
|
||||
- **Free tier**: 28 instance-hours per day
|
||||
- **Beyond free tier**: ~$0.05 per instance-hour
|
||||
- **Typical cost**: $10-50/month depending on traffic
|
||||
|
||||
### Firebase
|
||||
- **Spark plan**: Free for most use cases
|
||||
- **Blaze plan**: Pay-as-you-go for high usage
|
||||
- **Typical cost**: $0-25/month depending on authentication usage
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Service Account**: Minimal permissions granted (Firestore and Firebase Admin)
|
||||
2. **Secret Management**: Sensitive data stored in Secret Manager
|
||||
3. **HTTPS**: All traffic encrypted with TLS
|
||||
4. **Authentication**: Firebase ID tokens verified server-side
|
||||
5. **Access Control**: User access controlled through Firestore profiles
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Build failures**: Check Dockerfile and requirements.txt
|
||||
2. **Permission errors**: Verify service account has correct IAM roles
|
||||
3. **Firebase connection**: Ensure service account key is properly formatted
|
||||
4. **CORS errors**: Configure Firebase Auth domain correctly
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Check Cloud Run service status
|
||||
gcloud run services describe rothbard-portal-service
|
||||
|
||||
# Test service locally
|
||||
docker run -p 5000:5000 gcr.io/$PROJECT_ID/rothbard-portal:latest
|
||||
|
||||
# Check Terraform state
|
||||
terraform show
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues with:
|
||||
- **Terraform deployment**: Check Terraform logs and state
|
||||
- **Cloud Run**: View Cloud Run logs in Google Console
|
||||
- **Firebase**: Check Firebase Console for configuration issues
|
||||
- **Application**: Review Flask application logs
|
||||
@@ -1,29 +0,0 @@
|
||||
# App Engine configuration file
|
||||
runtime: python311
|
||||
|
||||
instance_class: F1
|
||||
automatic_scaling:
|
||||
min_idle_instances: automatic
|
||||
max_idle_instances: 1
|
||||
min_pending_latency: automatic
|
||||
max_pending_latency: automatic
|
||||
|
||||
# Environment variables
|
||||
env_variables:
|
||||
FLASK_ENV: production
|
||||
PORT: 8080
|
||||
|
||||
# Handlers
|
||||
handlers:
|
||||
- url: /.*
|
||||
script: auto
|
||||
secure: always
|
||||
|
||||
# Health check
|
||||
health_check:
|
||||
enable_health_check: true
|
||||
check_path: "/"
|
||||
|
||||
# Runtime configuration
|
||||
runtime_config:
|
||||
python_version: 3
|
||||
@@ -1,229 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Rothbard Law Group Deployment Script
|
||||
# This script automates the deployment process for Cloud Run
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
PROJECT_ID=""
|
||||
DOMAIN_NAME=""
|
||||
HOSTING_OPTION="cloud_run"
|
||||
|
||||
# Helper functions
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
log_info "Checking prerequisites..."
|
||||
|
||||
# Check gcloud
|
||||
if ! command -v gcloud &> /dev/null; then
|
||||
log_error "gcloud is not installed. Please install Google Cloud SDK."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check terraform
|
||||
if ! command -v terraform &> /dev/null; then
|
||||
log_error "terraform is not installed. Please install Terraform."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check docker (for Cloud Run)
|
||||
if [ "$HOSTING_OPTION" = "cloud_run" ] && ! command -v docker &> /dev/null; then
|
||||
log_error "docker is not installed. Please install Docker for Cloud Run deployment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Prerequisites check passed!"
|
||||
}
|
||||
|
||||
# Setup project
|
||||
setup_project() {
|
||||
log_info "Setting up Google Cloud project..."
|
||||
|
||||
if [ -z "$PROJECT_ID" ]; then
|
||||
read -p "Enter your GCP Project ID: " PROJECT_ID
|
||||
fi
|
||||
|
||||
# Set the project
|
||||
gcloud config set project "$PROJECT_ID"
|
||||
|
||||
# Enable required APIs
|
||||
log_info "Enabling required APIs..."
|
||||
gcloud services enable run.googleapis.com
|
||||
gcloud services enable cloudbuild.googleapis.com
|
||||
gcloud services enable firestore.googleapis.com
|
||||
gcloud services enable firebase.googleapis.com
|
||||
gcloud services enable secretmanager.googleapis.com
|
||||
|
||||
log_info "Project setup completed!"
|
||||
}
|
||||
|
||||
# Build and push Docker image (Cloud Run)
|
||||
build_and_push_image() {
|
||||
if [ "$HOSTING_OPTION" != "cloud_run" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Building Docker image..."
|
||||
|
||||
# Build the image
|
||||
docker build -t gcr.io/$PROJECT_ID/rothbard-portal:latest .
|
||||
|
||||
log_info "Pushing Docker image to Google Container Registry..."
|
||||
|
||||
# Configure Docker to use gcloud as a credential helper
|
||||
gcloud auth configure-docker
|
||||
|
||||
# Push the image
|
||||
docker push gcr.io/$PROJECT_ID/rothbard-portal:latest
|
||||
|
||||
log_info "Docker image pushed successfully!"
|
||||
}
|
||||
|
||||
# Deploy infrastructure with Terraform
|
||||
deploy_infrastructure() {
|
||||
log_info "Deploying infrastructure with Terraform..."
|
||||
|
||||
cd terraform
|
||||
|
||||
# Create terraform.tfvars if it doesn't exist
|
||||
if [ ! -f "terraform.tfvars" ]; then
|
||||
log_warn "terraform.tfvars not found. Creating from example..."
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
|
||||
# Update with project ID
|
||||
sed -i "s/your-gcp-project-id/$PROJECT_ID/g" terraform.tfvars
|
||||
|
||||
if [ -n "$DOMAIN_NAME" ]; then
|
||||
sed -i "s/rothbard-portal.example.com/$DOMAIN_NAME/g" terraform.tfvars
|
||||
fi
|
||||
|
||||
log_warn "Please edit terraform/terraform.tfvars with your specific configuration before continuing."
|
||||
read -p "Press Enter to continue after editing..."
|
||||
fi
|
||||
|
||||
# Initialize Terraform
|
||||
terraform init
|
||||
|
||||
# Plan deployment
|
||||
log_info "Planning Terraform deployment..."
|
||||
terraform plan
|
||||
|
||||
# Apply deployment
|
||||
log_info "Applying Terraform configuration..."
|
||||
terraform apply -auto-approve
|
||||
|
||||
# Get the output URL
|
||||
APP_URL=$(terraform output -raw application_url)
|
||||
|
||||
cd ..
|
||||
|
||||
log_info "Infrastructure deployed successfully!"
|
||||
log_info "Application URL: $APP_URL"
|
||||
}
|
||||
|
||||
# Create service account key for local development
|
||||
create_service_account_key() {
|
||||
log_info "Creating service account key for development..."
|
||||
|
||||
SA_NAME="rothbard-flask-app"
|
||||
SA_EMAIL="$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com"
|
||||
|
||||
# Create service account if it doesn't exist
|
||||
if ! gcloud iam service-accounts describe "$SA_EMAIL" &> /dev/null; then
|
||||
gcloud iam service-accounts create "$SA_NAME" \
|
||||
--display-name="Rothbard Flask App Service Account"
|
||||
fi
|
||||
|
||||
# Grant necessary roles
|
||||
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
|
||||
--member="serviceAccount:$SA_EMAIL" \
|
||||
--role="roles/datastore.user"
|
||||
|
||||
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
|
||||
--member="serviceAccount:$SA_EMAIL" \
|
||||
--role="roles/firebase.admin"
|
||||
|
||||
# Create key
|
||||
gcloud iam service-accounts keys create ~/rothbard-service-account.json \
|
||||
--iam-account="$SA_EMAIL"
|
||||
|
||||
log_info "Service account key created at ~/rothbard-service-account.json"
|
||||
log_warn "Add this to your environment: export GOOGLE_APPLICATION_CREDENTIALS=~/rothbard-service-account.json"
|
||||
}
|
||||
|
||||
# Main deployment function
|
||||
main() {
|
||||
log_info "Starting Rothbard Law Group deployment..."
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-p|--project)
|
||||
PROJECT_ID="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--domain)
|
||||
DOMAIN_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-o|--option)
|
||||
HOSTING_OPTION="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo "Options:"
|
||||
echo " -p, --project PROJECT_ID GCP Project ID"
|
||||
echo " -d, --domain DOMAIN_NAME Domain name (optional)"
|
||||
echo " -o, --option HOSTING_OPTION Hosting option (cloud_run or app_engine)"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate hosting option
|
||||
if [[ ! "$HOSTING_OPTION" =~ ^(cloud_run|app_engine)$ ]]; then
|
||||
log_error "Invalid hosting option. Use 'cloud_run' or 'app_engine'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_prerequisites
|
||||
setup_project
|
||||
build_and_push_image
|
||||
deploy_infrastructure
|
||||
create_service_account_key
|
||||
|
||||
log_info "Deployment completed successfully!"
|
||||
log_info "Next steps:"
|
||||
log_info "1. Configure Firebase Authentication in the Firebase Console"
|
||||
log_info "2. Set up Firestore security rules"
|
||||
log_info "3. Enable user accounts in Firestore"
|
||||
log_info "4. Configure your custom domain (if applicable)"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
@@ -1,87 +0,0 @@
|
||||
|
||||
# Select hosting option based on variable
|
||||
module "hosting" {
|
||||
source = "./modules/cloud_run"
|
||||
|
||||
# Common variables
|
||||
app_name = "rothbard-portal"
|
||||
gcp_project_id = var.gcp_project_id
|
||||
gcp_region = var.gcp_region
|
||||
firebase_project_id = google_firebase_project.default.project
|
||||
flask_secret_key = var.flask_secret_key
|
||||
service_account_email = google_service_account.flask_app.email
|
||||
service_account_key_data = var.service_account_key_data
|
||||
|
||||
# Filevine credentials
|
||||
filevine_client_id = var.filevine_client_id
|
||||
filevine_client_secret = var.filevine_client_secret
|
||||
filevine_pat = var.filevine_pat
|
||||
filevine_org_id = var.filevine_org_id
|
||||
filevine_user_id = var.filevine_user_id
|
||||
|
||||
# Module-specific variables
|
||||
container_image = var.container_image
|
||||
}
|
||||
|
||||
# Additional variables for hosting options
|
||||
variable "flask_secret_key" {
|
||||
description = "Flask secret key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "service_account_key_data" {
|
||||
description = "Service account key JSON data"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "container_image" {
|
||||
description = "Docker image for Cloud Run deployment"
|
||||
type = string
|
||||
default = "gcr.io/your-project/rothbard-portal:latest"
|
||||
}
|
||||
|
||||
variable "app_source_zip_path" {
|
||||
description = "Path to App Engine source zip"
|
||||
type = string
|
||||
default = "./app-source.zip"
|
||||
}
|
||||
|
||||
|
||||
# Filevine credentials
|
||||
variable "filevine_client_id" {
|
||||
description = "Filevine client ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_secret" {
|
||||
description = "Filevine client secret"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_pat" {
|
||||
description = "Filevine personal access token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_org_id" {
|
||||
description = "Filevine organization ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_user_id" {
|
||||
description = "Filevine user ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# Output hosting-specific URLs
|
||||
output "application_url" {
|
||||
description = "URL of the deployed application"
|
||||
value = module.hosting.service_url
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "~> 6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
project = var.gcp_project_id
|
||||
region = var.gcp_region
|
||||
}
|
||||
|
||||
|
||||
# Firebase Project Setup
|
||||
resource "google_firebase_project" "default" {
|
||||
provider = google-beta
|
||||
project = var.gcp_project_id
|
||||
}
|
||||
|
||||
# Firebase Web App
|
||||
resource "google_firebase_web_app" "rothbard_portal" {
|
||||
provider = google-beta
|
||||
project = google_firebase_project.default.project
|
||||
display_name = "Rothbard Client Portal"
|
||||
|
||||
app_url = "https://${var.domain_name}"
|
||||
|
||||
# Handle OAuth redirect
|
||||
oauth_config {
|
||||
client_id = var.oauth_client_id
|
||||
client_secret = var.oauth_client_secret
|
||||
}
|
||||
}
|
||||
|
||||
# Firestore Database
|
||||
resource "google_firestore_database" "default" {
|
||||
provider = google-beta
|
||||
project = var.gcp_project_id
|
||||
name = "(default)"
|
||||
location_id = var.firestore_location
|
||||
type = "FIRESTORE_NATIVE"
|
||||
|
||||
delete_protection_state = "DISABLED"
|
||||
}
|
||||
|
||||
# Firebase Authentication - Complete Configuration
|
||||
resource "google_identitytoolkit_config" "default" {
|
||||
provider = google-beta
|
||||
project = var.gcp_project_id
|
||||
|
||||
sign_in_options {
|
||||
email {
|
||||
enabled = true
|
||||
password_required = true
|
||||
}
|
||||
|
||||
# Disable other providers for security
|
||||
phone {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
google {
|
||||
enabled = var.enable_google_signin
|
||||
}
|
||||
|
||||
facebook {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
apple {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
# Email configuration
|
||||
email {
|
||||
reset_password_template {
|
||||
from_email_address = var.auth_from_email
|
||||
from_display_name = var.auth_from_name
|
||||
reply_to = var.auth_reply_to
|
||||
subject = "Reset your Rothbard Law Group password"
|
||||
html = file("${path.module}/templates/reset_password.html")
|
||||
text = file("${path.module}/templates/reset_password.txt")
|
||||
}
|
||||
|
||||
email_verification_template {
|
||||
from_email_address = var.auth_from_email
|
||||
from_display_name = var.auth_from_name
|
||||
reply_to = var.auth_reply_to
|
||||
subject = "Verify your Rothbard Law Group account"
|
||||
html = file("${path.module}/templates/email_verification.html")
|
||||
text = file("${path.module}/templates/email_verification.txt")
|
||||
}
|
||||
}
|
||||
|
||||
# Security settings
|
||||
sign_in {
|
||||
allow_duplicate_emails = false
|
||||
}
|
||||
|
||||
# Multi-factor authentication (disabled for simplicity)
|
||||
multi_factor_auth {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
# Anonymous user access (disabled)
|
||||
anonymous {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
# Service Account for the Flask App
|
||||
resource "google_service_account" "flask_app" {
|
||||
account_id = "rothbard-flask-app"
|
||||
display_name = "Rothbard Flask App Service Account"
|
||||
}
|
||||
|
||||
# IAM permissions for the Flask App
|
||||
resource "google_project_iam_member" "firestore_access" {
|
||||
project = var.gcp_project_id
|
||||
role = "roles/datastore.user"
|
||||
member = "serviceAccount:${google_service_account.flask_app.email}"
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "firebase_admin" {
|
||||
project = var.gcp_project_id
|
||||
role = "roles/firebase.admin"
|
||||
member = "serviceAccount:${google_service_account.flask_app.email}"
|
||||
}
|
||||
|
||||
# Firestore Security Rules
|
||||
resource "google_firestore_security_policy" "default" {
|
||||
project = var.gcp_project_id
|
||||
policy = {
|
||||
rules = [
|
||||
{
|
||||
description = "Allow users to read/write their own profile"
|
||||
match = {
|
||||
collection = "users"
|
||||
document = "{userId}"
|
||||
}
|
||||
allow = [
|
||||
{
|
||||
resource = "read"
|
||||
condition = {
|
||||
name = "request.auth.uid == userId"
|
||||
}
|
||||
},
|
||||
{
|
||||
resource = "write"
|
||||
condition = {
|
||||
name = "request.auth.uid == userId"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
description = "Only authenticated users can access the database"
|
||||
match = {
|
||||
collection = "{collection=**}"
|
||||
}
|
||||
allow = [
|
||||
{
|
||||
resource = "read"
|
||||
condition = {
|
||||
name = "request.auth != null"
|
||||
}
|
||||
},
|
||||
{
|
||||
resource = "write"
|
||||
condition = {
|
||||
name = "request.auth != null"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Firebase Hosting (optional - for static assets)
|
||||
resource "google_firebase_hosting_site" "default" {
|
||||
provider = google-beta
|
||||
project = var.gcp_project_id
|
||||
site_id = "rothbard-portal"
|
||||
|
||||
# Default configuration for hosting
|
||||
config {
|
||||
public_root_dir = "public"
|
||||
headers = [
|
||||
{
|
||||
headers = ["Cache-Control: public, max-age=31536000"]
|
||||
glob = "**/*.@(jpg|jpeg|gif|png|svg|webp)"
|
||||
},
|
||||
{
|
||||
headers = ["Cache-Control: public, max-age=86400"]
|
||||
glob = "**/*.@(css|js)"
|
||||
}
|
||||
]
|
||||
redirects = [
|
||||
{
|
||||
status_code = 302
|
||||
path = "/login"
|
||||
location = "/login.html"
|
||||
}
|
||||
]
|
||||
rewrites = [
|
||||
{
|
||||
glob = "**"
|
||||
path = "/index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Output important values
|
||||
output "firebase_web_app_id" {
|
||||
description = "Firebase Web App ID"
|
||||
value = google_firebase_web_app.rothbard_portal.app_id
|
||||
}
|
||||
|
||||
output "firebase_project_id" {
|
||||
description = "Firebase Project ID"
|
||||
value = google_firebase_project.default.project
|
||||
}
|
||||
|
||||
output "service_account_email" {
|
||||
description = "Service account email for Flask app"
|
||||
value = google_service_account.flask_app.email
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
# Enable App Engine Admin API
|
||||
resource "google_project_service" "appengine" {
|
||||
project = var.gcp_project_id
|
||||
service = "appengine.googleapis.com"
|
||||
}
|
||||
|
||||
# App Engine Application
|
||||
resource "google_app_engine_application" "app" {
|
||||
project = var.gcp_project_id
|
||||
location_id = var.gcp_region
|
||||
depends_on = [google_project_service.appengine]
|
||||
}
|
||||
|
||||
# App Engine Service for Flask app
|
||||
resource "google_app_engine_standard_app_version" "flask_app" {
|
||||
project = var.gcp_project_id
|
||||
service = "default"
|
||||
version_id = "${var.app_name}-v1"
|
||||
|
||||
runtime = "python311"
|
||||
|
||||
entrypoint {
|
||||
command = "gunicorn -b :$PORT app:app"
|
||||
}
|
||||
|
||||
deployment {
|
||||
zip {
|
||||
source_url = google_storage_bucket_object.app_source_zip.output_uri
|
||||
}
|
||||
}
|
||||
|
||||
env_variables = {
|
||||
FLASK_SECRET_KEY = var.flask_secret_key
|
||||
FIREBASE_PROJECT_ID = var.firebase_project_id
|
||||
GOOGLE_APPLICATION_CREDENTIALS = "/etc/secrets/service-account.json"
|
||||
FILEVINE_CLIENT_ID = var.filevine_client_id
|
||||
FILEVINE_CLIENT_SECRET = var.filevine_client_secret
|
||||
FILEVINE_PERSONAL_ACCESS_TOKEN = var.filevine_pat
|
||||
FILEVINE_ORG_ID = var.filevine_org_id
|
||||
FILEVINE_USER_ID = var.filevine_user_id
|
||||
}
|
||||
|
||||
# Service account
|
||||
service_account = var.service_account_email
|
||||
|
||||
# Resources
|
||||
resources {
|
||||
cpu = 1
|
||||
memory_gb = 0.5
|
||||
disk_gb = 0.5
|
||||
}
|
||||
|
||||
# Automatic scaling
|
||||
automatic_scaling {
|
||||
min_idle_instances = 0
|
||||
max_idle_instances = 1
|
||||
min_pending_latency = "automatic"
|
||||
max_pending_latency = "automatic"
|
||||
max_concurrent_requests = 80
|
||||
}
|
||||
|
||||
# Health check
|
||||
health_check {
|
||||
enable_health_check = true
|
||||
check_path = "/"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_storage_bucket_object.app_source_zip,
|
||||
google_secret_manager_secret_version.service_account_key
|
||||
]
|
||||
}
|
||||
|
||||
# Make App Engine service publicly accessible
|
||||
resource "google_app_engine_firewall_rule" "allow_all" {
|
||||
project = var.gcp_project_id
|
||||
action = "ALLOW"
|
||||
priority = "1"
|
||||
|
||||
source_range = "*"
|
||||
}
|
||||
|
||||
# Cloud Storage bucket for app source code
|
||||
resource "google_storage_bucket" "app_source" {
|
||||
name = "${var.app_name}-source-${var.gcp_project_id}"
|
||||
location = var.gcp_region
|
||||
force_destroy = true
|
||||
|
||||
uniform_bucket_level_access = true
|
||||
}
|
||||
|
||||
# Upload app source code
|
||||
resource "google_storage_bucket_object" "app_source_zip" {
|
||||
name = "app-source.zip"
|
||||
bucket = google_storage_bucket.app_source.name
|
||||
source = var.app_source_zip_path
|
||||
}
|
||||
|
||||
# Store service account key in Secret Manager
|
||||
resource "google_secret_manager_secret" "service_account_key" {
|
||||
project = var.gcp_project_id
|
||||
secret_id = "${var.app_name}-service-account-key"
|
||||
|
||||
replication {
|
||||
automatic = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_version" "service_account_key" {
|
||||
secret = google_secret_manager_secret.service_account_key.id
|
||||
secret_data = var.service_account_key_data
|
||||
}
|
||||
|
||||
# Output the app URL
|
||||
output "app_url" {
|
||||
description = "App Engine application URL"
|
||||
value = "https://${google_app_engine_application.app.default_hostname}"
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
variable "app_name" {
|
||||
description = "Name of the application"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_project_id" {
|
||||
description = "GCP Project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_region" {
|
||||
description = "GCP region"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "app_source_zip_path" {
|
||||
description = "Path to the app source code zip file"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "firebase_project_id" {
|
||||
description = "Firebase project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "flask_secret_key" {
|
||||
description = "Flask secret key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "service_account_email" {
|
||||
description = "Service account email for the App Engine service"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "service_account_key_data" {
|
||||
description = "Service account key JSON data"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_id" {
|
||||
description = "Filevine client ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_secret" {
|
||||
description = "Filevine client secret"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_pat" {
|
||||
description = "Filevine personal access token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_org_id" {
|
||||
description = "Filevine organization ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_user_id" {
|
||||
description = "Filevine user ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
# Cloud Run Service for Flask App
|
||||
resource "google_cloud_run_service" "flask_app" {
|
||||
name = "${var.app_name}-service"
|
||||
location = var.gcp_region
|
||||
|
||||
template {
|
||||
spec {
|
||||
containers {
|
||||
image = var.container_image
|
||||
|
||||
# Environment variables for the Flask app
|
||||
env {
|
||||
name = "FLASK_SECRET_KEY"
|
||||
value = var.flask_secret_key
|
||||
}
|
||||
|
||||
env {
|
||||
name = "FIREBASE_PROJECT_ID"
|
||||
value = var.firebase_project_id
|
||||
}
|
||||
|
||||
env {
|
||||
name = "GOOGLE_APPLICATION_CREDENTIALS"
|
||||
value = "/etc/secrets/service-account.json"
|
||||
}
|
||||
|
||||
# Filevine API credentials
|
||||
env {
|
||||
name = "FILEVINE_CLIENT_ID"
|
||||
value = var.filevine_client_id
|
||||
}
|
||||
|
||||
env {
|
||||
name = "FILEVINE_CLIENT_SECRET"
|
||||
value = var.filevine_client_secret
|
||||
}
|
||||
|
||||
env {
|
||||
name = "FILEVINE_PERSONAL_ACCESS_TOKEN"
|
||||
value = var.filevine_pat
|
||||
}
|
||||
|
||||
env {
|
||||
name = "FILEVINE_ORG_ID"
|
||||
value = var.filevine_org_id
|
||||
}
|
||||
|
||||
env {
|
||||
name = "FILEVINE_USER_ID"
|
||||
value = var.filevine_user_id
|
||||
}
|
||||
|
||||
# Memory and CPU limits
|
||||
resources {
|
||||
limits = {
|
||||
cpu = "1000m"
|
||||
memory = "512Mi"
|
||||
}
|
||||
}
|
||||
|
||||
# Mount service account key
|
||||
volume_mount {
|
||||
name = "service-account-key"
|
||||
mount_path = "/etc/secrets"
|
||||
read_only = true
|
||||
}
|
||||
}
|
||||
|
||||
# Service account for the container
|
||||
service_account_name = var.service_account_email
|
||||
|
||||
# Volumes
|
||||
volumes {
|
||||
name = "service-account-key"
|
||||
secret {
|
||||
secret_name = google_secret_manager_secret.service_account_key.secret_id
|
||||
items {
|
||||
key = "latest"
|
||||
path = "service-account.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Allow unauthenticated access
|
||||
container_concurrency = 100
|
||||
timeout_seconds = 300
|
||||
}
|
||||
|
||||
# Traffic settings
|
||||
metadata {
|
||||
annotations = {
|
||||
"autoscaling.knative.dev/maxScale" = "10"
|
||||
"autoscaling.knative.dev/minScale" = "1"
|
||||
"run.googleapis.com/ingress" = "all"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traffic {
|
||||
percent = 100
|
||||
latest_revision = true
|
||||
}
|
||||
|
||||
depends_on = [google_secret_manager_secret_version.service_account_key]
|
||||
}
|
||||
|
||||
# Make Cloud Run service publicly accessible
|
||||
resource "google_cloud_run_service_iam_member" "public" {
|
||||
location = google_cloud_run_service.flask_app.location
|
||||
project = google_cloud_run_service.flask_app.project
|
||||
service = google_cloud_run_service.flask_app.name
|
||||
role = "roles/run.invoker"
|
||||
member = "allUsers"
|
||||
}
|
||||
|
||||
# Store service account key in Secret Manager
|
||||
resource "google_secret_manager_secret" "service_account_key" {
|
||||
project = var.gcp_project_id
|
||||
secret_id = "${var.app_name}-service-account-key"
|
||||
|
||||
replication {
|
||||
automatic = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_version" "service_account_key" {
|
||||
secret = google_secret_manager_secret.service_account_key.id
|
||||
secret_data = var.service_account_key_data
|
||||
}
|
||||
|
||||
# Cloud Storage bucket for container storage (if needed)
|
||||
resource "google_storage_bucket" "app_storage" {
|
||||
name = "${var.app_name}-storage-${var.gcp_project_id}"
|
||||
location = var.gcp_region
|
||||
force_destroy = true
|
||||
|
||||
uniform_bucket_level_access = true
|
||||
}
|
||||
|
||||
# Output the service URL
|
||||
output "service_url" {
|
||||
description = "Cloud Run service URL"
|
||||
value = google_cloud_run_service.flask_app.status[0].url
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
variable "app_name" {
|
||||
description = "Name of the application"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_project_id" {
|
||||
description = "GCP Project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_region" {
|
||||
description = "GCP region"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "container_image" {
|
||||
description = "Docker image for the Flask app"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "firebase_project_id" {
|
||||
description = "Firebase project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "flask_secret_key" {
|
||||
description = "Flask secret key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "service_account_email" {
|
||||
description = "Service account email for the Cloud Run service"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "service_account_key_data" {
|
||||
description = "Service account key JSON data"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_id" {
|
||||
description = "Filevine client ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_secret" {
|
||||
description = "Filevine client secret"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_pat" {
|
||||
description = "Filevine personal access token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_org_id" {
|
||||
description = "Filevine organization ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_user_id" {
|
||||
description = "Filevine user ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verify Your Email - Rothbard Law Group</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
background: #1a365d;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
padding: 30px 20px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background: #2c5282;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.footer {
|
||||
background: #e2e8f0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
.welcome-box {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #2c5282;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Rothbard Law Group</h1>
|
||||
<p>Client Portal</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2>Welcome to the Rothbard Law Group Client Portal</h2>
|
||||
|
||||
<div class="welcome-box">
|
||||
<p><strong>Thank you for signing up!</strong></p>
|
||||
<p>You're just one step away from accessing your case information securely.</p>
|
||||
</div>
|
||||
|
||||
<p>Please verify your email address to complete your registration:</p>
|
||||
|
||||
<p><a href="{{ verificationLink }}" class="button">Verify Email Address</a></p>
|
||||
|
||||
<p>Or copy and paste this link into your browser:</p>
|
||||
<p>{{ verificationLink }}</p>
|
||||
|
||||
<p><strong>What happens next?</strong></p>
|
||||
<ul>
|
||||
<li>Once verified, you'll have full access to the client portal</li>
|
||||
<li>You can view your case information and documents</li>
|
||||
<li>Communicate securely with our legal team</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Important:</strong></p>
|
||||
<ul>
|
||||
<li>This verification link will expire in 24 hours</li>
|
||||
<li>If you didn't create an account, please ignore this email</li>
|
||||
<li>Your account information is kept secure and confidential</li>
|
||||
</ul>
|
||||
|
||||
<p>If you have any questions or need assistance, please contact our support team.</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2024 Rothbard Law Group. All rights reserved.</p>
|
||||
<p>This is an automated message. Please do not reply to this email.</p>
|
||||
<p>Rothbard Law Group | 123 Legal Street | City, State 12345 | (555) 123-4567</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,31 +0,0 @@
|
||||
Rothbard Law Group - Email Verification
|
||||
|
||||
Welcome to Rothbard Law Group Client Portal!
|
||||
|
||||
Thank you for signing up for our client portal. You're just one step away from accessing your case information securely.
|
||||
|
||||
Please verify your email address to complete your registration:
|
||||
{{ verificationLink }}
|
||||
|
||||
If the button doesn't work, you can copy and paste this link into your browser.
|
||||
|
||||
What happens next?
|
||||
- Once verified, you'll have full access to the client portal
|
||||
- You can view your case information and documents
|
||||
- Communicate securely with our legal team
|
||||
|
||||
Important Information:
|
||||
- This verification link will expire in 24 hours
|
||||
- If you didn't create an account, please ignore this email
|
||||
- Your account information is kept secure and confidential
|
||||
|
||||
If you have any questions or need assistance, please contact our support team.
|
||||
|
||||
Thank you for choosing Rothbard Law Group.
|
||||
|
||||
---
|
||||
Rothbard Law Group
|
||||
123 Legal Street
|
||||
City, State 12345
|
||||
(555) 123-4567
|
||||
support@rothbardlaw.com
|
||||
@@ -1,78 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Reset Your Password - Rothbard Law Group</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
background: #1a365d;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
padding: 30px 20px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background: #2c5282;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.footer {
|
||||
background: #e2e8f0;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Rothbard Law Group</h1>
|
||||
<p>Client Portal</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2>Reset Your Password</h2>
|
||||
|
||||
<p>Hello,</p>
|
||||
|
||||
<p>We received a request to reset the password for your Rothbard Law Group client portal account. Click the button below to reset your password:</p>
|
||||
|
||||
<p><a href="{{ resetLink }}" class="button">Reset Password</a></p>
|
||||
|
||||
<p>Or copy and paste this link into your browser:</p>
|
||||
<p>{{ resetLink }}</p>
|
||||
|
||||
<p><strong>Important:</strong></p>
|
||||
<ul>
|
||||
<li>This link will expire in 24 hours</li>
|
||||
<li>If you didn't request a password reset, please ignore this email</li>
|
||||
<li>For security reasons, never share this link with anyone</li>
|
||||
</ul>
|
||||
|
||||
<p>If you have any questions or need assistance, please contact our support team.</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2024 Rothbard Law Group. All rights reserved.</p>
|
||||
<p>This is an automated message. Please do not reply to this email.</p>
|
||||
<p>Rothbard Law Group | 123 Legal Street | City, State 12345 | (555) 123-4567</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
Rothbard Law Group - Password Reset
|
||||
|
||||
Hello,
|
||||
|
||||
We received a request to reset your password for your Rothbard Law Group client portal account.
|
||||
|
||||
To reset your password, please visit this link:
|
||||
{{ resetLink }}
|
||||
|
||||
If the button doesn't work, you can copy and paste the link into your browser.
|
||||
|
||||
Important Information:
|
||||
- This link will expire in 24 hours
|
||||
- If you didn't request a password reset, please ignore this email
|
||||
- For security reasons, never share this link with anyone
|
||||
|
||||
If you have any questions or need assistance, please contact our support team.
|
||||
|
||||
Thank you,
|
||||
Rothbard Law Group
|
||||
|
||||
---
|
||||
Rothbard Law Group
|
||||
123 Legal Street
|
||||
City, State 12345
|
||||
(555) 123-4567
|
||||
support@rothbardlaw.com
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copy this file to terraform.tfvars and fill in your values
|
||||
gcp_project_id = "811764775573"
|
||||
domain_name = "rothbard.yourdomain.com"
|
||||
|
||||
# Optional: Override defaults
|
||||
gcp_region = "us-central1"
|
||||
firestore_location = "us-central1"
|
||||
hosting_option = "cloud_run" # Options: cloud_run, app_engine, gcs
|
||||
environment = "staging"
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copy this file to terraform.tfvars and fill in your values
|
||||
gcp_project_id = "your-gcp-project-id"
|
||||
domain_name = "rothbard.yourdomain.com"
|
||||
|
||||
# Optional: Override defaults
|
||||
# gcp_region = "us-central1"
|
||||
# firestore_location = "us-central1"
|
||||
# hosting_option = "cloud_run" # Options: cloud_run, app_engine, gcs
|
||||
# environment = "production"
|
||||
@@ -1,76 +0,0 @@
|
||||
variable "gcp_project_id" {
|
||||
description = "GCP Project ID for the deployment"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_region" {
|
||||
description = "GCP region for resources"
|
||||
type = string
|
||||
default = "us-central1"
|
||||
}
|
||||
|
||||
variable "firestore_location" {
|
||||
description = "Location for Firestore database"
|
||||
type = string
|
||||
default = "us-central1"
|
||||
}
|
||||
|
||||
variable "domain_name" {
|
||||
description = "Domain name for the application"
|
||||
type = string
|
||||
default = "rothbard-portal.example.com"
|
||||
}
|
||||
|
||||
variable "oauth_client_id" {
|
||||
description = "OAuth client ID for Firebase"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "oauth_client_secret" {
|
||||
description = "OAuth client secret for Firebase"
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "hosting_option" {
|
||||
description = "Hosting option for the Flask app"
|
||||
type = string
|
||||
default = "cloud_run"
|
||||
validation {
|
||||
condition = contains(["cloud_run", "app_engine"], var.hosting_option)
|
||||
error_message = "The hosting_option must be one of: cloud_run, app_engine."
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment tag"
|
||||
type = string
|
||||
default = "production"
|
||||
}
|
||||
|
||||
# Firebase Authentication Configuration
|
||||
variable "enable_google_signin" {
|
||||
description = "Enable Google Sign-In authentication provider"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "auth_from_email" {
|
||||
description = "From email address for authentication emails"
|
||||
type = string
|
||||
default = "noreply@rothbardlaw.com"
|
||||
}
|
||||
|
||||
variable "auth_from_name" {
|
||||
description = "From display name for authentication emails"
|
||||
type = string
|
||||
default = "Rothbard Law Group"
|
||||
}
|
||||
|
||||
variable "auth_reply_to" {
|
||||
description = "Reply-to email address for authentication emails"
|
||||
type = string
|
||||
default = "support@rothbardlaw.com"
|
||||
}
|
||||
Reference in New Issue
Block a user