Adds the full Next.js app (app/, components/, lib/, scripts/, assets/, docs/, Dockerfile, compose, configs) to version control. Security: - CIPC BizPortal customer-code logins (Login 1 + Login 3) now read CIPC_LOGIN1_CODE / CIPC_LOGIN1_PASSWORD / CIPC_LOGIN3_CODE / CIPC_LOGIN3_PASSWORD from .env.local instead of source. - BizPortal personal login in apply-bizportal route reads BIZPORTAL_ID_NO / BIZPORTAL_PASSWORD. - Added .env.example as the committed template. .gitignore excludes .env*, node_modules, .next, logs, sqlite, Playwright storage_state.json (live CIPC cookies), scratch dirs, client PDFs and debug screenshots.
102 lines
No EOL
3.5 KiB
Python
102 lines
No EOL
3.5 KiB
Python
"""Clear all three UFC documents, after saving local copies.
|
|
|
|
The user asked to remove every document from the UFC resolution:
|
|
1. ysterbaard_resolutions.signed_letter
|
|
2. ysterbaard_director_resignations.letter
|
|
3. ysterbaard_director_resignations.signature
|
|
|
|
PocketBase file deletion is irreversible, so the bytes are copied into
|
|
scripts/_backup_ufc_docs/ first. This script never deletes the record itself —
|
|
only the file fields, which is what "remove the documents" means here.
|
|
"""
|
|
import io
|
|
import json
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
from pathlib import Path
|
|
from pypdf import PdfReader
|
|
|
|
JEI = Path(r"C:\Users\SJ\.minimax-agent\projects\JEI")
|
|
WORKFLOW = Path(r"C:\Users\SJ\.minimax-agent\projects\Workflow")
|
|
BACKUP = WORKFLOW / "scripts" / "_backup_ufc_docs"
|
|
|
|
env = {}
|
|
for line in (JEI / ".env.local").read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if line and not line.startswith("#") and "=" in line:
|
|
k, v = line.split("=", 1)
|
|
env[k.strip()] = v.strip().strip('"').strip("'")
|
|
PB = env.get("NEXT_PUBLIC_PB_URL") or env["PB_URL"]
|
|
TOK = env["PB_TOKEN"]
|
|
|
|
TARGETS = [
|
|
("pbc_266170881", "e0dbxprazb70rqx", "signed_letter", "resolution_signed_letter"),
|
|
("ysterbaard_director_resignations", "8cfie18uhgy0n0o", "letter", "resignation_letter"),
|
|
("ysterbaard_director_resignations", "8cfie18uhgy0n0o", "signature", "resignation_signature"),
|
|
]
|
|
|
|
APPLY = "--apply" in __import__("sys").argv
|
|
|
|
|
|
def req(url, method="GET", body=None, ctype="application/json"):
|
|
r = urllib.request.Request(url, data=body, method=method,
|
|
headers={"Authorization": TOK, "Content-Type": ctype})
|
|
try:
|
|
with urllib.request.urlopen(r, timeout=120) as resp:
|
|
return resp.status, resp.read()
|
|
except urllib.error.HTTPError as e:
|
|
return e.code, e.read()
|
|
|
|
|
|
BACKUP.mkdir(parents=True, exist_ok=True)
|
|
|
|
for col, rid, field, label in TARGETS:
|
|
status, raw = req(f"{PB}/api/collections/{col}/records/{rid}")
|
|
rec = json.loads(raw)
|
|
fname = rec.get(field)
|
|
if not fname:
|
|
print(f"{label}: already empty")
|
|
continue
|
|
|
|
# 1) Back the bytes up locally.
|
|
furl = f"{PB}/api/files/{col}/{rid}/{urllib.parse.quote(fname)}"
|
|
status, blob = req(furl)
|
|
if status == 200:
|
|
out = BACKUP / f"{label}_{fname}"
|
|
out.write_bytes(blob)
|
|
try:
|
|
rd = PdfReader(io.BytesIO(blob))
|
|
imgs = sum(len(p.images) for p in rd.pages)
|
|
pages = len(rd.pages)
|
|
except Exception:
|
|
imgs, pages = "?", "?"
|
|
print(f"{label}: backed up {len(blob)} B -> {out.name} (pages={pages}, signature_images={imgs})")
|
|
else:
|
|
print(f"{label}: BACKUP FAILED HTTP {status} — aborting, nothing cleared")
|
|
raise SystemExit(1)
|
|
|
|
if not APPLY:
|
|
print(f" (dry run) would clear {field} on {col}/{rid}")
|
|
continue
|
|
|
|
# 2) Clear the file field.
|
|
status, raw = req(
|
|
f"{PB}/api/collections/{col}/records/{rid}",
|
|
method="PATCH",
|
|
body=json.dumps({field: ""}).encode(),
|
|
)
|
|
if status != 200:
|
|
print(f" FAILED to clear {field}: HTTP {status} {raw[:200]}")
|
|
continue
|
|
now = json.loads(raw).get(field)
|
|
print(f" cleared {field} -> {now!r}")
|
|
|
|
if not APPLY:
|
|
print("\nDRY RUN — re-run with --apply")
|
|
else:
|
|
print("\n=== final state ===")
|
|
for col, rid, field, label in TARGETS:
|
|
_, raw = req(f"{PB}/api/collections/{col}/records/{rid}")
|
|
print(f" {label:26s} {field:15s} = {json.loads(raw).get(field)!r}")
|
|
print(f"\nlocal backups in: {BACKUP}") |