mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-08 06:43:22 +00:00
- Added functionality to check if Supabase is enabled via the .env file before cloning the repository and preparing the environment. - Updated the command execution flow to conditionally include Supabase in the Docker commands based on its enabled status. - Enhanced the service selection wizard in 04_wizard.sh to include Supabase as an option for user selection.
255 lines
11 KiB
Python
255 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
start_services.py
|
|
|
|
This script starts the Supabase stack first, waits for it to initialize, and then starts
|
|
the local AI stack. Both stacks use the same Docker Compose project name ("localai")
|
|
so they appear together in Docker Desktop.
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import shutil
|
|
import time
|
|
import argparse
|
|
import platform
|
|
import sys
|
|
from dotenv import dotenv_values
|
|
|
|
def is_supabase_enabled():
|
|
"""Check if 'supabase' is in COMPOSE_PROFILES in .env file."""
|
|
env_values = dotenv_values(".env")
|
|
compose_profiles = env_values.get("COMPOSE_PROFILES", "")
|
|
return "supabase" in compose_profiles.split(',')
|
|
|
|
def run_command(cmd, cwd=None):
|
|
"""Run a shell command and print it."""
|
|
print("Running:", " ".join(cmd))
|
|
subprocess.run(cmd, cwd=cwd, check=True)
|
|
|
|
def clone_supabase_repo():
|
|
"""Clone the Supabase repository using sparse checkout if not already present."""
|
|
if not is_supabase_enabled():
|
|
print("Supabase is not enabled, skipping clone.")
|
|
return
|
|
if not os.path.exists("supabase"):
|
|
print("Cloning the Supabase repository...")
|
|
run_command([
|
|
"git", "clone", "--filter=blob:none", "--no-checkout",
|
|
"https://github.com/supabase/supabase.git"
|
|
])
|
|
os.chdir("supabase")
|
|
run_command(["git", "sparse-checkout", "init", "--cone"])
|
|
run_command(["git", "sparse-checkout", "set", "docker"])
|
|
run_command(["git", "checkout", "master"])
|
|
os.chdir("..")
|
|
else:
|
|
print("Supabase repository already exists, updating...")
|
|
os.chdir("supabase")
|
|
run_command(["git", "pull"])
|
|
os.chdir("..")
|
|
|
|
def prepare_supabase_env():
|
|
"""Copy .env to .env in supabase/docker."""
|
|
if not is_supabase_enabled():
|
|
print("Supabase is not enabled, skipping env preparation.")
|
|
return
|
|
env_path = os.path.join("supabase", "docker", ".env")
|
|
env_example_path = os.path.join(".env")
|
|
print("Copying .env in root to .env in supabase/docker...")
|
|
shutil.copyfile(env_example_path, env_path)
|
|
|
|
def stop_existing_containers():
|
|
"""Stop and remove existing containers for our unified project ('localai')."""
|
|
print("Stopping and removing existing containers for the unified project 'localai'...")
|
|
cmd = [
|
|
"docker", "compose",
|
|
"-p", "localai",
|
|
"-f", "docker-compose.yml"
|
|
]
|
|
if is_supabase_enabled():
|
|
cmd.extend(["-f", "supabase/docker/docker-compose.yml"])
|
|
cmd.append("down")
|
|
run_command(cmd)
|
|
|
|
def start_supabase():
|
|
"""Start the Supabase services (using its compose file)."""
|
|
if not is_supabase_enabled():
|
|
print("Supabase is not enabled, skipping start.")
|
|
return
|
|
print("Starting Supabase services...")
|
|
run_command([
|
|
"docker", "compose", "-p", "localai", "-f", "supabase/docker/docker-compose.yml", "up", "-d"
|
|
])
|
|
|
|
def start_local_ai():
|
|
"""Start the local AI services (using its compose file)."""
|
|
print("Starting local AI services...")
|
|
cmd = ["docker", "compose", "-p", "localai"]
|
|
cmd.extend(["-f", "docker-compose.yml", "up", "-d"])
|
|
run_command(cmd)
|
|
|
|
def generate_searxng_secret_key():
|
|
"""Generate a secret key for SearXNG based on the current platform."""
|
|
print("Checking SearXNG settings...")
|
|
|
|
# Define paths for SearXNG settings files
|
|
settings_path = os.path.join("searxng", "settings.yml")
|
|
settings_base_path = os.path.join("searxng", "settings-base.yml")
|
|
|
|
# Check if settings-base.yml exists
|
|
if not os.path.exists(settings_base_path):
|
|
print(f"Warning: SearXNG base settings file not found at {settings_base_path}")
|
|
return
|
|
|
|
# Check if settings.yml exists, if not create it from settings-base.yml
|
|
if not os.path.exists(settings_path):
|
|
print(f"SearXNG settings.yml not found. Creating from {settings_base_path}...")
|
|
try:
|
|
shutil.copyfile(settings_base_path, settings_path)
|
|
print(f"Created {settings_path} from {settings_base_path}")
|
|
except Exception as e:
|
|
print(f"Error creating settings.yml: {e}")
|
|
return
|
|
else:
|
|
print(f"SearXNG settings.yml already exists at {settings_path}")
|
|
|
|
print("Generating SearXNG secret key...")
|
|
|
|
# Detect the platform and run the appropriate command
|
|
system = platform.system()
|
|
|
|
try:
|
|
if system == "Windows":
|
|
print("Detected Windows platform, using PowerShell to generate secret key...")
|
|
# PowerShell command to generate a random key and replace in the settings file
|
|
ps_command = [
|
|
"powershell", "-Command",
|
|
"$randomBytes = New-Object byte[] 32; " +
|
|
"(New-Object Security.Cryptography.RNGCryptoServiceProvider).GetBytes($randomBytes); " +
|
|
"$secretKey = -join ($randomBytes | ForEach-Object { \"{0:x2}\" -f $_ }); " +
|
|
"(Get-Content searxng/settings.yml) -replace 'ultrasecretkey', $secretKey | Set-Content searxng/settings.yml"
|
|
]
|
|
subprocess.run(ps_command, check=True)
|
|
|
|
elif system == "Darwin": # macOS
|
|
print("Detected macOS platform, using sed command with empty string parameter...")
|
|
# macOS sed command requires an empty string for the -i parameter
|
|
openssl_cmd = ["openssl", "rand", "-hex", "32"]
|
|
random_key = subprocess.check_output(openssl_cmd).decode('utf-8').strip()
|
|
sed_cmd = ["sed", "-i", "", f"s|ultrasecretkey|{random_key}|g", settings_path]
|
|
subprocess.run(sed_cmd, check=True)
|
|
|
|
else: # Linux and other Unix-like systems
|
|
print("Detected Linux/Unix platform, using standard sed command...")
|
|
# Standard sed command for Linux
|
|
openssl_cmd = ["openssl", "rand", "-hex", "32"]
|
|
random_key = subprocess.check_output(openssl_cmd).decode('utf-8').strip()
|
|
sed_cmd = ["sed", "-i", f"s|ultrasecretkey|{random_key}|g", settings_path]
|
|
subprocess.run(sed_cmd, check=True)
|
|
|
|
print("SearXNG secret key generated successfully.")
|
|
|
|
except Exception as e:
|
|
print(f"Error generating SearXNG secret key: {e}")
|
|
print("You may need to manually generate the secret key using the commands:")
|
|
print(" - Linux: sed -i \"s|ultrasecretkey|$(openssl rand -hex 32)|g\" searxng/settings.yml")
|
|
print(" - macOS: sed -i '' \"s|ultrasecretkey|$(openssl rand -hex 32)|g\" searxng/settings.yml")
|
|
print(" - Windows (PowerShell):")
|
|
print(" $randomBytes = New-Object byte[] 32")
|
|
print(" (New-Object Security.Cryptography.RNGCryptoServiceProvider).GetBytes($randomBytes)")
|
|
print(" $secretKey = -join ($randomBytes | ForEach-Object { \"{0:x2}\" -f $_ })")
|
|
print(" (Get-Content searxng/settings.yml) -replace 'ultrasecretkey', $secretKey | Set-Content searxng/settings.yml")
|
|
|
|
def check_and_fix_docker_compose_for_searxng():
|
|
"""Check and modify docker-compose.yml for SearXNG first run."""
|
|
docker_compose_path = "docker-compose.yml"
|
|
if not os.path.exists(docker_compose_path):
|
|
print(f"Warning: Docker Compose file not found at {docker_compose_path}")
|
|
return
|
|
|
|
try:
|
|
# Read the docker-compose.yml file
|
|
with open(docker_compose_path, 'r') as file:
|
|
content = file.read()
|
|
|
|
# Default to first run
|
|
is_first_run = True
|
|
|
|
# Check if Docker is running and if the SearXNG container exists
|
|
try:
|
|
# Check if the SearXNG container is running
|
|
container_check = subprocess.run(
|
|
["docker", "ps", "--filter", "name=searxng", "--format", "{{.Names}}"],
|
|
capture_output=True, text=True, check=True
|
|
)
|
|
searxng_containers = container_check.stdout.strip().split('\n')
|
|
|
|
# If SearXNG container is running, check inside for uwsgi.ini
|
|
if any(container for container in searxng_containers if container):
|
|
container_name = next(container for container in searxng_containers if container)
|
|
print(f"Found running SearXNG container: {container_name}")
|
|
|
|
# Check if uwsgi.ini exists inside the container
|
|
container_check = subprocess.run(
|
|
["docker", "exec", container_name, "sh", "-c", "[ -f /etc/searxng/uwsgi.ini ] && echo 'found' || echo 'not_found'"],
|
|
capture_output=True, text=True, check=True
|
|
)
|
|
|
|
if "found" in container_check.stdout:
|
|
print("Found uwsgi.ini inside the SearXNG container - not first run")
|
|
is_first_run = False
|
|
else:
|
|
print("uwsgi.ini not found inside the SearXNG container - first run")
|
|
is_first_run = True
|
|
else:
|
|
print("No running SearXNG container found - assuming first run")
|
|
except Exception as e:
|
|
print(f"Error checking Docker container: {e} - assuming first run")
|
|
|
|
if is_first_run and "cap_drop: - ALL" in content:
|
|
print("First run detected for SearXNG. Temporarily removing 'cap_drop: - ALL' directive...")
|
|
# Temporarily comment out the cap_drop line
|
|
modified_content = content.replace("cap_drop: - ALL", "# cap_drop: - ALL # Temporarily commented out for first run")
|
|
|
|
# Write the modified content back
|
|
with open(docker_compose_path, 'w') as file:
|
|
file.write(modified_content)
|
|
|
|
print("Note: After the first run completes successfully, you should re-add 'cap_drop: - ALL' to docker-compose.yml for security reasons.")
|
|
elif not is_first_run and "# cap_drop: - ALL # Temporarily commented out for first run" in content:
|
|
print("SearXNG has been initialized. Re-enabling 'cap_drop: - ALL' directive for security...")
|
|
# Uncomment the cap_drop line
|
|
modified_content = content.replace("# cap_drop: - ALL # Temporarily commented out for first run", "cap_drop: - ALL")
|
|
|
|
# Write the modified content back
|
|
with open(docker_compose_path, 'w') as file:
|
|
file.write(modified_content)
|
|
|
|
except Exception as e:
|
|
print(f"Error checking/modifying docker-compose.yml for SearXNG: {e}")
|
|
|
|
def main():
|
|
if is_supabase_enabled():
|
|
clone_supabase_repo()
|
|
prepare_supabase_env()
|
|
|
|
# Generate SearXNG secret key and check docker-compose.yml
|
|
generate_searxng_secret_key()
|
|
check_and_fix_docker_compose_for_searxng()
|
|
|
|
stop_existing_containers()
|
|
|
|
# Start Supabase first
|
|
if is_supabase_enabled():
|
|
start_supabase()
|
|
|
|
# Give Supabase some time to initialize
|
|
print("Waiting for Supabase to initialize...")
|
|
time.sleep(10)
|
|
|
|
# Then start the local AI services
|
|
start_local_ai()
|
|
|
|
if __name__ == "__main__":
|
|
main() |