#!/usr/bin/env python3 """Push ExposedGays to Gitea, create Coolify app, apply Supabase schema.""" import json import os import subprocess import sys import urllib.request import urllib.error try: import paramiko except ImportError: subprocess.check_call([sys.executable, "-m", "pip", "install", "paramiko", "-q"]) import paramiko HOST = "10.10.0.10" SUPA_HOST = "10.10.0.11" USER = "localadministrator" PASS = os.environ.get("COOLIFY_SSH_PASS", "Bbt9115xty9176!") GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "dbf033cee4b0e3be5699f374a15f3fa240e2896a") COOLIFY_TOKEN = os.environ.get("COOLIFY_TOKEN", "1|zALRdpUD3VkejNVlEV5UuRzTeoYVhshZtke5VkCNa6e9262d") REPO_NAME = "exposedgays" PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) GIT_AUTH = f"https://{USER}:{GITEA_TOKEN}@gitea.onsethost.com/localadministrator/{REPO_NAME}.git" def ssh_run(host, cmd, timeout=120): c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect(host, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False) print(f"[{host}] $ {cmd[:120]}") _, o, e = c.exec_command(cmd, timeout=timeout) code = o.channel.recv_exit_status() out = (o.read() + e.read()).decode(errors="replace") if out.strip(): print(out[-8000:]) print(f"exit={code}") c.close() return code, out def gitea_api(method, path, data=None): url = f"https://gitea.onsethost.com/api/v1{path}" body = json.dumps(data).encode() if data else None req = urllib.request.Request(url, data=body, method=method) req.add_header("Authorization", f"token {GITEA_TOKEN}") req.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(req, timeout=30) as resp: return resp.status, json.loads(resp.read().decode()) except urllib.error.HTTPError as ex: body = ex.read().decode() try: return ex.code, json.loads(body) except Exception: return ex.code, {"message": body} def coolify_api(method, path, data=None): url = f"https://coolify.onsethost.com/api/v1{path}" body = json.dumps(data).encode() if data else None req = urllib.request.Request(url, data=body, method=method) req.add_header("Authorization", f"Bearer {COOLIFY_TOKEN}") req.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(req, timeout=60) as resp: raw = resp.read().decode() return resp.status, json.loads(raw) if raw else {} except urllib.error.HTTPError as ex: body = ex.read().decode() try: return ex.code, json.loads(body) except Exception: return ex.code, {"message": body} def push_gitea(): print("\n=== 1. Create Gitea repo (if missing) ===") code, resp = gitea_api("POST", "/user/repos", { "name": REPO_NAME, "private": True, "auto_init": False, "description": "ExposedGays — free gay cruising + shop", }) if code in (201, 409): print(f"Repo ready (HTTP {code})") else: print(f"Repo create: {code} {resp}") print("\n=== 2. Git commit + push from COOLIFY_01 ===") schema_sql = open(os.path.join(PROJECT_ROOT, "supabase", "schema.sql"), encoding="utf-8").read() functions_sql = open(os.path.join(PROJECT_ROOT, "supabase", "functions.sql"), encoding="utf-8").read() # Tar project and upload via SSH c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect(HOST, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False) sftp = c.open_sftp() import tarfile import io buf = io.BytesIO() with tarfile.open(fileobj=buf, mode="w:gz") as tar: for root, dirs, files in os.walk(PROJECT_ROOT): dirs[:] = [d for d in dirs if d not in (".next", "node_modules", ".git")] for f in files: if f in (".env.local",): continue full = os.path.join(root, f) arc = os.path.relpath(full, PROJECT_ROOT) tar.add(full, arcname=arc) buf.seek(0) sftp.putfo(buf, "/tmp/exposedgays.tar.gz") sftp.close() cmds = [ "rm -rf /tmp/exposedgays && mkdir -p /tmp/exposedgays && tar -xzf /tmp/exposedgays.tar.gz -C /tmp/exposedgays", f"cd /tmp/exposedgays && git init -b main && git config user.email 'ryan.salazar@justtworoommates.com' && git config user.name 'Ryan Salazar'", "cd /tmp/exposedgays && git add -A && git commit -m 'ExposedGays: map, shop, auth, Supabase schema' || true", f"cd /tmp/exposedgays && git remote remove origin 2>/dev/null; git remote add origin {GIT_AUTH}", f"cd /tmp/exposedgays && git push -u origin main --force", ] for cmd in cmds: _, o, e = c.exec_command(cmd, timeout=180) code = o.channel.recv_exit_status() out = (o.read() + e.read()).decode(errors="replace") print(out[-3000:]) print(f"exit={code}") c.close() return GIT_AUTH.replace(GITEA_TOKEN, "***") def apply_supabase_schema(): print("\n=== 3. Apply Supabase schema on SUPABASE_01 ===") schema_path = os.path.join(PROJECT_ROOT, "supabase", "schema.sql") func_path = os.path.join(PROJECT_ROOT, "supabase", "functions.sql") c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect(SUPA_HOST, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False) sftp = c.open_sftp() sftp.put(schema_path, "/tmp/eg_schema.sql") sftp.put(func_path, "/tmp/eg_functions.sql") sftp.close() cmd = ( "docker exec -i supabase-db psql -U postgres -d postgres " "-f /dev/stdin < /tmp/eg_schema.sql 2>&1; " "docker exec -i supabase-db psql -U postgres -d postgres " "-f /dev/stdin < /tmp/eg_functions.sql 2>&1" ) # Copy into container first for f in ("eg_schema.sql", "eg_functions.sql"): c.exec_command(f"docker cp /tmp/{f} supabase-db:/tmp/{f}") import time time.sleep(2) _, o, e = c.exec_command( "docker exec supabase-db psql -U postgres -d postgres -f /tmp/eg_schema.sql 2>&1; " "docker exec supabase-db psql -U postgres -d postgres -f /tmp/eg_functions.sql 2>&1", timeout=120, ) out = (o.read() + e.read()).decode(errors="replace") print(out[-5000:]) c.close() def create_coolify_app(git_url_auth): print("\n=== 4. Create Coolify application ===") # List projects code, projects = coolify_api("GET", "/projects") print(f"Projects: {code}") project_uuid = None if code == 200 and projects: items = projects if isinstance(projects, list) else projects.get("data", projects) if items: project_uuid = items[0].get("uuid") or items[0].get("id") print(f"Using project: {project_uuid}") payload = { "project_uuid": project_uuid, "server_uuid": None, "environment_name": "production", "git_repository": f"https://gitea.onsethost.com/localadministrator/{REPO_NAME}.git", "git_branch": "main", "build_pack": "dockerfile", "dockerfile_location": "/Dockerfile", "ports_exposes": "3000", "name": "exposedgays", "description": "ExposedGays — free cruising + shop", "domains": "https://exposedgays.com,https://www.exposedgays.com", "instant_deploy": True, } code, resp = coolify_api("POST", "/applications/public", payload) print(f"Create app: HTTP {code}") print(json.dumps(resp, indent=2)[:3000]) app_uuid = resp.get("uuid") if isinstance(resp, dict) else None if not app_uuid and isinstance(resp, dict): app_uuid = resp.get("id") if app_uuid: print(f"\n=== 5. Set env vars on {app_uuid} ===") envs = [ ("NEXT_PUBLIC_SUPABASE_URL", "https://supabase.onsethost.com"), ("NEXT_PUBLIC_SUPABASE_ANON_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzc2NzEzOTczLCJleHAiOjIwOTIwNzM5NzN9.oRPm6uJt4Gj-ASOixznas7-IC1pCtlZtEFew-SmY9wo"), ("NEXT_PUBLIC_APP_URL", "https://exposedgays.com"), ("NEXT_PUBLIC_DEFAULT_CITY", "Fort Lauderdale"), ("NEXT_PUBLIC_DEFAULT_LAT", "26.1224"), ("NEXT_PUBLIC_DEFAULT_LNG", "-80.1373"), ] for key, value in envs: c, r = coolify_api("POST", f"/applications/{app_uuid}/envs", {"key": key, "value": value}) print(f" {key}: HTTP {c}") print("\n=== 6. Trigger deploy ===") code, dep = coolify_api("POST", f"/deploy?uuid={app_uuid}&force_rebuild=true") print(f"Deploy: HTTP {code} {dep}") return app_uuid def main(): git_url = push_gitea() try: apply_supabase_schema() except Exception as ex: print(f"Supabase schema warning: {ex}") app_uuid = create_coolify_app(git_url) print("\n=== DONE ===") print(f"Gitea: https://gitea.onsethost.com/localadministrator/{REPO_NAME}") print(f"Coolify app: {app_uuid or 'check Coolify UI'}") print("Next: add exposedgays.com to Cloudflare tunnel → Coolify container") if __name__ == "__main__": main()