Files
exposedgays/deploy/setup-infra.py

237 lines
9.2 KiB
Python

#!/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()