Add Dokploy scripts to apply and verify unified approval_status SQL.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
133
scripts/dokploy_apply_approval_status_unify.py
Normal file
133
scripts/dokploy_apply_approval_status_unify.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Apply approval_status_unify.sql + generation_requests_copy_v2.sql via Dokploy supabase-db."""
|
||||||
|
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")
|
||||||
|
BASE = "https://dokploy.4ventures.es"
|
||||||
|
SQLS = [
|
||||||
|
ROOT / "sql" / "approval_status_unify.sql",
|
||||||
|
ROOT / "sql" / "generation_requests_copy_v2.sql",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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().splitlines():
|
||||||
|
if line.startswith(f"{key}="):
|
||||||
|
return line.split("=", 1)[1].strip().strip('"').strip("'")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
email = env("DOKPLOY_EMAIL") or "marketing@4ventures.es"
|
||||||
|
password = env("DOKPLOY_PASSWORD")
|
||||||
|
if not password:
|
||||||
|
print("missing DOKPLOY_PASSWORD", flush=True)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(headless=True)
|
||||||
|
page = browser.new_page(viewport={"width": 1400, "height": 1000})
|
||||||
|
page.goto(BASE + "/", wait_until="domcontentloaded", timeout=90000)
|
||||||
|
page.wait_for_timeout(800)
|
||||||
|
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="domcontentloaded", timeout=120000
|
||||||
|
)
|
||||||
|
page.wait_for_timeout(800)
|
||||||
|
page.locator(
|
||||||
|
"input[placeholder*='ilter' i], input[placeholder*='earch' i]"
|
||||||
|
).first.fill("supabase-db")
|
||||||
|
page.wait_for_timeout(1000)
|
||||||
|
page.keyboard.press("Escape")
|
||||||
|
page.wait_for_timeout(200)
|
||||||
|
page.get_by_role("button", name=re.compile(r"Open menu", re.I)).first.click()
|
||||||
|
page.wait_for_timeout(400)
|
||||||
|
page.get_by_text("Terminal", exact=True).click()
|
||||||
|
page.wait_for_timeout(1000)
|
||||||
|
if page.locator("button", has_text="Bash").count():
|
||||||
|
page.locator("button", has_text="Bash").first.click()
|
||||||
|
page.wait_for_timeout(2800)
|
||||||
|
if page.locator("textarea.xterm-helper-textarea").count():
|
||||||
|
page.locator("textarea.xterm-helper-textarea").first.click()
|
||||||
|
|
||||||
|
def run(cmd: str, wait: int = 800) -> None:
|
||||||
|
page.keyboard.type(cmd, delay=1)
|
||||||
|
page.keyboard.press("Enter")
|
||||||
|
page.wait_for_timeout(wait)
|
||||||
|
|
||||||
|
all_ok = True
|
||||||
|
for idx, sql_path in enumerate(SQLS):
|
||||||
|
tag = f"m{idx}"
|
||||||
|
print(f"APPLY {sql_path.name}", flush=True)
|
||||||
|
b64 = base64.b64encode(sql_path.read_text(encoding="utf-8").encode()).decode()
|
||||||
|
chunks = [b64[i : i + 180] for i in range(0, len(b64), 180)]
|
||||||
|
run(f"rm -f /tmp/{tag}.b64 /tmp/{tag}.sql; echo READY_{tag}", 1200)
|
||||||
|
for i, ch in enumerate(chunks):
|
||||||
|
op = ">" if i == 0 else ">>"
|
||||||
|
run(f"printf '%s' '{ch}' {op} /tmp/{tag}.b64", 350)
|
||||||
|
run(
|
||||||
|
f"base64 -d /tmp/{tag}.b64 > /tmp/{tag}.sql && "
|
||||||
|
f"psql -U postgres -d postgres -v ON_ERROR_STOP=1 -f /tmp/{tag}.sql; "
|
||||||
|
f"echo EXIT_{tag}:$?",
|
||||||
|
18000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify constraints / sample statuses
|
||||||
|
run(
|
||||||
|
"psql -U postgres -d postgres -c \""
|
||||||
|
"select conname, pg_get_constraintdef(oid) "
|
||||||
|
"from pg_constraint "
|
||||||
|
"where conname in ("
|
||||||
|
"'creative_pipeline_status_check',"
|
||||||
|
"'creatives_status_check',"
|
||||||
|
"'generation_requests_reason_check'"
|
||||||
|
");\" ; echo VERIFY_DONE",
|
||||||
|
8000,
|
||||||
|
)
|
||||||
|
run(
|
||||||
|
"psql -U postgres -d postgres -c \""
|
||||||
|
"select status, count(*) from public.creative_pipeline group by 1 order by 1;\" "
|
||||||
|
"; echo PIPE_STATUSES",
|
||||||
|
6000,
|
||||||
|
)
|
||||||
|
|
||||||
|
text = page.evaluate(
|
||||||
|
"() => (document.querySelector('.xterm-rows')||{}).innerText||''"
|
||||||
|
)
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
for idx, sql_path in enumerate(SQLS):
|
||||||
|
tag = f"m{idx}"
|
||||||
|
ok = f"EXIT_{tag}:0" in text
|
||||||
|
print(f"SQL_OK {sql_path.name}", ok, flush=True)
|
||||||
|
all_ok = all_ok and ok
|
||||||
|
|
||||||
|
print("--- terminal tail ---", flush=True)
|
||||||
|
print(text[-2500:], flush=True)
|
||||||
|
return 0 if all_ok else 6
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
134
scripts/dokploy_verify_approval_status.py
Normal file
134
scripts/dokploy_verify_approval_status.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Verify + re-apply approval_status_unify.sql if needed via Dokploy supabase-db."""
|
||||||
|
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")
|
||||||
|
BASE = "https://dokploy.4ventures.es"
|
||||||
|
SQL = ROOT / "sql" / "approval_status_unify.sql"
|
||||||
|
|
||||||
|
|
||||||
|
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().splitlines():
|
||||||
|
if line.startswith(f"{key}="):
|
||||||
|
return line.split("=", 1)[1].strip().strip('"').strip("'")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
email = env("DOKPLOY_EMAIL") or "marketing@4ventures.es"
|
||||||
|
password = env("DOKPLOY_PASSWORD")
|
||||||
|
if not password:
|
||||||
|
print("missing DOKPLOY_PASSWORD", flush=True)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
b64 = base64.b64encode(SQL.read_text(encoding="utf-8").encode()).decode()
|
||||||
|
chunks = [b64[i : i + 180] for i in range(0, len(b64), 180)]
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(headless=True)
|
||||||
|
page = browser.new_page(viewport={"width": 1400, "height": 1000})
|
||||||
|
page.goto(BASE + "/", wait_until="domcontentloaded", timeout=90000)
|
||||||
|
page.wait_for_timeout(800)
|
||||||
|
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="domcontentloaded", timeout=120000
|
||||||
|
)
|
||||||
|
page.wait_for_timeout(800)
|
||||||
|
page.locator(
|
||||||
|
"input[placeholder*='ilter' i], input[placeholder*='earch' i]"
|
||||||
|
).first.fill("supabase-db")
|
||||||
|
page.wait_for_timeout(1000)
|
||||||
|
page.keyboard.press("Escape")
|
||||||
|
page.wait_for_timeout(200)
|
||||||
|
page.get_by_role("button", name=re.compile(r"Open menu", re.I)).first.click()
|
||||||
|
page.wait_for_timeout(400)
|
||||||
|
page.get_by_text("Terminal", exact=True).click()
|
||||||
|
page.wait_for_timeout(1000)
|
||||||
|
if page.locator("button", has_text="Bash").count():
|
||||||
|
page.locator("button", has_text="Bash").first.click()
|
||||||
|
page.wait_for_timeout(2800)
|
||||||
|
if page.locator("textarea.xterm-helper-textarea").count():
|
||||||
|
page.locator("textarea.xterm-helper-textarea").first.click()
|
||||||
|
|
||||||
|
def run(cmd: str, wait: int = 800) -> None:
|
||||||
|
page.keyboard.type(cmd, delay=1)
|
||||||
|
page.keyboard.press("Enter")
|
||||||
|
page.wait_for_timeout(wait)
|
||||||
|
|
||||||
|
# Disable pager
|
||||||
|
run("export PAGER=cat; export LESS=-FX; echo PAGER_OK", 1000)
|
||||||
|
|
||||||
|
# Re-apply idempotent migration
|
||||||
|
run("rm -f /tmp/asu.b64 /tmp/asu.sql; echo READY", 1000)
|
||||||
|
for i, ch in enumerate(chunks):
|
||||||
|
op = ">" if i == 0 else ">>"
|
||||||
|
run(f"printf '%s' '{ch}' {op} /tmp/asu.b64", 300)
|
||||||
|
run(
|
||||||
|
"base64 -d /tmp/asu.b64 > /tmp/asu.sql && "
|
||||||
|
"psql -U postgres -d postgres -v ON_ERROR_STOP=1 -P pager=off -f /tmp/asu.sql; "
|
||||||
|
"echo EXIT_ASU:$?",
|
||||||
|
20000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compact verifies
|
||||||
|
run(
|
||||||
|
"psql -U postgres -d postgres -P pager=off -tAc "
|
||||||
|
"\"select string_agg(conname, ',' order by conname) from pg_constraint "
|
||||||
|
"where conname in ("
|
||||||
|
"'creative_pipeline_status_check',"
|
||||||
|
"'creatives_status_check',"
|
||||||
|
"'generation_requests_reason_check'"
|
||||||
|
");\" ; echo CONS:$?",
|
||||||
|
8000,
|
||||||
|
)
|
||||||
|
run(
|
||||||
|
"psql -U postgres -d postgres -P pager=off -tAc "
|
||||||
|
"\"select string_agg(status||':'||c, ',' order by status) from ("
|
||||||
|
"select status, count(*)::text c from public.creative_pipeline group by 1) s;\" "
|
||||||
|
"; echo PIPE:$?",
|
||||||
|
6000,
|
||||||
|
)
|
||||||
|
run(
|
||||||
|
"psql -U postgres -d postgres -P pager=off -tAc "
|
||||||
|
"\"select string_agg(status||':'||c, ',' order by status) from ("
|
||||||
|
"select status, count(*)::text c from public.creatives group by 1) s;\" "
|
||||||
|
"; echo ADS:$?",
|
||||||
|
6000,
|
||||||
|
)
|
||||||
|
|
||||||
|
text = page.evaluate(
|
||||||
|
"() => (document.querySelector('.xterm-rows')||{}).innerText||''"
|
||||||
|
)
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
ok = "EXIT_ASU:0" in text
|
||||||
|
print("SQL_OK", ok, flush=True)
|
||||||
|
print("--- tail ---", flush=True)
|
||||||
|
print(text[-2000:], flush=True)
|
||||||
|
return 0 if ok else 6
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user