Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .env.orig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Gemini API key for AI features
GEMINI_API_KEY=AIzaSyD9tBXMN5JcXAGpmN8a0jNscbWTDToXLWU

# Super Admin Authorization for OAuth
# Option 1: Specific email addresses (comma-separated)
SUPER_ADMIN_EMAILS=bokelley@scope3.com

# Option 2: Email domains (anyone with these domains, comma-separated)
SUPER_ADMIN_DOMAINS=

# Note: You can use both SUPER_ADMIN_EMAILS and SUPER_ADMIN_DOMAINS together

# Google OAuth Configuration
# The OAuth client credentials file must be present at:
# client_secret_1002878641006-1balq5ha6fq3fq58bsho78gst2da4e6u.apps.googleusercontent.com.json
#
# To get your own OAuth credentials:
# 1. Go to https://console.cloud.google.com/
# 2. Create a new project or select existing
# 3. Enable Google+ API
# 4. Create OAuth 2.0 credentials (Web application)
# 5. Add redirect URI: http://localhost:8001/auth/google/callback
# 6. Download the JSON file and place it in the project root
107 changes: 107 additions & 0 deletions CONDUCTOR_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Conductor Workspace Setup Guide

This guide documents the setup process for Conductor workspace copies to ensure they work correctly with unique ports and configurations.

## Port Configuration

Each Conductor workspace requires unique ports to avoid conflicts. The ports are configured in the `.env` file:

```bash
# Server Ports (unique for this Conductor workspace)
POSTGRES_PORT=5433 # Default: 5432
ADCP_SALES_PORT=8081 # Default: 8080
ADMIN_UI_PORT=8002 # Default: 8001
DATABASE_URL=postgresql://adcp_user:secure_password_change_me@localhost:5433/adcp
```

## Setup Steps for New Conductor Workspaces

1. **Fix Database Initialization Bug**
- The `database.py` file has indentation issues where `principals_data` and `products_data` are only defined inside an `if` block but used outside it
- Ensure the `for p in principals_data:` and `for p in products_data:` loops are properly indented inside the `if` block

2. **Copy Required Files from Root Workspace**
```bash
# Copy OAuth credentials
cp ../../client_secret_*.json .

# Copy .env file and modify ports
cp ../../.env .
```

3. **Configure Unique Ports**
- Edit `.env` to set unique ports for this workspace
- Suggested port allocation scheme:
- Workspace 1: PostgreSQL 5433, MCP 8081, Admin 8002
- Workspace 2: PostgreSQL 5434, MCP 8082, Admin 8003
- etc.

4. **Docker Compose Configuration**
- The `docker-compose.yml` file now uses environment variables for ports
- No need to edit docker-compose.yml directly, just set the `.env` values

5. **Start Services**
```bash
docker-compose build
docker-compose up -d
```

## Verification

Check that all services are running:
```bash
docker-compose ps
```

Test endpoints:
- MCP Server: `curl http://localhost:8081/mcp/`
- Admin UI: `http://localhost:8002/`
- PostgreSQL: `psql -h localhost -p 5433 -U adcp_user -d adcp`

## Important Notes

1. **OAuth Redirect URI**: If using Google OAuth, ensure the redirect URI in Google Console matches your Admin UI port (e.g., `http://localhost:8002/auth/google/callback`)

2. **Database URL**: The DATABASE_URL in `.env` must match the POSTGRES_PORT setting

3. **Port Consolidation**: All port definitions are now in `.env` to avoid duplication and make configuration easier

## Automated Setup Script

Consider creating a script to automate this process:

```bash
#!/bin/bash
# setup_conductor_workspace.sh

WORKSPACE_NUM=$1
BASE_DIR="../.."

# Calculate ports based on workspace number
POSTGRES_PORT=$((5432 + $WORKSPACE_NUM))
ADCP_PORT=$((8080 + $WORKSPACE_NUM))
ADMIN_PORT=$((8001 + $WORKSPACE_NUM))

# Copy required files
cp $BASE_DIR/client_secret_*.json .
cp $BASE_DIR/.env .

# Update .env with unique ports
cat >> .env << EOF

# Server Ports (unique for Conductor workspace $WORKSPACE_NUM)
POSTGRES_PORT=$POSTGRES_PORT
ADCP_SALES_PORT=$ADCP_PORT
ADMIN_UI_PORT=$ADMIN_PORT
DATABASE_URL=postgresql://adcp_user:secure_password_change_me@localhost:$POSTGRES_PORT/adcp
EOF

# Fix database.py indentation issues
# (Add sed commands or Python script to fix indentation)

# Build and start
docker-compose build
docker-compose up -d
```

This ensures consistent setup across all Conductor workspaces.
76 changes: 76 additions & 0 deletions check_ports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""
Port availability checker for AdCP Sales Agent.
Validates that required ports are available before starting services.
"""

import socket
import sys
import os
from typing import List, Tuple


def check_port(host: str, port: int) -> bool:
"""Check if a port is available for binding."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
return True
except OSError:
return False


def get_ports_from_env() -> List[Tuple[str, int]]:
"""Get configured ports from environment variables."""
ports = []

# PostgreSQL port
pg_port = int(os.environ.get('POSTGRES_PORT', '5432'))
ports.append(('PostgreSQL', pg_port))

