Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -16,6 +16,9 @@ CORS(app)
|
|
| 16 |
BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space"
|
| 17 |
|
| 18 |
|
|
|
|
|
|
|
|
|
|
| 19 |
@app.route("/")
|
| 20 |
def home():
|
| 21 |
return jsonify({
|
|
@@ -25,22 +28,19 @@ def home():
|
|
| 25 |
|
| 26 |
|
| 27 |
# -------------------------------------------------
|
| 28 |
-
#
|
| 29 |
# -------------------------------------------------
|
| 30 |
def get_actual_bundle_id(ipa_path):
|
|
|
|
| 31 |
try:
|
| 32 |
with zipfile.ZipFile(ipa_path, "r") as ipa:
|
| 33 |
|
| 34 |
for name in ipa.namelist():
|
| 35 |
|
| 36 |
-
if (
|
| 37 |
-
name.startswith("Payload/")
|
| 38 |
-
and name.count("/") == 2
|
| 39 |
-
and name.endswith(".app/Info.plist")
|
| 40 |
-
):
|
| 41 |
with ipa.open(name) as f:
|
| 42 |
plist_data = plistlib.load(f)
|
| 43 |
-
|
| 44 |
|
| 45 |
except Exception as e:
|
| 46 |
print("Bundle error:", e)
|
|
@@ -49,22 +49,21 @@ def get_actual_bundle_id(ipa_path):
|
|
| 49 |
|
| 50 |
|
| 51 |
# -------------------------------------------------
|
| 52 |
-
#
|
| 53 |
# -------------------------------------------------
|
| 54 |
-
def
|
| 55 |
-
with open(job_dir / "bundle_id.txt", "w") as f:
|
| 56 |
-
f.write(bundle_id)
|
| 57 |
|
|
|
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
|
| 65 |
|
| 66 |
# -------------------------------------------------
|
| 67 |
-
# SIGN
|
| 68 |
# -------------------------------------------------
|
| 69 |
@app.route("/sign", methods=["POST"])
|
| 70 |
def sign():
|
|
@@ -103,33 +102,27 @@ def sign():
|
|
| 103 |
bundle_id = get_actual_bundle_id(ipa_path) or "com.simplebit.dynamic"
|
| 104 |
|
| 105 |
# -------------------------------------------------
|
| 106 |
-
# Extract IPA
|
| 107 |
# -------------------------------------------------
|
| 108 |
temp_dir = tempfile.mkdtemp()
|
| 109 |
|
| 110 |
with zipfile.ZipFile(ipa_path, "r") as zip_ref:
|
| 111 |
zip_ref.extractall(temp_dir)
|
| 112 |
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
if not payload_path.exists():
|
| 116 |
-
shutil.rmtree(temp_dir)
|
| 117 |
-
return jsonify({"error": "Payload missing"}), 500
|
| 118 |
|
| 119 |
-
|
| 120 |
-
for item in payload_path.iterdir():
|
| 121 |
-
if item.suffix == ".app":
|
| 122 |
-
app_folder = item
|
| 123 |
-
break
|
| 124 |
|
| 125 |
-
if not
|
| 126 |
shutil.rmtree(temp_dir)
|
| 127 |
-
return jsonify({
|
|
|
|
|
|
|
| 128 |
|
| 129 |
-
print("APP:",
|
| 130 |
|
| 131 |
# -------------------------------------------------
|
| 132 |
-
#
|
| 133 |
# -------------------------------------------------
|
| 134 |
command = [
|
| 135 |
zsign_path,
|
|
@@ -137,7 +130,7 @@ def sign():
|
|
| 137 |
"-k", str(p12_path),
|
| 138 |
"-p", password,
|
| 139 |
"-m", str(prov_path),
|
| 140 |
-
str(
|
| 141 |
]
|
| 142 |
|
| 143 |
result = subprocess.run(
|
|
@@ -158,7 +151,7 @@ def sign():
|
|
| 158 |
}), 500
|
| 159 |
|
| 160 |
# -------------------------------------------------
|
| 161 |
-
#
|
| 162 |
# -------------------------------------------------
|
| 163 |
shutil.make_archive(
|
| 164 |
str(job_dir / "signed"),
|
|
@@ -170,10 +163,10 @@ def sign():
|
|
| 170 |
|
| 171 |
shutil.rmtree(temp_dir)
|
| 172 |
|
| 173 |
-
|
|
|
|
| 174 |
|
| 175 |
plist_url = f"{BASE_URL}/plist/{job_id}"
|
| 176 |
-
|
| 177 |
install_url = f"itms-services://?action=download-manifest&url={plist_url}"
|
| 178 |
|
| 179 |
return redirect(install_url)
|
|
@@ -190,13 +183,17 @@ def plist(job_id):
|
|
| 190 |
|
| 191 |
job_dir = Path(job_id)
|
| 192 |
|
| 193 |
-
bundle_id =
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
ipa_url = f"{BASE_URL}/download/{job_id}"
|
| 196 |
|
| 197 |
plist_content = f"""<?xml version="1.0" encoding="UTF-8"?>
|
| 198 |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
| 199 |
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
|
|
| 200 |
<plist version="1.0">
|
| 201 |
<dict>
|
| 202 |
<key>items</key>
|
|
@@ -225,17 +222,17 @@ def plist(job_id):
|
|
| 225 |
</dict>
|
| 226 |
</array>
|
| 227 |
</dict>
|
| 228 |
-
</plist>
|
|
|
|
| 229 |
|
| 230 |
plist_path = job_dir / "manifest.plist"
|
| 231 |
-
|
| 232 |
plist_path.write_text(plist_content)
|
| 233 |
|
| 234 |
return send_file(plist_path, mimetype="application/xml")
|
| 235 |
|
| 236 |
|
| 237 |
# -------------------------------------------------
|
| 238 |
-
# DOWNLOAD IPA (
|
| 239 |
# -------------------------------------------------
|
| 240 |
@app.route("/download/<job_id>")
|
| 241 |
def download(job_id):
|
|
@@ -245,7 +242,11 @@ def download(job_id):
|
|
| 245 |
if not ipa_path.exists():
|
| 246 |
return jsonify({"error": "IPA not found"}), 404
|
| 247 |
|
| 248 |
-
response = send_file(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
response.headers["Content-Disposition"] = "inline; filename=signed.ipa"
|
| 250 |
return response
|
| 251 |
|
|
@@ -261,5 +262,8 @@ def health():
|
|
| 261 |
})
|
| 262 |
|
| 263 |
|
|
|
|
|
|
|
|
|
|
| 264 |
if __name__ == "__main__":
|
| 265 |
app.run(host="0.0.0.0", port=7860, debug=True)
|
|
|
|
| 16 |
BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space"
|
| 17 |
|
| 18 |
|
| 19 |
+
# -------------------------------------------------
|
| 20 |
+
# HOME
|
| 21 |
+
# -------------------------------------------------
|
| 22 |
@app.route("/")
|
| 23 |
def home():
|
| 24 |
return jsonify({
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
# -------------------------------------------------
|
| 31 |
+
# GET BUNDLE ID (safe)
|
| 32 |
# -------------------------------------------------
|
| 33 |
def get_actual_bundle_id(ipa_path):
|
| 34 |
+
|
| 35 |
try:
|
| 36 |
with zipfile.ZipFile(ipa_path, "r") as ipa:
|
| 37 |
|
| 38 |
for name in ipa.namelist():
|
| 39 |
|
| 40 |
+
if name.endswith("Info.plist") and ".app/" in name:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
with ipa.open(name) as f:
|
| 42 |
plist_data = plistlib.load(f)
|
| 43 |
+
return plist_data.get("CFBundleIdentifier")
|
| 44 |
|
| 45 |
except Exception as e:
|
| 46 |
print("Bundle error:", e)
|
|
|
|
| 49 |
|
| 50 |
|
| 51 |
# -------------------------------------------------
|
| 52 |
+
# FIND APP BUNDLE ANYWHERE (FIXED)
|
| 53 |
# -------------------------------------------------
|
| 54 |
+
def find_app_bundle(temp_dir):
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
temp_dir = Path(temp_dir)
|
| 57 |
|
| 58 |
+
for item in temp_dir.rglob("*.app"):
|
| 59 |
+
if item.is_dir():
|
| 60 |
+
return item
|
| 61 |
+
|
| 62 |
+
return None
|
| 63 |
|
| 64 |
|
| 65 |
# -------------------------------------------------
|
| 66 |
+
# SIGN ROUTE
|
| 67 |
# -------------------------------------------------
|
| 68 |
@app.route("/sign", methods=["POST"])
|
| 69 |
def sign():
|
|
|
|
| 102 |
bundle_id = get_actual_bundle_id(ipa_path) or "com.simplebit.dynamic"
|
| 103 |
|
| 104 |
# -------------------------------------------------
|
| 105 |
+
# Extract IPA
|
| 106 |
# -------------------------------------------------
|
| 107 |
temp_dir = tempfile.mkdtemp()
|
| 108 |
|
| 109 |
with zipfile.ZipFile(ipa_path, "r") as zip_ref:
|
| 110 |
zip_ref.extractall(temp_dir)
|
| 111 |
|
| 112 |
+
print("EXTRACTED FILES:", list(Path(temp_dir).rglob("*")))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
+
app_bundle = find_app_bundle(temp_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
+
if not app_bundle:
|
| 117 |
shutil.rmtree(temp_dir)
|
| 118 |
+
return jsonify({
|
| 119 |
+
"error": "No .app bundle found in IPA"
|
| 120 |
+
}), 500
|
| 121 |
|
| 122 |
+
print("FOUND APP:", app_bundle)
|
| 123 |
|
| 124 |
# -------------------------------------------------
|
| 125 |
+
# SIGN APP
|
| 126 |
# -------------------------------------------------
|
| 127 |
command = [
|
| 128 |
zsign_path,
|
|
|
|
| 130 |
"-k", str(p12_path),
|
| 131 |
"-p", password,
|
| 132 |
"-m", str(prov_path),
|
| 133 |
+
str(app_bundle)
|
| 134 |
]
|
| 135 |
|
| 136 |
result = subprocess.run(
|
|
|
|
| 151 |
}), 500
|
| 152 |
|
| 153 |
# -------------------------------------------------
|
| 154 |
+
# REPACK IPA
|
| 155 |
# -------------------------------------------------
|
| 156 |
shutil.make_archive(
|
| 157 |
str(job_dir / "signed"),
|
|
|
|
| 163 |
|
| 164 |
shutil.rmtree(temp_dir)
|
| 165 |
|
| 166 |
+
# Save bundle ID
|
| 167 |
+
(job_dir / "bundle_id.txt").write_text(bundle_id)
|
| 168 |
|
| 169 |
plist_url = f"{BASE_URL}/plist/{job_id}"
|
|
|
|
| 170 |
install_url = f"itms-services://?action=download-manifest&url={plist_url}"
|
| 171 |
|
| 172 |
return redirect(install_url)
|
|
|
|
| 183 |
|
| 184 |
job_dir = Path(job_id)
|
| 185 |
|
| 186 |
+
bundle_id = "com.simplebit.dynamic"
|
| 187 |
+
|
| 188 |
+
if (job_dir / "bundle_id.txt").exists():
|
| 189 |
+
bundle_id = (job_dir / "bundle_id.txt").read_text().strip()
|
| 190 |
|
| 191 |
ipa_url = f"{BASE_URL}/download/{job_id}"
|
| 192 |
|
| 193 |
plist_content = f"""<?xml version="1.0" encoding="UTF-8"?>
|
| 194 |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
| 195 |
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
| 196 |
+
|
| 197 |
<plist version="1.0">
|
| 198 |
<dict>
|
| 199 |
<key>items</key>
|
|
|
|
| 222 |
</dict>
|
| 223 |
</array>
|
| 224 |
</dict>
|
| 225 |
+
</plist>
|
| 226 |
+
"""
|
| 227 |
|
| 228 |
plist_path = job_dir / "manifest.plist"
|
|
|
|
| 229 |
plist_path.write_text(plist_content)
|
| 230 |
|
| 231 |
return send_file(plist_path, mimetype="application/xml")
|
| 232 |
|
| 233 |
|
| 234 |
# -------------------------------------------------
|
| 235 |
+
# DOWNLOAD IPA (iOS SAFE)
|
| 236 |
# -------------------------------------------------
|
| 237 |
@app.route("/download/<job_id>")
|
| 238 |
def download(job_id):
|
|
|
|
| 242 |
if not ipa_path.exists():
|
| 243 |
return jsonify({"error": "IPA not found"}), 404
|
| 244 |
|
| 245 |
+
response = send_file(
|
| 246 |
+
ipa_path,
|
| 247 |
+
mimetype="application/octet-stream"
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
response.headers["Content-Disposition"] = "inline; filename=signed.ipa"
|
| 251 |
return response
|
| 252 |
|
|
|
|
| 262 |
})
|
| 263 |
|
| 264 |
|
| 265 |
+
# -------------------------------------------------
|
| 266 |
+
# RUN
|
| 267 |
+
# -------------------------------------------------
|
| 268 |
if __name__ == "__main__":
|
| 269 |
app.run(host="0.0.0.0", port=7860, debug=True)
|