修复偏移问题,修复代理问题
This commit is contained in:
@@ -1,53 +1,178 @@
|
||||
import re
|
||||
|
||||
with open('app/routes.py', 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
with open('app/routes/printer_routes.py', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Add serve_proxy_file after serve_file
|
||||
serve_file_code = """@main_bp.route('/file/<int:file_id>')
|
||||
@login_required
|
||||
def serve_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
abort(403)
|
||||
path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename)
|
||||
return send_file(path)"""
|
||||
# Find everything between @sock.route('/proxy', bp=printer_bp) and def octo_proxy(path):
|
||||
start_str = "@sock.route('/proxy', bp=printer_bp)"
|
||||
end_str = "@printer_bp.route('/proxy', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])"
|
||||
|
||||
proxy_code = """@main_bp.route('/file/<int:file_id>')
|
||||
@login_required
|
||||
def serve_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
abort(403)
|
||||
path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename)
|
||||
return send_file(path)
|
||||
pre_content = content[:content.find(start_str)]
|
||||
post_content = content[content.find(end_str):]
|
||||
|
||||
@main_bp.route('/proxy/<int:file_id>')
|
||||
new_proxy = """@printer_bp.route('/proxy', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
|
||||
@printer_bp.route('/proxy/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
|
||||
@login_required
|
||||
def serve_proxy_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
abort(403)
|
||||
path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename)
|
||||
proxy_path = path + '.proxy.stl'
|
||||
if not os.path.exists(proxy_path):
|
||||
from stl_simplifier import simplify_stl
|
||||
def octo_proxy(path):
|
||||
if not current_user.is_admin:
|
||||
return "Unauthorized", 403
|
||||
|
||||
url_config = SystemConfig.query.filter_by(key='octoprint_url').first()
|
||||
if not url_config or not url_config.value:
|
||||
return "OctoPrint URL not configured", 404
|
||||
|
||||
base_url = url_config.value.rstrip('/')
|
||||
|
||||
# --- WebSocket Proxy Logic ---
|
||||
if request.headers.get('Upgrade', '').lower() == 'websocket':
|
||||
from flask_sock import Server, ConnectionClosed
|
||||
|
||||
# Check if environment supports WebSockets
|
||||
try:
|
||||
simplify_stl(path, proxy_path, keep_ratio=0.1) # compress to 10%
|
||||
except:
|
||||
return send_file(path) # fallback to original if error
|
||||
if os.path.exists(proxy_path):
|
||||
return send_file(proxy_path)
|
||||
return send_file(path)"""
|
||||
ws = Server(request.environ)
|
||||
except Exception as e:
|
||||
return "WebSocket Upgrade Failed", 400
|
||||
|
||||
def handle_ws():
|
||||
if base_url.startswith('https://'):
|
||||
ws_base = base_url.replace('https://', 'wss://', 1)
|
||||
else:
|
||||
ws_base = base_url.replace('http://', 'ws://', 1)
|
||||
|
||||
target_url = f"{ws_base}/{path}"
|
||||
if request.query_string:
|
||||
target_url = f"{target_url}?{request.query_string.decode('utf-8')}"
|
||||
|
||||
text = text.replace(serve_file_code, proxy_code)
|
||||
# Forward essential headers like NGINX Proxy
|
||||
headers = {
|
||||
'Host': request.host,
|
||||
'X-Real-IP': request.remote_addr,
|
||||
}
|
||||
forwarded_for = request.headers.get('X-Forwarded-For', '')
|
||||
headers['X-Forwarded-For'] = f"{forwarded_for}, {request.remote_addr}" if forwarded_for else request.remote_addr
|
||||
headers['X-Forwarded-Proto'] = request.scheme
|
||||
headers['X-Forwarded-Host'] = request.host
|
||||
headers['X-Forwarded-Port'] = str(request.environ.get('SERVER_PORT', '80'))
|
||||
|
||||
if request.headers.get('Cookie'):
|
||||
headers['Cookie'] = request.headers.get('Cookie')
|
||||
|
||||
# Change plater models to use serve_proxy_file
|
||||
old_str = "url_for('main.serve_file', file_id=f.id)"
|
||||
new_str = "url_for('main.serve_proxy_file', file_id=f.id)"
|
||||
try:
|
||||
remote_ws = ws_connect(target_url, additional_headers=headers)
|
||||
except Exception as e:
|
||||
ws.close(1011, str(e))
|
||||
return
|
||||
|
||||
text = text.replace(old_str, new_str)
|
||||
def recv_loop():
|
||||
try:
|
||||
for message in remote_ws:
|
||||
ws.send(message)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
try: remote_ws.close()
|
||||
except: pass
|
||||
try: ws.close()
|
||||
except: pass
|
||||
|
||||
with open('app/routes.py', 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
print("Patched successfully")
|
||||
t = threading.Thread(target=recv_loop)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
try:
|
||||
while True:
|
||||
data = ws.receive()
|
||||
if data is None:
|
||||
break
|
||||
remote_ws.send(data)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
try: remote_ws.close()
|
||||
except: pass
|
||||
try: ws.close()
|
||||
except: pass
|
||||
|
||||
try:
|
||||
handle_ws()
|
||||
except ConnectionClosed:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
try: ws.close()
|
||||
except: pass
|
||||
|
||||
class WebSocketResponse(Response):
|
||||
def __call__(self, *args, **kwargs):
|
||||
if getattr(ws, 'mode', 'werkzeug') == 'werkzeug':
|
||||
return super().__call__(*args, **kwargs)
|
||||
return []
|
||||
|
||||
return WebSocketResponse()
|
||||
|
||||
# --- Standard HTTP Proxy Logic ---
|
||||
from urllib.parse import urlparse
|
||||
target_url = f"{base_url}/{path}"
|
||||
|
||||
if request.query_string:
|
||||
target_url = f"{target_url}?{request.query_string.decode('utf-8')}"
|
||||
|
||||
# Build headers for reverse proxy based on nginx config reference
|
||||
parsed_base = urlparse(base_url)
|
||||
headers = {k: v for k, v in request.headers if k.lower() not in ['host', 'content-length']}
|
||||
|
||||
# NGINX equivalent proxy headers
|
||||
headers['Host'] = request.host
|
||||
headers['X-Real-IP'] = request.remote_addr
|
||||
headers['X-Real-Port'] = str(request.environ.get('REMOTE_PORT', ''))
|
||||
|
||||
forwarded_for = request.headers.get('X-Forwarded-For', '')
|
||||
headers['X-Forwarded-For'] = f"{forwarded_for}, {request.remote_addr}" if forwarded_for else request.remote_addr
|
||||
|
||||
headers['X-Forwarded-Protocol'] = request.scheme
|
||||
headers['X-Script-Name'] = "/printer/proxy"
|
||||
headers['X-Forwarded-Host'] = request.host
|
||||
headers['X-Forwarded-Port'] = str(request.environ.get('SERVER_PORT', '80'))
|
||||
headers['REMOTE-HOST'] = request.remote_addr
|
||||
|
||||
if request.headers.get('Upgrade'):
|
||||
headers['Upgrade'] = request.headers.get('Upgrade')
|
||||
if request.headers.get('Connection'):
|
||||
headers['Connection'] = request.headers.get('Connection')
|
||||
|
||||
try:
|
||||
# proxy_connect_timeout 60s, proxy_read_timeout 600s
|
||||
resp = requests.request(
|
||||
method=request.method,
|
||||
url=target_url,
|
||||
headers=headers,
|
||||
data=request.get_data(),
|
||||
cookies=request.cookies,
|
||||
allow_redirects=False,
|
||||
stream=True,
|
||||
timeout=(60, 600)
|
||||
)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return f"Proxy connection error: {str(e)}", 502
|
||||
|
||||
# Strip headers that might break the iframe or framing
|
||||
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection', 'x-frame-options', 'content-security-policy']
|
||||
response_headers = [(name, value) for (name, value) in resp.headers.items() if name.lower() not in excluded_headers]
|
||||
|
||||
def generate():
|
||||
for chunk in resp.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
yield chunk
|
||||
|
||||
return Response(generate(), resp.status_code, response_headers)
|
||||
"""
|
||||
|
||||
post_end_str = " return Response(generate(), resp.status_code, response_headers)"
|
||||
post_end_idx = post_content.find(post_end_str) + len(post_end_str)
|
||||
final_content = pre_content + new_proxy + post_content[post_end_idx:]
|
||||
|
||||
with open('app/routes/printer_routes.py', 'w', encoding='utf-8') as f:
|
||||
f.write(final_content)
|
||||
|
||||
print("Patched!")
|
||||
|
||||
Reference in New Issue
Block a user