Files
Onyva-Postling/scripts/setup_linkedin_auth.py
Ruben Fischer f14515e9cf Major updates: LinkedIn auto-posting, timezone fixes, and Docker improvements
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>
2026-02-11 11:30:20 +01:00

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()