Restore Video workspace and route native video API through shared endpoint
This commit is contained in:
1 parent
badc7701fa
commit
67cf57a5b7
15 files changed
+166
-20
No files matched your search
+46
-1
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-owned local helper. Only inventory and fresh Docker install; no generic RPC."""
|
||||
import argparse
|
||||
import http.client
|
||||
from urllib.parse import urlsplit
|
||||
import re
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
@@ -149,6 +151,46 @@ class Manager:
|
||||
if data['action']=='install':return self.install()
|
||||
raise ValueError('Aktion nicht erlaubt.')
|
||||
|
||||
def video_http(manager,data,source,target):
|
||||
"""Fixed registered upstream only; no model translation or secret returned to Deck."""
|
||||
sent=False;connection=None
|
||||
try:
|
||||
if set(data)!={'action','service','method','path','length','headers'}:raise ValueError('Ungültige Proxy-Anfrage.')
|
||||
method=data['method'];path=data['path'];length=data['length'];headers=data['headers']
|
||||
if method not in ('GET','POST','PUT','PATCH','DELETE','HEAD','OPTIONS') or not isinstance(path,str) or not path.startswith('/') or path.startswith('//') or any(ord(c)<32 for c in path):raise ValueError('Ungültige HTTP-Anfrage.')
|
||||
if type(length) is not int or not 0<=length<=256*1024*1024 or not isinstance(headers,dict):raise ValueError('Ungültige Anfragegröße.')
|
||||
allowed={'content-type','accept','range','if-range','if-none-match','if-modified-since'}
|
||||
if any(not isinstance(k,str) or k.lower() not in allowed or not isinstance(v,str) or '\r' in v or '\n' in v for k,v in headers.items()):raise ValueError('Ungültige Header.')
|
||||
config=next((c for c in manager.video_configs() if c['id']==data['service']),None)
|
||||
if config is None:raise ValueError('Videodienst nicht freigegeben.')
|
||||
if manager.video_status(config)['running'] is not True:raise ValueError('Videodienst nicht gestartet.')
|
||||
url=urlsplit(config['api_url'])
|
||||
if url.scheme not in ('http','https') or not url.hostname or url.username or url.password:raise ValueError('Ungültiges Backend-Ziel.')
|
||||
connection=(http.client.HTTPSConnection if url.scheme=='https' else http.client.HTTPConnection)(url.hostname,url.port,timeout=3600)
|
||||
connection.putrequest(method,path,skip_accept_encoding=True)
|
||||
for key,value in headers.items():connection.putheader(key,value)
|
||||
if config.get('token_file'):connection.putheader('Authorization','Bearer '+Path(config['token_file']).read_text().strip())
|
||||
connection.putheader('Content-Length',str(length));connection.putheader('Connection','close');connection.endheaders()
|
||||
remaining=length
|
||||
while remaining:
|
||||
chunk=source.read(min(1024*1024,remaining))
|
||||
if not chunk:raise ValueError('Unvollständige Anfrage.')
|
||||
connection.send(chunk);remaining-=len(chunk)
|
||||
response=connection.getresponse();sent=True
|
||||
target.write(f'HTTP/1.1 {response.status} {response.reason}\r\n'.encode())
|
||||
hop={'connection','keep-alive','proxy-authenticate','proxy-authorization','te','trailer','transfer-encoding','upgrade'}
|
||||
for key,value in response.getheaders():
|
||||
if key.lower() not in hop:target.write(f'{key}: {value}\r\n'.encode('latin-1'))
|
||||
target.write(b'Connection: close\r\n\r\n');target.flush()
|
||||
if method!='HEAD':
|
||||
while chunk:=response.read(1024*1024):target.write(chunk);target.flush()
|
||||
except Exception:
|
||||
if not sent:
|
||||
body=b'{"error":"Video-API nicht erreichbar oder Anfrage nicht erlaubt."}'
|
||||
target.write(b'HTTP/1.1 502 Bad Gateway\r\nContent-Type: application/json\r\nContent-Length: '+str(len(body)).encode()+b'\r\nConnection: close\r\n\r\n'+body)
|
||||
finally:
|
||||
if connection:connection.close()
|
||||
|
||||
class Handler(socketserver.StreamRequestHandler):
|
||||
def handle(self):
|
||||
self.connection.settimeout(20)
|
||||
@@ -157,7 +199,10 @@ class Handler(socketserver.StreamRequestHandler):
|
||||
if uid not in (0,self.server.client_uid):raise ValueError('Client nicht erlaubt.')
|
||||
raw=self.rfile.readline(4097)
|
||||
if len(raw)>4096:raise ValueError('Anfrage zu groß.')
|
||||
result=self.server.manager.dispatch(json.loads(raw))
|
||||
data=json.loads(raw)
|
||||
if isinstance(data,dict) and data.get('action')=='video-http':
|
||||
self.connection.settimeout(3600);video_http(self.server.manager,data,self.rfile,self.wfile);return
|
||||
result=self.server.manager.dispatch(data)
|
||||
except Exception as exc:result={'error':str(exc) if isinstance(exc,ValueError) else 'Docker-Systemhelfer nicht verfügbar.'}
|
||||
self.wfile.write(json.dumps(result).encode()+b'\n')
|
||||
|
||||
|
||||
Reference in new issue
Block a user