Files
4ventures-client-portal/scripts/dokploy_verify_agent_rls.py
2026-09-21 18:16:44 +02:00

138 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Verify agent_runs RLS; apply sql/agent_runs_rls.sql only if still disabled."""
from __future__ import annotations
import base64
import re
import sys
from pathlib import Path
from playwright.sync_api import sync_playwright
ROOT = Path("/Users/4ventures/4ventures-client-portal")
BPS = Path("/Users/4ventures/Projects/bps-facturacion")
SHOT = ROOT / "scripts/dokploy-shots"
OUT = ROOT / ".ops" / "rls_inventory.txt"
BASE = "https://dokploy.4ventures.es"
SQL_FILE = ROOT / "sql/agent_runs_rls.sql"
INVENTORY = (
"SELECT c.relname AS table_name, c.relrowsecurity AS rls_enabled "
"FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace "
"WHERE n.nspname = 'public' AND c.relkind = 'r' ORDER BY 1;"
)
def env(key: str) -> str:
import os
v = os.environ.get(key, "").strip()
if v:
return v
p = BPS / ".env"
if not p.is_file():
return ""
for line in p.read_text(encoding="utf-8").splitlines():
if line.startswith(f"{key}="):
return line.split("=", 1)[1].strip().strip('"').strip("'")
return ""
def term_run(page, cmd: str, wait_ms: int = 4000) -> None:
helper = page.locator("textarea.xterm-helper-textarea")
if helper.count():
helper.first.click()
page.keyboard.type(cmd, delay=2)
page.keyboard.press("Enter")
page.wait_for_timeout(wait_ms)
def term_text(page) -> str:
return page.evaluate(
"""() => {
const rows = document.querySelector('.xterm-rows');
return rows ? rows.innerText : '';
}"""
)
def main() -> int:
email = env("DOKPLOY_EMAIL") or "marketing@4ventures.es"
password = env("DOKPLOY_PASSWORD")
if not password:
print("NO_DOKPLOY_PASSWORD", flush=True)
return 2
SHOT.mkdir(parents=True, exist_ok=True)
OUT.parent.mkdir(parents=True, exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1600, "height": 1200})
page.goto(BASE + "/", wait_until="domcontentloaded", timeout=90000)
page.wait_for_timeout(700)
if "/dashboard" not in page.url:
page.locator('input[type="email"], input[name="email"]').first.fill(email)
page.locator('input[type="password"]').first.fill(password)
page.locator('button[type="submit"]').first.click()
page.wait_for_url(re.compile(r"/dashboard"), timeout=90000)
print("login ok", flush=True)
page.goto(f"{BASE}/dashboard/docker", wait_until="networkidle", timeout=120000)
page.wait_for_timeout(1000)
page.locator("input[placeholder*='ilter' i], input[placeholder*='earch' i]").first.fill(
"supabase-db"
)
page.wait_for_timeout(1200)
page.keyboard.press("Escape")
page.wait_for_timeout(300)
page.get_by_role("button", name=re.compile(r"Open menu", re.I)).first.click()
page.wait_for_timeout(600)
page.get_by_text(re.compile(r"^Terminal$", re.I)).first.click()
page.wait_for_timeout(3500)
term_run(page, f'psql -U postgres -d postgres -c "{INVENTORY}" ; echo RLS_INV', 7000)
text = term_text(page)
OUT.write_text(text[-4000:], encoding="utf-8")
print("inventory_written", flush=True)
agent_on = bool(re.search(r"agent_runs\s+\|\s+t\b", text))
agent_off = bool(re.search(r"agent_runs\s+\|\s+f\b", text))
print("agent_runs_rls", "on" if agent_on else ("off" if agent_off else "unknown"), flush=True)
if agent_on:
print("OK_ALREADY", flush=True)
browser.close()
return 0
if not agent_off:
print("PARSE_FAIL — see .ops/rls_inventory.txt", flush=True)
page.screenshot(path=str(SHOT / "rls_parse_fail.png"), full_page=True)
browser.close()
return 4
# Apply SQL via base64 chunks
b64 = base64.b64encode(SQL_FILE.read_text(encoding="utf-8").encode()).decode()
chunks = [b64[i : i + 200] for i in range(0, len(b64), 200)]
term_run(page, "rm -f /tmp/agent_rls.b64 /tmp/agent_rls.sql; echo READY", 2000)
for i, ch in enumerate(chunks):
op = ">" if i == 0 else ">>"
term_run(page, f"printf '%s' '{ch}' {op} /tmp/agent_rls.b64", 800)
term_run(
page,
"base64 -d /tmp/agent_rls.b64 > /tmp/agent_rls.sql && "
"psql -U postgres -d postgres -v ON_ERROR_STOP=1 -f /tmp/agent_rls.sql; "
"echo EXIT:$?",
8000,
)
term_run(page, f'psql -U postgres -d postgres -c "{INVENTORY}" ; echo RLS_INV2', 7000)
text2 = term_text(page)
OUT.write_text(text2[-4000:], encoding="utf-8")
page.screenshot(path=str(SHOT / "rls_after.png"), full_page=True)
ok = bool(re.search(r"agent_runs\s+\|\s+t\b", text2))
print("agent_runs_after", "on" if ok else "still_off", flush=True)
browser.close()
return 0 if ok else 6
if __name__ == "__main__":
raise SystemExit(main())