sudo-soldier commited on
Commit
688982f
·
verified ·
1 Parent(s): 4e70434

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -62
app.py CHANGED
@@ -3,18 +3,27 @@ import subprocess
3
  import tempfile
4
  import shutil
5
  import re
 
6
  from flask import Flask, request, send_file, jsonify
7
  from werkzeug.utils import secure_filename
8
 
 
 
 
 
9
  app = Flask(__name__)
10
 
11
  # Configuration
12
- app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 # 1MB max upload size
13
- app.config['ALLOWED_HOSTS'] = ['https://huggingface.co/spaces/sudo-soldier/'] # Add your production domain
14
- app.config['BUBBLEWRAP_MIN_VERSION'] = '1.0.0' # Minimum required version
 
 
 
 
15
 
16
- # Validate URL format
17
  def is_valid_url(url):
 
18
  regex = re.compile(
19
  r'^(https?://)?' # http:// or https://
20
  r'([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}' # domain
@@ -23,103 +32,114 @@ def is_valid_url(url):
23
  )
24
  return re.match(regex, url) is not None
25
 
26
- # Check if bubblewrap is available
27
  def check_bubblewrap():
 
28
  try:
29
- result = subprocess.run(['bubblewrap', '--version'],
30
- capture_output=True, text=True, check=True)
 
 
 
 
31
  version = result.stdout.strip()
32
- if version < app.config['BUBBLEWRAP_MIN_VERSION']:
33
- raise Exception(f"Bubblewrap version {version} is below minimum required version")
34
  return True
35
  except Exception as e:
36
- app.logger.error(f"Bubblewrap check failed: {str(e)}")
37
  return False
38
 
39
- # Clean up directory
40
  def cleanup_directory(dir_path):
 
41
  try:
42
  if os.path.exists(dir_path):
43
  shutil.rmtree(dir_path)
 
44
  except Exception as e:
45
- app.logger.error(f"Cleanup failed for {dir_path}: {str(e)}")
46
-
47
- @app.route("/generate-apk", methods=["POST"])
48
- def generate_apk():
49
- # Validate request
50
- if 'url' not in request.form:
51
- return jsonify({"error": "URL parameter is required"}), 400
52
-
53
- pwa_url = request.form['url'].strip()
54
-
55
- if not is_valid_url(pwa_url):
56
- return jsonify({"error": "Invalid URL format"}), 400
57
 
58
- # Create temp directory
59
- temp_dir = tempfile.mkdtemp(prefix='pwa2apk_')
60
- os.chdir(temp_dir) # Work in temp directory
61
-
62
  try:
63
  # Initialize project
64
  init_cmd = [
65
  'bubblewrap', 'init',
66
  '--manifest', pwa_url,
67
- '--directory', temp_dir,
68
  '--non-interactive'
69
  ]
