All checks were successful
Deploy Plane Project Tracker / Deploy to cisystem (push) Successful in 55s
- Check local disk first in patch_frontend.py to avoid remote HTTP retrieval - Use robust streaming with timeout and user-agent fallback instead of urlretrieve - Include pre-patched frontend JS files in deploy/patches - Add safe fallback in install.sh so optional patch generation does not fail deployment
133 lines
6.3 KiB
Python
133 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import urllib.request
|
|
|
|
def extract_or_fetch(filename, out_path, image="makeplane/plane-frontend:stable"):
|
|
if os.path.exists(out_path) and os.path.getsize(out_path) > 50:
|
|
print(f"Using existing {filename} on host ({os.path.getsize(out_path)} bytes)")
|
|
return True
|
|
|
|
for base_dir in ["/usr/share/caddy/html/assets", "/usr/share/nginx/html/assets"]:
|
|
src_container_path = f"{base_dir}/{filename}"
|
|
try:
|
|
res = subprocess.run(
|
|
["docker", "run", "--rm", image, "cat", src_container_path],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
check=True,
|
|
)
|
|
if len(res.stdout) > 50:
|
|
with open(out_path, "wb") as f:
|
|
f.write(res.stdout)
|
|
print(f"Extracted {filename} from {image} ({len(res.stdout)} bytes)")
|
|
return True
|
|
except Exception:
|
|
continue
|
|
|
|
url = f"https://jira.tradhox.com/assets/{filename}"
|
|
try:
|
|
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
content = resp.read()
|
|
if len(content) > 50:
|
|
with open(out_path, "wb") as f:
|
|
f.write(content)
|
|
print(f"Fetched {filename} from {url} ({len(content)} bytes)")
|
|
return True
|
|
except Exception as e:
|
|
print(f"Notice: Could not fetch {filename} from {url}: {e}")
|
|
|
|
# Fallback stub to satisfy docker-compose volume mounts if needed
|
|
with open(out_path, "w", encoding="utf-8") as f:
|
|
f.write("// frontend patch stub\n")
|
|
print(f"Created fallback stub for {filename}")
|
|
return False
|
|
|
|
def patch_page(file_path):
|
|
if not os.path.exists(file_path):
|
|
return
|
|
try:
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
code = f.read()
|
|
|
|
# 1. Replace Community with Enterprise in top box
|
|
old_box = "(0,L.jsx)(A,{title:`Community`,description:`Unlimited projects, issues, cycles, modules, pages, and storage`})"
|
|
new_box = "(0,L.jsx)(A,{title:`Enterprise`,description:`Enterprise Edition Active — Unlimited users, projects, issues, cycles, modules, pages, storage, and all enterprise features unlocked`})"
|
|
if old_box in code:
|
|
code = code.replace(old_box, new_box)
|
|
|
|
# 2. Update W for enterprise
|
|
old_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`billed monthly`,yearlyPriceSecondaryDescription:`billed yearly`,buttonCTA:`Talk to Sales`,isActive:!1}"
|
|
new_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`Active Plan`,yearlyPriceSecondaryDescription:`Active Plan`,buttonCTA:`Active Plan`,isActive:!0}"
|
|
if old_ent in code:
|
|
code = code.replace(old_ent, new_ent)
|
|
|
|
# 3. Update Y pricing descriptions and button
|
|
old_ph = "p=d?`a user per month`:`Quote on request`,h=r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
|
|
new_ph = "p=t===o.ENTERPRISE?`Active Lifetime License`:d?`a user per month`:`Quote on request`,h=t===o.ENTERPRISE?`All features unlocked`:r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
|
|
if old_ph in code:
|
|
code = code.replace(old_ph, new_ph)
|
|
|
|
old_btn = "(0,L.jsx)(T,{variant:`primary`,size:`lg`,onClick:()=>{let e=r??`year`,n=l[t][e]??`https://plane.so/talk-to-sales`;window.open(n,`_blank`)},className:`w-full`,children:d?`Upgrade to ${u}`:a(`common.upgrade_cta.talk_to_sales`)})"
|
|
new_btn = "(0,L.jsx)(T,{variant:t===o.ENTERPRISE?`primary`:`secondary`,size:`lg`,disabled:!0,className:t===o.ENTERPRISE?`w-full opacity-95 cursor-default`:`w-full opacity-60 cursor-default`,children:t===o.ENTERPRISE?`Current Plan (Active)`:`Included in Enterprise`})"
|
|
if old_btn in code:
|
|
code = code.replace(old_btn, new_btn)
|
|
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
f.write(code)
|
|
print(f"Patched page successfully: {file_path} ({len(code)} bytes)")
|
|
except Exception as e:
|
|
print(f"Notice: Could not patch page {file_path}: {e}")
|
|
|
|
def patch_layout(file_path):
|
|
if not os.path.exists(file_path):
|
|
return
|
|
try:
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
code = f.read()
|
|
|
|
old_badge = 'onClick:()=>t(!0),"aria-haspopup":`dialog`,"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Community`'
|
|
new_badge = 'onClick:()=>{},"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Enterprise`'
|
|
if old_badge in code:
|
|
code = code.replace(old_badge, new_badge)
|
|
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
f.write(code)
|
|
print(f"Patched layout successfully: {file_path} ({len(code)} bytes)")
|
|
except Exception as e:
|
|
print(f"Notice: Could not patch layout {file_path}: {e}")
|
|
|
|
def patch_badge(file_path):
|
|
try:
|
|
# The upgrade badge returns null so no "Pro" badges render anywhere
|
|
new_code = 'import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};\n'
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
f.write(new_code)
|
|
print(f"Patched badge successfully: {file_path} ({len(new_code)} bytes)")
|
|
except Exception as e:
|
|
print(f"Notice: Could not patch badge {file_path}: {e}")
|
|
|
|
def main():
|
|
target_dir = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(os.path.abspath(__file__))
|
|
image = sys.argv[2] if len(sys.argv) > 2 else "makeplane/plane-frontend:stable"
|
|
os.makedirs(target_dir, exist_ok=True)
|
|
|
|
page_file = os.path.join(target_dir, "page-BEQX0L4U.js")
|
|
layout_file = os.path.join(target_dir, "layout-C0Zt7niA.js")
|
|
badge_file = os.path.join(target_dir, "upgrade-badge-BxAQzHXC.js")
|
|
|
|
extract_or_fetch("page-BEQX0L4U.js", page_file, image)
|
|
patch_page(page_file)
|
|
|
|
extract_or_fetch("layout-C0Zt7niA.js", layout_file, image)
|
|
patch_layout(layout_file)
|
|
|
|
extract_or_fetch("upgrade-badge-BxAQzHXC.js", badge_file, image)
|
|
patch_badge(badge_file)
|
|
|
|
print("All frontend chunks successfully patched!")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|