docs: record agent_runs RLS fix and Traefik IP candidate
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
137
scripts/dokploy_verify_agent_rls.py
Normal file
137
scripts/dokploy_verify_agent_rls.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user