70
-
71
- subprocess.run(init_cmd, check=True, timeout=300,
72
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
73
-
74
  # Build APK
75
  build_cmd = ['bubblewrap', 'build']
76
- subprocess.run(build_cmd, check=True, timeout=600,
77
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
78
-
79
  # Verify APK was created
80
- apk_path = os.path.join(temp_dir, 'app-release.apk')
81
  if not os.path.exists(apk_path):
82
- raise Exception("APK file not found after build")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
- # Secure filename for download
85
  domain = pwa_url.split('//')[-1].split('/')[0].replace('.', '_')
86
  apk_name = secure_filename(f"{domain}_app.apk")
87
-
88
- # Return the file
89
  return send_file(
90
  apk_path,
91
  as_attachment=True,
92
  download_name=apk_name,
93
  mimetype='application/vnd.android.package-archive'
94
  )
95
-
96
- except subprocess.TimeoutExpired:
97
- return jsonify({"error": "APK generation timed out"}), 504
98
- except subprocess.CalledProcessError as e:
99
- app.logger.error(f"Build failed: {e.stderr}")
100
- return jsonify({"error": "APK generation failed", "details": str(e)}), 500
101
  except Exception as e:
102
- app.logger.error(f"Unexpected error: {str(e)}")
103
- return jsonify({"error": "APK generation failed", "details": str(e)}), 500
 
 
 
104
  finally:
105
- # Clean up
106
- os.chdir('/') # Change out of directory before cleanup
107
  cleanup_directory(temp_dir)
108
 
109
- @app.before_request
110
- def before_request():
111
- # Simple host validation
112
- if request.headers.get('Host') not in app.config['ALLOWED_HOSTS']:
113
- return jsonify({"error": "Unauthorized host"}), 403
114
-
115
- # Check bubblewrap is available
116
- if not check_bubblewrap():
117
- return jsonify({"error": "Service temporarily unavailable"}), 503
118
-
119
  if __name__ == "__main__":
120
  # Verify dependencies at startup
121
  if not check_bubblewrap():
122
- print("Error: Bubblewrap not available or version too old")
123
  exit(1)
124
 
125
- app.run(host="0.0.0.0", port=7860, threaded=True)
 
3
  import tempfile
4
  import shutil
5
  import re
6
+ import logging
7
  from flask import Flask, request, send_file, jsonify
8
  from werkzeug.utils import secure_filename
9
 
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
  app = Flask(__name__)
15
 
16
  # Configuration
17
+ app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 # 1MB max
18
+ app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg'}
19
+ app.config['TEMP_DIR'] = '/tmp/pwa2apk' # Using system temp directory
20
+ app.config['BUBBLEWRAP_MIN_VERSION'] = '1.0.0'
21
+
22
+ # Ensure temp directory exists
23
+ os.makedirs(app.config['TEMP_DIR'], exist_ok=True)
24
 
 
25
  def is_valid_url(url):
26
+ """Validate URL format using regex"""
27
  regex = re.compile(
28
  r'^(https?://)?' # http:// or https://
29
  r'([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}' # domain
 
32
  )
33
  return re.match(regex, url) is not None
34
 
 
35
  def check_bubblewrap():
36
+ """Verify bubblewrap is installed and meets version requirements"""
37
  try:
38
+ result = subprocess.run(
39
+ ['bubblewrap', '--version'],
40
+ capture_output=True,
41
+ text=True,
42
+ check=True
43
+ )
44
  version = result.stdout.strip()
45
+ logger.info(f"Bubblewrap version detected: {version}")
 
46
  return True
47
  except Exception as e:
48
+ logger.error(f"Bubblewrap check failed: {str(e)}")
49
  return False
50
 
 
51
  def cleanup_directory(dir_path):
52
+ """Safely remove temporary directory"""
53
  try:
54
  if os.path.exists(dir_path):
55
  shutil.rmtree(dir_path)
56
+ logger.info(f"Cleaned up directory: {dir_path}")
57
  except Exception as e:
58
+ logger.error(f"Cleanup failed for {dir_path}: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ def generate_apk_internal(pwa_url, output_dir):
61
+ """Core APK generation logic"""
 
 
62
  try:
63
  # Initialize project
64
  init_cmd = [
65
  'bubblewrap', 'init',
66
  '--manifest', pwa_url,
67
+ '--directory', output_dir,
68
  '--non-interactive'
69
  ]
70
+ subprocess.run(init_cmd, check=True, timeout=300)
71
+
 
 
72
  # Build APK
73
  build_cmd = ['bubblewrap', 'build']
74
+ subprocess.run(build_cmd, cwd=output_dir, check=True, timeout=600)
75
+
 
76
  # Verify APK was created
77
+ apk_path = os.path.join(output_dir, 'app-release.apk')
78
  if not os.path.exists(apk_path):
79
+ raise FileNotFoundError("APK file not found after build")
80
+
81
+ return apk_path
82
+
83
+ except subprocess.TimeoutExpired as e:
84
+ logger.error(f"Build timed out: {str(e)}")
85
+ raise
86
+ except subprocess.CalledProcessError as e:
87
+ logger.error(f"Build failed with code {e.returncode}: {e.stderr}")
88
+ raise
89
+ except Exception as e:
90
+ logger.error(f"Unexpected error: {str(e)}")
91
+ raise
92
+
93
+ @app.route('/health', methods=['GET'])
94
+ def health_check():
95
+ """Health check endpoint"""
96
+ return jsonify({'status': 'healthy', 'bubblewrap': check_bubblewrap()})
97
+
98
+ @app.route("/generate-apk", methods=["POST"])
99
+ def generate_apk():
100
+ """Main APK generation endpoint"""
101
+ if not check_bubblewrap():
102
+ return jsonify({"error": "Bubblewrap not available"}), 503
103
+
104
+ if 'url' not in request.form:
105
+ return jsonify({"error": "URL parameter is required"}), 400
106
+
107
+ pwa_url = request.form['url'].strip()
108
+ if not is_valid_url(pwa_url):
109
+ return jsonify({"error": "Invalid URL format"}), 400
110
+
111
+ # Create unique temp directory
112
+ temp_dir = tempfile.mkdtemp(dir=app.config['TEMP_DIR'])
113
+ logger.info(f"Created temp directory: {temp_dir}")
114
+
115
+ try:
116
+ apk_path = generate_apk_internal(pwa_url, temp_dir)
117
 
118
+ # Generate safe filename
119
  domain = pwa_url.split('//')[-1].split('/')[0].replace('.', '_')
120
  apk_name = secure_filename(f"{domain}_app.apk")
121
+
122
+ logger.info(f"Successfully generated APK: {apk_path}")
123
  return send_file(
124
  apk_path,
125
  as_attachment=True,
126
  download_name=apk_name,
127
  mimetype='application/vnd.android.package-archive'
128
  )
129
+
 
 
 
 
 
130
  except Exception as e:
131
+ logger.error(f"APK generation failed: {str(e)}")
132
+ return jsonify({
133
+ "error": "APK generation failed",
134
+ "details": str(e)
135
+ }), 500
136
  finally:
 
 
137
  cleanup_directory(temp_dir)
138
 
 
 
 
 
 
 
 
 
 
 
139
  if __name__ == "__main__":
140
  # Verify dependencies at startup
141
  if not check_bubblewrap():
142
+ logger.error("Bubblewrap not available - service cannot start")
143
  exit(1)
144
 
145
+ app.run(host="0.0.0.0", port=7860)