# MCP Server port
mcp_port = int(os.environ.get('ADCP_SALES_PORT', '8080'))
ports.append(('MCP Server', mcp_port))

# Admin UI port
admin_port = int(os.environ.get('ADMIN_UI_PORT', '8001'))
ports.append(('Admin UI', admin_port))

return ports


def main():
"""Check all configured ports and report availability."""
print("Checking port availability for AdCP Sales Agent...")
print("-" * 50)

ports = get_ports_from_env()
all_available = True
unavailable_ports = []

for service_name, port in ports:
if check_port('0.0.0.0', port):
print(f"✓ {service_name} port {port} is available")
else:
print(f"✗ {service_name} port {port} is NOT available")
all_available = False
unavailable_ports.append((service_name, port))

print("-" * 50)

if all_available:
print("✓ All ports are available. Services can be started.")
sys.exit(0)
else:
print("✗ Some ports are not available:")
for service_name, port in unavailable_ports:
print(f" - {service_name}: {port}")
print("\nTo find what's using a port, run:")
print(" lsof -i :<port> (on macOS/Linux)")
print(" netstat -ano | findstr :<port> (on Windows)")
sys.exit(1)


if __name__ == "__main__":
main()
72 changes: 36 additions & 36 deletions database.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,21 @@ def init_db():
},
"access_token": "purina_token"
}
]

for p in principals_data:
conn.execute("""
INSERT INTO principals (
tenant_id, principal_id, name,
platform_mappings, access_token
) VALUES (?, ?, ?, ?, ?)
""", (
"default",
p["principal_id"],
p["name"],
json.dumps(p["platform_mappings"]),
p["access_token"]
))
]
for p in principals_data:
conn.execute("""
INSERT INTO principals (
tenant_id, principal_id, name,
platform_mappings, access_token
) VALUES (?, ?, ?, ?, ?)
""", (
"default",
p["principal_id"],
p["name"],
json.dumps(p["platform_mappings"]),
p["access_token"]
))

# Create sample products
products_data = [
Expand Down Expand Up @@ -161,27 +161,27 @@ def init_db():
"cpm": 2.5,
"price_guidance": None
}
]

for p in products_data:
conn.execute("""
INSERT INTO products (
tenant_id, product_id, name, description,
formats, targeting_template, delivery_type,
is_fixed_price, cpm, price_guidance
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
"default",
p["product_id"],
p["name"],
p["description"],
json.dumps(p["formats"]),
json.dumps(p["targeting_template"]),
p["delivery_type"],
p["is_fixed_price"], # Boolean works for both
p.get("cpm"),
json.dumps(p["price_guidance"]) if p.get("price_guidance") else None
))
]
for p in products_data:
conn.execute("""
INSERT INTO products (
tenant_id, product_id, name, description,
formats, targeting_template, delivery_type,
is_fixed_price, cpm, price_guidance
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
"default",
p["product_id"],
p["name"],
p["description"],
json.dumps(p["formats"]),
json.dumps(p["targeting_template"]),
p["delivery_type"],
p["is_fixed_price"], # Boolean works for both
p.get("cpm"),
json.dumps(p["price_guidance"]) if p.get("price_guidance") else None
))

# Update the print statement based on whether sample data was created
if os.environ.get('CREATE_SAMPLE_DATA', 'false').lower() == 'true':
Expand Down
27 changes: 27 additions & 0 deletions docker-compose-safe.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
# Safe docker-compose wrapper that checks port availability first

# Load environment variables
if [ -f .env ]; then
set -a # automatically export all variables
source .env
set +a
fi

# Check if ports are available
echo "Checking port availability before starting services..."
python3 check_ports.py

if [ $? -ne 0 ]; then
echo ""
echo "ERROR: Cannot start services due to port conflicts."
echo "Please either:"
echo " 1. Stop the conflicting services"
echo " 2. Change the ports in your .env file"
exit 1
fi

# If all ports are available, proceed with docker-compose
echo ""
echo "Starting docker-compose services..."
docker-compose "$@"
9 changes: 5 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ services:
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
- "${POSTGRES_PORT:-5433}:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U adcp_user -d adcp"]
interval: 10s
Expand Down Expand Up @@ -40,7 +40,7 @@ services:
condition: service_healthy

ports:
- "8080:8080"
- "${ADCP_SALES_PORT:-8081}:8080"

volumes:
# Optional: Mount audit logs
Expand All @@ -54,7 +54,7 @@ services:
command: python admin_ui.py
environment:
DATABASE_URL: postgresql://adcp_user:secure_password_change_me@postgres:5432/adcp?sslmode=disable
ADMIN_UI_PORT: 8001
ADMIN_UI_PORT: ${ADMIN_UI_PORT:-8002}
FLASK_ENV: production
# Super admin authorization (set these via .env file)
SUPER_ADMIN_EMAILS: ${SUPER_ADMIN_EMAILS}
Expand All @@ -69,11 +69,12 @@ services:
condition: service_healthy

ports:
- "8001:8001"
- "${ADMIN_UI_PORT:-8002}:${ADMIN_UI_PORT:-8002}"

volumes:
# Mount OAuth credentials file
- ./client_secret_1002878641006-1balq5ha6fq3fq58bsho78gst2da4e6u.apps.googleusercontent.com.json:/app/client_secret.json:ro
- ./audit_logs:/app/audit_logs

volumes:
postgres_data: