Features: - Add LinkedIn OAuth integration and auto-posting functionality - Add scheduler service for automated post publishing - Add metadata field to generated_posts for LinkedIn URLs - Add privacy policy page for LinkedIn API compliance - Add company management features and employee accounts - Add license key system for company registrations Fixes: - Fix timezone issues (use UTC consistently across app) - Fix datetime serialization errors in database operations - Fix scheduling timezone conversion (local time to UTC) - Fix import errors (get_database -> db) Infrastructure: - Update Docker setup to use port 8001 (avoid conflicts) - Add SSL support with nginx-proxy and Let's Encrypt - Add LinkedIn setup documentation - Add migration scripts for schema updates Services: - Add linkedin_service.py for LinkedIn API integration - Add scheduler_service.py for background job processing - Add storage_service.py for Supabase Storage - Add email_service.py improvements - Add encryption utilities for token storage Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
172 lines
5.3 KiB
Python
Executable File
172 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Script to configure LinkedIn (OIDC) authentication for self-hosted Supabase.
|
|
This script updates your .env file or docker-compose.yml with the necessary GoTrue environment variables.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# ==================== CONFIGURATION ====================
|
|
# TODO: Fill in your LinkedIn credentials below
|
|
|
|
LINKEDIN_CLIENT_ID = "your-linkedin-client-id"
|
|
LINKEDIN_CLIENT_SECRET = "your-linkedin-client-secret"
|
|
|
|
# Your Supabase instance URL (where your self-hosted instance is accessible)
|
|
SUPABASE_URL = "https://your-supabase-domain.com" # e.g., https://supabase.example.com
|
|
|
|
# Path to your Supabase docker-compose directory (where .env or docker-compose.yml is located)
|
|
DOCKER_COMPOSE_DIR = "/path/to/your/supabase" # e.g., /home/user/supabase
|
|
|
|
# =======================================================
|
|
|
|
|
|
def validate_config():
|
|
"""Validate that all required configuration is set."""
|
|
errors = []
|
|
|
|
if LINKEDIN_CLIENT_ID.startswith("your-"):
|
|
errors.append("LINKEDIN_CLIENT_ID")
|
|
if LINKEDIN_CLIENT_SECRET.startswith("your-"):
|
|
errors.append("LINKEDIN_CLIENT_SECRET")
|
|
if SUPABASE_URL.startswith("https://your-"):
|
|
errors.append("SUPABASE_URL")
|
|
if DOCKER_COMPOSE_DIR.startswith("/path/to/"):
|
|
errors.append("DOCKER_COMPOSE_DIR")
|
|
|
|
if errors:
|
|
print("❌ Error: Please update the following configuration variables in this script:")
|
|
for var in errors:
|
|
print(f" - {var}")
|
|
sys.exit(1)
|
|
|
|
|
|
def find_env_file():
|
|
"""Find the .env file in the docker-compose directory."""
|
|
compose_dir = Path(DOCKER_COMPOSE_DIR)
|
|
|
|
if not compose_dir.exists():
|
|
print(f"❌ Error: Directory not found: {DOCKER_COMPOSE_DIR}")
|
|
sys.exit(1)
|
|
|
|
# Look for common .env file locations
|
|
env_files = [
|
|
compose_dir / ".env",
|
|
compose_dir / "docker" / ".env",
|
|
compose_dir / ".env.local",
|
|
]
|
|
|
|
for env_file in env_files:
|
|
if env_file.exists():
|
|
return env_file
|
|
|
|
return None
|
|
|
|
|
|
def update_env_file(env_file):
|
|
"""Update the .env file with LinkedIn OAuth configuration."""
|
|
|
|
# LinkedIn OAuth configuration for GoTrue
|
|
linkedin_config = f"""
|
|
# LinkedIn OAuth Configuration (added by setup script)
|
|
GOTRUE_EXTERNAL_LINKEDIN_ENABLED=true
|
|
GOTRUE_EXTERNAL_LINKEDIN_CLIENT_ID={LINKEDIN_CLIENT_ID}
|
|
GOTRUE_EXTERNAL_LINKEDIN_SECRET={LINKEDIN_CLIENT_SECRET}
|
|
GOTRUE_EXTERNAL_LINKEDIN_REDIRECT_URI={SUPABASE_URL}/auth/v1/callback
|
|
"""
|
|
|
|
# Read existing content
|
|
if env_file:
|
|
with open(env_file, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Check if LinkedIn config already exists
|
|
if "GOTRUE_EXTERNAL_LINKEDIN_ENABLED" in content:
|
|
print(f"⚠️ LinkedIn OAuth configuration already exists in {env_file}")
|
|
print(" Please update it manually or remove the existing lines first.")
|
|
return False
|
|
|
|
# Backup original file
|
|
backup_file = env_file.with_suffix('.env.backup')
|
|
with open(backup_file, 'w') as f:
|
|
f.write(content)
|
|
print(f"📋 Backup created: {backup_file}")
|
|
|
|
# Append LinkedIn config
|
|
with open(env_file, 'a') as f:
|
|
f.write(linkedin_config)
|
|
|
|
print(f"✅ LinkedIn OAuth configuration added to {env_file}")
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def create_env_snippet():
|
|
"""Create a snippet file if no .env file is found."""
|
|
snippet_file = Path(DOCKER_COMPOSE_DIR) / "linkedin_oauth_snippet.env"
|
|
|
|
linkedin_config = f"""# LinkedIn OAuth Configuration for GoTrue
|
|
# Add these lines to your .env file or docker-compose.yml environment section
|
|
|
|
GOTRUE_EXTERNAL_LINKEDIN_ENABLED=true
|
|
GOTRUE_EXTERNAL_LINKEDIN_CLIENT_ID={LINKEDIN_CLIENT_ID}
|
|
GOTRUE_EXTERNAL_LINKEDIN_SECRET={LINKEDIN_CLIENT_SECRET}
|
|
GOTRUE_EXTERNAL_LINKEDIN_REDIRECT_URI={SUPABASE_URL}/auth/v1/callback
|
|
"""
|
|
|
|
with open(snippet_file, 'w') as f:
|
|
f.write(linkedin_config)
|
|
|
|
print(f"📄 Created configuration snippet: {snippet_file}")
|
|
print(" Copy these variables to your .env file or docker-compose.yml")
|
|
return True
|
|
|
|
|
|
def print_next_steps():
|
|
"""Print instructions for completing the setup."""
|
|
print("\n" + "="*60)
|
|
print("✅ Configuration complete!")
|
|
print("="*60)
|
|
print("\nNext steps:\n")
|
|
print("1. Restart your Supabase services:")
|
|
print(f" cd {DOCKER_COMPOSE_DIR}")
|
|
print(" docker-compose down")
|
|
print(" docker-compose up -d")
|
|
print()
|
|
print("2. Add redirect URL in LinkedIn Developer Portal:")
|
|
print(f" {SUPABASE_URL}/auth/v1/callback")
|
|
print()
|
|
print("3. Test the authentication in your application")
|
|
print()
|
|
print("4. Check GoTrue logs for any errors:")
|
|
print(" docker-compose logs -f auth")
|
|
print()
|
|
|
|
|
|
def main():
|
|
"""Main function."""
|
|
print("🔧 Configuring LinkedIn OAuth for self-hosted Supabase")
|
|
print("="*60)
|
|
|
|
validate_config()
|
|
|
|
env_file = find_env_file()
|
|
|
|
if env_file:
|
|
print(f"📁 Found .env file: {env_file}")
|
|
if update_env_file(env_file):
|
|
print_next_steps()
|
|
else:
|
|
print("\n⚠️ Please update your configuration manually.")
|
|
else:
|
|
print("⚠️ No .env file found in the docker-compose directory")
|
|
create_env_snippet()
|
|
print_next_steps()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|