Restore Video workspace and route native video API through shared endpoint
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
|
||||
WORKDIR /app
|
||||
COPY deploy/image-requirements.lock /app/deploy/image-requirements.lock
|
||||
COPY deploy/tts-requirements.lock /app/deploy/tts-requirements.lock
|
||||
COPY api_compat.py stt.py execution_setup.py tts_runtime.py tts_test.py tts_worker.py auto_test.py chat_test.py endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py profiles.py capacity.py server.py runtime.py video.py catalog.py hub_auth.py auth.py collect_hardware.py /app/
|
||||
COPY api_compat.py stt.py execution_setup.py tts_runtime.py tts_test.py tts_worker.py auto_test.py chat_test.py endpoint.py inference.py docker_support.py image_encoder_node.py image_runtime.py image_test.py profiles.py capacity.py server.py runtime.py video.py video_proxy.py catalog.py hub_auth.py auth.py collect_hardware.py /app/
|
||||
COPY video-ui.js stt-ui.js tts-ui.js auto-test-ui.js chat-test-ui.js endpoint-ui.js docker-ui.js image-test-ui.js profiles-ui.js index.html app.js studio.js runtime-ui.js catalog-ui.js style.css login.html login.js access-ui.js network-ui.js /app/
|
||||
COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \
|
||||
|
||||
+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')
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import urllib.request
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
LABEL = 'de.casaderoll.athena-deck.standalone'
|
||||
FILES = ['deploy/tts-requirements.lock','stt.py','stt-ui.js','api_compat.py','execution_setup.py','tts_runtime.py','tts_test.py','tts_worker.py','tts-ui.js','auto_test.py','auto-test-ui.js','chat_test.py','chat-test-ui.js','endpoint.py','inference.py','endpoint-ui.js','docker_support.py','docker-ui.js','deploy/docker_helper.py','deploy/setup_docker_helper.py','image_encoder_node.py','image_runtime.py','image_test.py','image-test-ui.js','profiles.py','profiles-ui.js','capacity.py','runtime.py','runtime-ui.js','video.py','video-ui.js','catalog.py','hub_auth.py','catalog-ui.js','server.py','auth.py','collect_hardware.py','index.html','app.js','studio.js','style.css','login.html','login.js','access-ui.js','network-ui.js','network/__init__.py','network/client.py','network/config.py','network/rpc.py','deploy/Dockerfile','deploy/image-requirements.lock']
|
||||
FILES = ['deploy/tts-requirements.lock','stt.py','stt-ui.js','api_compat.py','execution_setup.py','tts_runtime.py','tts_test.py','tts_worker.py','tts-ui.js','auto_test.py','auto-test-ui.js','chat_test.py','chat-test-ui.js','endpoint.py','inference.py','endpoint-ui.js','docker_support.py','docker-ui.js','deploy/docker_helper.py','deploy/setup_docker_helper.py','image_encoder_node.py','image_runtime.py','image_test.py','image-test-ui.js','profiles.py','profiles-ui.js','capacity.py','runtime.py','runtime-ui.js','video.py','video_proxy.py','video-ui.js','catalog.py','hub_auth.py','catalog-ui.js','server.py','auth.py','collect_hardware.py','index.html','app.js','studio.js','style.css','login.html','login.js','access-ui.js','network-ui.js','network/__init__.py','network/client.py','network/config.py','network/rpc.py','deploy/Dockerfile','deploy/image-requirements.lock']
|
||||
|
||||
|
||||
def run(*args, check=True, interactive=False):
|
||||
|
||||
Reference in New Issue
Block a user