Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files
app.py
CHANGED
|
@@ -1,24 +1,25 @@
|
|
| 1 |
-
from flask import Flask, request, send_file,
|
| 2 |
from flask_cors import CORS
|
| 3 |
import subprocess
|
| 4 |
import os
|
| 5 |
import uuid
|
| 6 |
import re
|
| 7 |
-
import shutil
|
| 8 |
|
| 9 |
app = Flask(__name__)
|
| 10 |
CORS(app)
|
| 11 |
|
|
|
|
| 12 |
BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space"
|
| 13 |
|
| 14 |
@app.route('/')
|
| 15 |
def home():
|
| 16 |
-
return "SimpleBit Engine is Online
|
| 17 |
|
| 18 |
@app.route('/sign', methods=['POST'])
|
| 19 |
def sign():
|
| 20 |
job_id = str(uuid.uuid4())[:8]
|
| 21 |
os.makedirs(job_id, exist_ok=True)
|
|
|
|
| 22 |
|
| 23 |
ipa = request.files['ipa']
|
| 24 |
p12 = request.files['p12']
|
|
@@ -34,27 +35,43 @@ def sign():
|
|
| 34 |
p12.save(p12_path)
|
| 35 |
prov.save(prov_path)
|
| 36 |
|
| 37 |
-
#
|
| 38 |
-
# This is the most compatible way to run zsign on Linux
|
| 39 |
command = f"zsign -k '{p12_path}' -p '{password}' -m '{prov_path}' -o '{out_path}' -z 0 '{ipa_path}'"
|
| 40 |
|
| 41 |
try:
|
| 42 |
-
#
|
| 43 |
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
| 44 |
|
| 45 |
if result.returncode != 0:
|
| 46 |
-
|
| 47 |
-
return f"Zsign Error (Code {result.returncode}): {result.stdout} {result.stderr}", 500
|
| 48 |
|
| 49 |
-
#
|
| 50 |
match = re.search(r"Bundle Id:\s+([\w\.-]+)", result.stdout)
|
| 51 |
bundle_id = match.group(1) if match else "com.simplebit.dynamic"
|
| 52 |
|
|
|
|
| 53 |
with open(os.path.join(job_id, "bundle_id.txt"), "w") as f:
|
| 54 |
f.write(bundle_id)
|
| 55 |
|
| 56 |
plist_url = f"{BASE_URL}/plist/{job_id}"
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
except Exception as e:
|
| 60 |
return f"System Error: {str(e)}", 500
|
|
@@ -101,11 +118,17 @@ def serve_plist(job_id):
|
|
| 101 |
</dict>
|
| 102 |
</plist>"""
|
| 103 |
|
| 104 |
-
|
|
|
|
| 105 |
|
| 106 |
@app.route('/download/<job_id>')
|
| 107 |
def download_ipa(job_id):
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
if __name__ == "__main__":
|
| 111 |
app.run(host="0.0.0.0", port=7860)
|
|
|
|
| 1 |
+
from flask import Flask, request, send_file, Response
|
| 2 |
from flask_cors import CORS
|
| 3 |
import subprocess
|
| 4 |
import os
|
| 5 |
import uuid
|
| 6 |
import re
|
|
|
|
| 7 |
|
| 8 |
app = Flask(__name__)
|
| 9 |
CORS(app)
|
| 10 |
|
| 11 |
+
# Base URL for your Hugging Face Space - Ensure this is the direct 'hf.space' link
|
| 12 |
BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space"
|
| 13 |
|
| 14 |
@app.route('/')
|
| 15 |
def home():
|
| 16 |
+
return "SimpleBit Engine is Online!"
|
| 17 |
|
| 18 |
@app.route('/sign', methods=['POST'])
|
| 19 |
def sign():
|
| 20 |
job_id = str(uuid.uuid4())[:8]
|
| 21 |
os.makedirs(job_id, exist_ok=True)
|
| 22 |
+
os.chmod(job_id, 0o777) # Ensure the folder is writable for signing
|
| 23 |
|
| 24 |
ipa = request.files['ipa']
|
| 25 |
p12 = request.files['p12']
|
|
|
|
| 35 |
p12.save(p12_path)
|
| 36 |
prov.save(prov_path)
|
| 37 |
|
| 38 |
+
# Optimized zsign command for speed and compatibility
|
|
|
|
| 39 |
command = f"zsign -k '{p12_path}' -p '{password}' -m '{prov_path}' -o '{out_path}' -z 0 '{ipa_path}'"
|
| 40 |
|
| 41 |
try:
|
| 42 |
+
# Run zsign and capture output to extract the real Bundle ID
|
| 43 |
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
| 44 |
|
| 45 |
if result.returncode != 0:
|
| 46 |
+
return f"Zsign Failed (Code {result.returncode}): {result.stderr}", 500
|
|
|
|
| 47 |
|
| 48 |
+
# Extract the real Bundle ID so the itms-services handshake doesn't fail
|
| 49 |
match = re.search(r"Bundle Id:\s+([\w\.-]+)", result.stdout)
|
| 50 |
bundle_id = match.group(1) if match else "com.simplebit.dynamic"
|
| 51 |
|
| 52 |
+
# Save the Bundle ID to a file for the /plist route
|
| 53 |
with open(os.path.join(job_id, "bundle_id.txt"), "w") as f:
|
| 54 |
f.write(bundle_id)
|
| 55 |
|
| 56 |
plist_url = f"{BASE_URL}/plist/{job_id}"
|
| 57 |
+
|
| 58 |
+
# User-facing landing page to prevent "Silent Failure" on Safari
|
| 59 |
+
return f'''
|
| 60 |
+
<html>
|
| 61 |
+
<head><meta name="viewport" content="width=device-width, initial-scale=1"></head>
|
| 62 |
+
<body style="display:flex; flex-direction:column; align-items:center; justify-content:center; height:100vh; font-family:-apple-system, sans-serif; background-color:#f2f2f7;">
|
| 63 |
+
<div style="background:white; padding:30px; border-radius:20px; box-shadow:0 10px 25px rgba(0,0,0,0.1); text-align:center;">
|
| 64 |
+
<h2 style="color:#1c1c1e; margin-bottom:10px;">Ready to Install</h2>
|
| 65 |
+
<p style="color:#8e8e93; margin-bottom:25px;">Bundle ID: <b>{bundle_id}</b></p>
|
| 66 |
+
<a href="itms-services://?action=download-manifest&url={plist_url}"
|
| 67 |
+
style="background:#007AFF; color:white; padding:15px 40px; text-decoration:none; border-radius:12px; font-weight:600; display:inline-block;">
|
| 68 |
+
Install App
|
| 69 |
+
</a>
|
| 70 |
+
</div>
|
| 71 |
+
<p style="margin-top:20px; color:#8e8e93; font-size:12px;">Make sure you are using Safari on iOS.</p>
|
| 72 |
+
</body>
|
| 73 |
+
</html>
|
| 74 |
+
'''
|
| 75 |
|
| 76 |
except Exception as e:
|
| 77 |
return f"System Error: {str(e)}", 500
|
|
|
|
| 118 |
</dict>
|
| 119 |
</plist>"""
|
| 120 |
|
| 121 |
+
# Return with correct XML mimetype for iOS recognition
|
| 122 |
+
return Response(plist_content, mimetype='application/xml')
|
| 123 |
|
| 124 |
@app.route('/download/<job_id>')
|
| 125 |
def download_ipa(job_id):
|
| 126 |
+
# Serve as octet-stream to ensure binary download
|
| 127 |
+
return send_file(
|
| 128 |
+
os.path.join(job_id, "signed.ipa"),
|
| 129 |
+
mimetype='application/octet-stream',
|
| 130 |
+
as_attachment=True
|
| 131 |
+
)
|
| 132 |
|
| 133 |
if __name__ == "__main__":
|
| 134 |
app.run(host="0.0.0.0", port=7860)
|