o
    4jy                     @   s   d Z ddlZddlZddlZddlZddlZddlmZm	Z	 ddl
mZmZ dZdZdZdZe Zdd	d
ddg dZG dd deZdd ZedkrPe  dS dS )ud  
Transloc Sync-Server
=====================
Sehr einfacher, lokaler HTTP-Server, der den aktuellen Gesprächszustand
zwischen Smartphone 1 (transloc.html - Mikrofon/Steuerung) und
Smartphone 2 (transloc-partner.html - reine Anzeige fuer den
Gespraechspartner) synchronisiert.

Zusaetzlich fungiert er als schmaler Proxy zu Ollama, damit Ollama selbst
NIE direkt oeffentlich erreichbar sein muss (nur /api/chat wird
weitergereicht, keine Verwaltungsfunktionen wie delete/pull/create).

Start:
    python3 sync_server.py [PORT]

Standard-Port: 5115

Endpunkte:
    GET  /state          -> aktueller Gesamtzustand als JSON
    POST /update         -> Teilzustand mergen
    POST /reset          -> Gespraechsverlauf (log) leeren
    POST /translate       -> leitet an Ollama /api/chat weiter (Proxy)
    GET  /ollama-status   -> leitet an Ollama /api/tags weiter (Proxy)
    N)urlsplitparse_qs)BaseHTTPRequestHandlerThreadingHTTPServeri     zhttp://127.0.0.1:11434(5473a890ac45f2a774b765b5148733de4ab2cf69SpanischTF)versiontargetLanguagerecognizingGerman	listeningspeakinglogc                   @   sR   e Zd ZdZdddZdd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd ZdS )HandlerzTranslocSync/1.1r   c                 C   sv   t |d}| | | dd | dtt| | dd | dd | d	d |   | j	| d S )
Nutf-8Content-Typezapplication/json; charset=utf-8Content-LengthzAccess-Control-Allow-Origin*zAccess-Control-Allow-MethodszGET, POST, OPTIONSzAccess-Control-Allow-Headers)
jsondumpsencodesend_responsesend_headerstrlenend_headerswfilewrite)selfpayloadstatusbody r"   $/home/solarski/webapp/sync_server.py
_send_json6   s   
zHandler._send_jsonc                 C   s   |  i  d S N)r$   r   r"   r"   r#   
do_OPTIONSA   s   zHandler.do_OPTIONSc                 C   s*   t | jj}t|dd gd }|tkS )Nkeyr   )r   pathqueryr   getAPP_KEY)r   r*   suppliedr"   r"   r#   _has_valid_keyD   s   zHandler._has_valid_keyc                 C   s   |   s| jddidd d S | jdr/t | t W d    d S 1 s(w   Y  d S | jdr<| d d S | jddid	d d S )
Nerrorunauthorized  r    z/statez/ollama-statusz	/api/tags	not found  )r.   r$   r)   
startswith
state_lockstate_proxy_ollama_getr&   r"   r"   r#   do_GETI   s   "zHandler.do_GETc                 C   s  |   s| jddidd d S t| jddpd}|r"| j|nd}z| r1t	|
dni }W n tjyH   | jdd	id
d Y d S w | jdrt[ dD ]}||v r`|| t|< qT|d}t|tr|ddv rd|v rtd |d |d d ttd tkrtd t d  td< td  d7  < | t W d    d S 1 sw   Y  d S | jdrt g td< td  d7  < | t W d    d S 1 sw   Y  d S | jdr| d| d S | jddidd d S )Nr/   r0   r1   r2   r   r   s   {}r   zinvalid jsoni  z/update)r
   r   r   r   logEntryrole)partneryoutextr   )r;   r>   r	      z/resetz
/translatez	/api/chatr3   r4   )r.   r$   intheadersr+   rfilereadstripr   loadsdecodeJSONDecodeErrorr)   r5   r6   r7   
isinstancedictappendr   MAX_LOG_ENTRIES_proxy_ollama_post)r   lengthrawdatar(   entryr"   r"   r#   do_POSTU   sD    
 ""zHandler.do_POSTc           	   
   C   s   | d| dg dt| ddd}t|d}tjjt| |ddid	d
}z*tjj	|dd}t
| d}W d    n1 sHw   Y  | | W d S  tjjyu } z| jdd| idd W Y d }~d S d }~ww )NmodelmessagesFthink)rR   rS   streamrT   r   r   zapplication/jsonPOST)rO   rA   methodx   timeoutr/   Ollama nicht erreichbar:   r2   )r+   boolr   r   r   urllibrequestRequestOLLAMA_BASEurlopenrE   rC   rF   r$   r/   URLError)	r   ollama_pathrO   r   r!   reqrespresulter"   r"   r#   rL   w   s*   
&zHandler._proxy_ollama_postc              
   C   s   t jjt| dd}z*t jj|dd}t| d}W d    n1 s(w   Y  | 	| W d S  t j
jyU } z| j	dd| idd	 W Y d }~d S d }~ww )
NGET)rW   
   rY   r   r/   r[   r\   r2   )r^   r_   r`   ra   rb   r   rE   rC   rF   r$   r/   rc   )r   rd   re   rf   rg   rh   r"   r"   r#   r8      s   &zHandler._proxy_ollama_getc                 G   s   d S r%   r"   )r   fmtargsr"   r"   r#   log_message   s   zHandler.log_messageN)r   )__name__
__module____qualname__server_versionr$   r'   r.   r9   rQ   rL   r8   rm   r"   r"   r"   r#   r   3   s    
"	r   c                  C   s   t } ttjdkr)z	ttjd } W n ty(   tdtjd  dt   Y nw td| ft}td|  d z|	  W d S  t
yI   Y d S w )Nr?   zUngueltiger Port 'z', verwende Standard z0.0.0.0z%Transloc Sync-Server laeuft auf Port z (Strg+C zum Beenden))DEFAULT_PORTr   sysargvr@   
ValueErrorprintr   r   serve_foreverKeyboardInterrupt)portserverr"   r"   r#   main   s   r{   __main__)__doc__r   rs   	threadingurllib.requestr^   urllib.errorurllib.parser   r   http.serverr   r   rr   rK   ra   r,   Lockr6   r7   r   r{   rn   r"   r"   r"   r#   <module>   s2   
h
