deploy_infrastructure.pyโข9.11 kB
#!/usr/bin/env python3
"""
Deploy AWS infrastructure for TimeLooker MCP Server using CDK.
"""
import os
import sys
import subprocess
import json
from pathlib import Path
def run_command(command, cwd=None):
"""Run a shell command and return the result."""
print(f"Running: {command}")
result = subprocess.run(command, shell=True, cwd=cwd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error: {result.stderr}")
sys.exit(1)
return result.stdout.strip()
def get_default_email_template():
"""Default HTML email template for notifications."""
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TimeLooker Notification</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #2c3e50; color: white; padding: 20px; text-align: center; border-radius: 5px 5px 0 0; }
.content { background: #f8f9fa; padding: 20px; border: 1px solid #dee2e6; }
.footer { background: #6c757d; color: white; padding: 15px; text-align: center; border-radius: 0 0 5px 5px; font-size: 0.9em; }
.item { background: white; margin: 10px 0; padding: 15px; border-left: 4px solid #007bff; border-radius: 3px; }
.item h3 { margin: 0 0 10px 0; color: #007bff; }
.item .meta { color: #6c757d; font-size: 0.9em; margin: 5px 0; }
.item .description { margin: 10px 0; }
.item .url { word-break: break-all; }
</style>
</head>
<body>
<div class="header">
<h1>๐ TimeLooker Notification</h1>
<p>New items found for your monitoring task</p>
</div>
<div class="content">
<h2>Task: {{task_description}}</h2>
<p><strong>Time:</strong> {{timestamp}}</p>
<p><strong>New items found:</strong> {{new_items_count}}</p>
<h3>New Items:</h3>
{{#new_items}}
<div class="item">
<h3>{{name}}</h3>
<div class="meta">
<strong>Source:</strong> {{source}} |
<strong>Location:</strong> {{location}}
</div>
{{#description}}
<div class="description">{{description}}</div>
{{/description}}
{{#url}}
<div class="meta">
<strong>URL:</strong> <a href="{{url}}" target="_blank">{{url}}</a>
</div>
{{/url}}
{{#additional_info}}
<div class="meta">
<strong>Additional Info:</strong> {{additional_info}}
</div>
{{/additional_info}}
</div>
{{/new_items}}
</div>
<div class="footer">
<p>This is an automated message from TimeLooker MCP Server</p>
<p>Powered by AWS Lambda, RDS, and SES</p>
</div>
</body>
</html>"""
def main():
"""Deploy the CDK infrastructure."""
project_root = Path(__file__).parent.parent
infrastructure_dir = project_root / "infrastructure"
print("๐ Deploying TimeLooker AWS Infrastructure...")
# Check if AWS CLI is configured
try:
run_command("aws sts get-caller-identity")
print("โ
AWS CLI is configured")
except:
print("โ AWS CLI not configured. Please run 'aws configure' first.")
sys.exit(1)
# Check if CDK is installed
try:
run_command("cdk --version")
print("โ
AWS CDK is available")
except:
print("โ AWS CDK not installed. Please install with 'npm install -g aws-cdk'")
sys.exit(1)
# Install CDK dependencies
print("\n๐ฆ Installing CDK dependencies...")
run_command("pip install -r requirements.txt", cwd=infrastructure_dir)
# Bootstrap CDK (if needed)
print("\n๐๏ธ Bootstrapping CDK...")
try:
run_command("cdk bootstrap --region eu-west-2", cwd=infrastructure_dir)
print("โ
CDK bootstrap completed")
except:
print("โน๏ธ CDK bootstrap may have already been done")
# Deploy the stack
print("\n๐ Deploying TimeLooker stack...")
# Set environment variable to silence Node.js version warning
env = os.environ.copy()
env['JSII_SILENCE_WARNING_UNTESTED_NODE_VERSION'] = '1'
result = subprocess.run(
"cdk deploy --require-approval never --outputs-file outputs.json",
shell=True,
cwd=infrastructure_dir,
capture_output=True,
text=True,
env=env
)
if result.returncode != 0:
print(f"Error: {result.stderr}")
sys.exit(1)
deploy_output = result.stdout.strip()
# Read outputs
outputs_file = infrastructure_dir / "outputs.json"
if outputs_file.exists():
with open(outputs_file) as f:
outputs = json.load(f)
stack_outputs = outputs.get("TimeLookerStack", {})
print("\nโ
Infrastructure deployed successfully!")
print("\n๐ Stack Outputs:")
for key, value in stack_outputs.items():
print(f" {key}: {value}")
# Upload email template to S3
bucket_name = stack_outputs.get('EmailTemplatesBucket', '')
if bucket_name:
print(f"\n๐ง Uploading email template to S3 bucket: {bucket_name}")
try:
# Create temporary email template file
template_content = get_default_email_template()
template_file = project_root / "temp_email_template.html"
with open(template_file, 'w') as f:
f.write(template_content)
# Upload to S3
run_command(f"aws s3 cp {template_file} s3://{bucket_name}/notification_template.html")
print("โ
Email template uploaded successfully")
# Clean up temp file
template_file.unlink()
except Exception as e:
print(f"โ ๏ธ Failed to upload email template: {e}")
print("You can upload it manually later")
# Create .env.aws file with actual values
env_file = project_root / ".env.aws"
print(f"\n๐ Creating {env_file}...")
env_content = f"""# AWS Cloud Deployment Configuration for TimeLooker MCP Server
# Generated automatically by deploy_infrastructure.py
# Deployment Mode
DEPLOY_TO_CLOUD=true
# AWS Infrastructure - All credentials retrieved from AWS Secrets Manager
# AWS_REGION is automatically detected from AWS CLI/environment
DATABASE_SECRET_ARN={stack_outputs.get('DatabaseSecretArn', '')}
OPENAI_SECRET_ARN={stack_outputs.get('OpenAISecretArn', '')}
X402_SECRET_ARN={stack_outputs.get('X402SecretArn', '')}
LAMBDA_EXECUTION_ROLE_ARN={stack_outputs.get('LambdaExecutionRoleArn', '')}
EMAIL_TEMPLATES_BUCKET={stack_outputs.get('EmailTemplatesBucket', '')}
# Database Configuration - Retrieved automatically from DATABASE_SECRET_ARN
# DATABASE_URL is set automatically from AWS Secrets Manager
# Database endpoint: {stack_outputs.get('DatabaseEndpoint', '')}:{stack_outputs.get('DatabasePort', '5432')}/{stack_outputs.get('DatabaseName', 'timelooker')}
# x402 Payment Configuration - Retrieved automatically from X402_SECRET_ARN
# PRIVATE_KEY is set automatically from AWS Secrets Manager
PAY_TO_ADDRESS=0x671cE47E4F38051ba3A990Ba306E2885C2Fe4102
# API Configuration
TASK_MANAGER_API_URL=http://localhost:8000
# Email Configuration
DEFAULT_SENDER_EMAIL=fortnightlydevs@gmail.com
# Logging
LOG_LEVEL=INFO
# Note: OPENAI_API_KEY, DATABASE_URL, and PRIVATE_KEY are automatically
# retrieved from AWS Secrets Manager when DEPLOY_TO_CLOUD=true
"""
with open(env_file, 'w') as f:
f.write(env_content)
print(f"โ
Created {env_file}")
# Post-deployment instructions
print("\n๐ง Post-Deployment Setup Required:")
print("1. Update the secrets in AWS Secrets Manager:")
print(f" - OpenAI API Key: aws secretsmanager update-secret --secret-id {stack_outputs.get('OpenAISecretArn', '')} --secret-string '{{\"api_key\":\"your_openai_key_here\"}}'")
print(f" - X402 Private Key: aws secretsmanager update-secret --secret-id {stack_outputs.get('X402SecretArn', '')} --secret-string '{{\"private_key\":\"your_private_key_here\"}}'")
print("\n2. Verify your email address in SES:")
print(f" - Email Identity: {stack_outputs.get('SESEmailIdentity', '')}")
print(" - Go to AWS Console > SES > Verified identities > Verify the email")
print("\n3. Update .env.aws with your private key and sender email")
print("\n4. Initialize the database:")
print(" python scripts/init_db.py --reset --sample")
else:
print("โ ๏ธ No outputs file found, but deployment may have succeeded")
print("\n๐ Infrastructure deployment complete!")
if __name__ == "__main__":
main()