from flask import Flask, request, send_file, jsonify, redirect from flask_cors import CORS import subprocess import plistlib import zipfile import shutil import uuid import tempfile from pathlib import Path app = Flask(__name__) CORS(app) BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space" # ------------------------------------------------- # HOME # ------------------------------------------------- @app.route("/") def home(): return jsonify({"status": "online"}) # ------------------------------------------------- # FIND .APP BUNDLE # ------------------------------------------------- def find_app_bundle(temp_dir): for item in Path(temp_dir).rglob("*.app"): if item.is_dir(): return item return None # ------------------------------------------------- # READ INFO.PLIST # ------------------------------------------------- def read_info_plist(app_bundle): for f in Path(app_bundle).rglob("Info.plist"): with open(f, "rb") as fp: return f, plistlib.load(fp) return None, None def write_info_plist(plist_path, data): with open(plist_path, "wb") as fp: plistlib.dump(data, fp) # ------------------------------------------------- # SIGN ROUTE # ------------------------------------------------- @app.route("/sign", methods=["POST"]) def sign(): try: if "ipa" not in request.files: return jsonify({"error": "Missing IPA"}), 400 if "p12" not in request.files: return jsonify({"error": "Missing P12"}), 400 if "provision" not in request.files: return jsonify({"error": "Missing provisioning profile"}), 400 zsign_path = shutil.which("zsign") if not zsign_path: return jsonify({"error": "zsign not installed"}), 500 job_id = str(uuid.uuid4())[:8] job_dir = Path(job_id) job_dir.mkdir(exist_ok=True) ipa_path = job_dir / "app.ipa" p12_path = job_dir / "cert.p12" prov_path = job_dir / "profile.mobileprovision" signed_path = job_dir / "signed.ipa" request.files["ipa"].save(ipa_path) request.files["p12"].save(p12_path) request.files["provision"].save(prov_path) password = request.form.get("password", "") # ------------------------------------------------- # EXTRACT IPA # ------------------------------------------------- temp_dir = tempfile.mkdtemp() with zipfile.ZipFile(ipa_path, "r") as zip_ref: zip_ref.extractall(temp_dir) app_bundle = find_app_bundle(temp_dir) if not app_bundle: shutil.rmtree(temp_dir) return jsonify({"error": "No .app found"}), 500 # ------------------------------------------------- # READ ORIGINAL INFO # ------------------------------------------------- plist_path, plist_data = read_info_plist(app_bundle) if not plist_data: shutil.rmtree(temp_dir) return jsonify({"error": "Info.plist missing"}), 500 original_bundle_id = plist_data.get("CFBundleIdentifier", "unknown.bundle") original_name = ( plist_data.get("CFBundleDisplayName") or plist_data.get("CFBundleName") or "App" ) # ------------------------------------------------- # DO NOT CHANGE BUNDLE ID (ENTERPRISE MODE) # ------------------------------------------------- target_bundle_id = original_bundle_id plist_data["CFBundleIdentifier"] = target_bundle_id plist_data["CFBundleDisplayName"] = original_name plist_data["CFBundleName"] = original_name write_info_plist(plist_path, plist_data) # ------------------------------------------------- # SIGN # ------------------------------------------------- command = [ zsign_path, "-f", "-k", str(p12_path), "-p", password, "-m", str(prov_path), str(app_bundle) ] result = subprocess.run(command, capture_output=True, text=True) if result.returncode != 0: shutil.rmtree(temp_dir) return jsonify({ "error": "Signing failed", "stdout": result.stdout, "stderr": result.stderr }), 500 # ------------------------------------------------- # BUILD CLEAN IPA # ------------------------------------------------- payload_dir = Path(job_dir) / "Payload" payload_dir.mkdir(exist_ok=True) shutil.copytree(app_bundle, payload_dir / app_bundle.name) zip_path = Path(job_dir) / "signed" shutil.make_archive(str(zip_path), "zip", job_dir, "Payload") shutil.move(str(zip_path) + ".zip", signed_path) shutil.rmtree(temp_dir) # save ORIGINAL bundle id for manifest (job_dir / "bundle_id.txt").write_text(original_bundle_id) plist_url = f"{BASE_URL}/plist/{job_id}" install_url = f"itms-services://?action=download-manifest&url={plist_url}" return redirect(install_url) except Exception as e: return jsonify({"error": str(e)}), 500 # ------------------------------------------------- # MANIFEST # ------------------------------------------------- @app.route("/plist/") def plist(job_id): job_dir = Path(job_id) bundle_id = "unknown" if (job_dir / "bundle_id.txt").exists(): bundle_id = (job_dir / "bundle_id.txt").read_text().strip() ipa_url = f"{BASE_URL}/download/{job_id}" plist_content = f""" items assets kind software-package url {ipa_url} metadata bundle-identifier {bundle_id} bundle-version 1.0 kind software title Signed App """ plist_path = job_dir / "manifest.plist" plist_path.write_text(plist_content) return send_file(plist_path, mimetype="application/xml") # ------------------------------------------------- # DOWNLOAD IPA # ------------------------------------------------- @app.route("/download/") def download(job_id): ipa_path = Path(job_id) / "signed.ipa" if not ipa_path.exists(): return jsonify({"error": "IPA not found"}), 404 response = send_file(ipa_path, mimetype="application/octet-stream") response.headers["Content-Disposition"] = "inline; filename=signed.ipa" return response # ------------------------------------------------- # HEALTH # ------------------------------------------------- @app.route("/health") def health(): return jsonify({"ok": True, "zsign": shutil.which("zsign") is not None}) # ------------------------------------------------- # RUN # ------------------------------------------------- if __name__ == "__main__": app.run(host="0.0.0.0", port=7860, debug=True)