Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
112 lines
3.9 KiB
Python
112 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Deploy tenant-site feature + SSL for torbatkaracademy."""
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import paramiko
|
|
|
|
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
PASS = "Moli@5404"
|
|
REMOTE = "/home/morteza/torbatyar"
|
|
|
|
|
|
def connect(host, user):
|
|
c = paramiko.SSHClient()
|
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
c.connect(host, username=user, password=PASS, timeout=30, allow_agent=False, look_for_keys=False)
|
|
return c
|
|
|
|
|
|
def run(c, cmd, sudo=False, timeout=300):
|
|
if sudo:
|
|
b64 = base64.b64encode(cmd.encode()).decode()
|
|
full = f"echo {PASS!r} | sudo -S -p '' bash -c 'echo {b64} | base64 -d | bash'"
|
|
else:
|
|
full = cmd
|
|
_, o, e = c.exec_command(full, timeout=timeout, get_pty=True)
|
|
text = (o.read() + e.read()).decode("utf-8", "replace")
|
|
print(text[-6000:] if len(text) > 6000 else text)
|
|
return text
|
|
|
|
|
|
def main():
|
|
files = [
|
|
"backend/core-service/app/api/v1/public_tenant.py",
|
|
"backend/core-service/app/api/v1/__init__.py",
|
|
"frontend/app/page.tsx",
|
|
"frontend/app/auth/callback/page.tsx",
|
|
"frontend/components/SiteHeader.tsx",
|
|
"frontend/components/TenantSitePage.tsx",
|
|
"frontend/lib/api.ts",
|
|
"frontend/lib/auth.ts",
|
|
"frontend/lib/tenant-host.ts",
|
|
"frontend/hooks/useTenantHost.ts",
|
|
"docker-compose.yml",
|
|
"infrastructure/deploy/.env.production.example",
|
|
"infrastructure/nginx/torbatyar.ir.conf",
|
|
]
|
|
app = connect("192.168.10.162", "morteza")
|
|
sftp = app.open_sftp()
|
|
for rel in files:
|
|
if rel.startswith("infrastructure/nginx"):
|
|
continue
|
|
local = ROOT / rel
|
|
remote = f"{REMOTE}/{rel}"
|
|
# ensure parent
|
|
sftp.put(str(local), remote)
|
|
print("up", rel)
|
|
# update .env with PLATFORM_BASE_DOMAIN
|
|
try:
|
|
with sftp.file(f"{REMOTE}/.env", "r") as f:
|
|
env = f.read().decode("utf-8", "replace")
|
|
except Exception:
|
|
env = (ROOT / "infrastructure/deploy/.env.production.example").read_text(encoding="utf-8")
|
|
if "NEXT_PUBLIC_PLATFORM_BASE_DOMAIN" not in env:
|
|
env += "\nNEXT_PUBLIC_PLATFORM_BASE_DOMAIN=torbatyar.ir\n"
|
|
with sftp.file(f"{REMOTE}/.env", "w") as f:
|
|
f.write(env)
|
|
sftp.close()
|
|
|
|
run(
|
|
app,
|
|
f"""
|
|
cd {REMOTE}
|
|
docker compose up -d --force-recreate core-service frontend
|
|
sleep 15
|
|
curl -sS -m 10 'http://127.0.0.1:8000/api/v1/public/tenant-site?slug=torbatkaracademy' -H 'Host: torbatkaracademy.torbatyar.ir'
|
|
echo
|
|
curl -sS -m 10 -o /dev/null -w 'fe=%{{http_code}}\\n' -H 'Host: torbatkaracademy.torbatyar.ir' http://127.0.0.1:3000/
|
|
""",
|
|
sudo=True,
|
|
timeout=240,
|
|
)
|
|
app.close()
|
|
|
|
ngx = connect("192.168.10.156", "torbatyaruser")
|
|
conf = (ROOT / "infrastructure/nginx/torbatyar.ir.conf").read_text(encoding="utf-8")
|
|
b64 = base64.b64encode(conf.encode()).decode()
|
|
run(
|
|
ngx,
|
|
f"""
|
|
echo {b64} | base64 -d > /etc/nginx/conf.d/torbatyar.ir.conf
|
|
certbot certonly --webroot -w /var/www/html --expand --non-interactive --agree-tos --email support@torbatyar.ir \
|
|
-d torbatyar.ir -d www.torbatyar.ir -d api.torbatyar.ir -d identity.torbatyar.ir -d auth.torbatyar.ir \
|
|
-d torbatkaracademy.torbatyar.ir 2>&1 | tail -n 30
|
|
nginx -t && systemctl reload nginx && echo RELOADED
|
|
curl -sk -m 15 -o /dev/null -w 'tenant_https=%{{http_code}}\\n' --resolve torbatkaracademy.torbatyar.ir:443:192.168.10.156 https://torbatkaracademy.torbatyar.ir/
|
|
curl -sk -m 15 --resolve api.torbatyar.ir:443:192.168.10.156 'https://api.torbatyar.ir/api/v1/public/tenant-site?slug=torbatkaracademy'
|
|
echo
|
|
""",
|
|
sudo=True,
|
|
timeout=300,
|
|
)
|
|
ngx.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|