UnTerraform

This commit is contained in:
2025-10-30 20:26:23 -07:00
parent f0c1edfaea
commit 5eff7f39a4
19 changed files with 0 additions and 1934 deletions

View File

@@ -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",
]
}

View File

@@ -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"]

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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 "$@"

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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}"
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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>&copy; 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>

View File

@@ -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

View File

@@ -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>&copy; 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>

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"
}