
    n"lj                    ~   U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	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 ddlmZmZmZ ddlmZ ddlmZmZ ddlmZ dd	lmZmZmZm Z m!Z!m"Z"  ee#      jI                         jJ                  Z&e&d
z  Z'e&dz  dz  Z(dZ)dZ*dZ+dZ,dZ-dZ.dZ/dZ0dZ1e-hZ2 G d de3      Z4d1dZ5d1dZ6d2dZ7d3d4dZ8d5dZ9d6dZ:d7dZ;d8dZ<d9dZ= G d  d!      Z> G d" d#      Z? G d$ d%      Z@ G d& d'      ZAdaBd(eCd)<    G d* d+e      ZDd:d,ZEd;d-ZFd<d=d.ZGd<d>d/ZHeId0k(  r eJ eH             y)?zMistral API key management service for Yunwo parsing workers.

The service intentionally uses only Python's standard library so it can be
deployed next to the production parser without dependency churn.
    )annotationsN)ThreadPoolExecutor)datetime	timedeltatimezone)
HTTPStatus)BaseHTTPRequestHandlerThreadingHTTPServer)Path)AnyDictIterableListOptionalTuplestaticdatazkeys.sqlitezhttps://mail-api.sdramago.topzhttps://api.mistral.ai/v1 activeunknownquota_exhausteddisablederrorc                      e Zd Zy)MailboxUnavailableN)__name__
__module____qualname__     (/tmp/yunwo-mistral-key-manager-server.pyr   r   2   s    r    r   c                 h    t        j                  t        j                        j	                  d      S )Nsecondstimespec)r   nowr   utc	isoformatr   r    r!   utc_nowr)   6   s#    <<%///CCr    c                 *   t        j                  t        j                        j	                         } | j
                  dk(  r"| j                  | j                  dz   dd      }n | j                  | j
                  dz   d      }|t        d      z
  }|| kD  r|j                         S |j
                  dk(  r"|j                  |j                  dz   dd      }n |j                  |j
                  dz   d      }|t        d      z
  j                         S )N      )yearmonthday)r.   r/   )days)
r   r&   r   r'   dater.   replacer-   r   r(   )today
next_month	candidatemonth_afters       r!   current_month_last_dayr7   :   s    LL&++-E{{b]]

QaQ]G
]]qa]@
YA..I5""$$2 ((joo.APQ(R ((z/?/?!/C(K)++6688r    c                P    t        j                  | dd      j                  d      S )NF),:)ensure_ascii
separatorsutf-8)jsondumpsencode)r   s    r!   
json_dumpsrA   J   s     ::d:FMMgVVr    c                t    t        j                  |       }||S |j                         j                         dv S )N>   1yonyestrue)osgetenvstriplowernamedefaultvalues      r!   read_env_boolrP   N   s4    IIdOE};;= $CCCr    c                    t        j                  |       }||j                         dk(  r|S 	 t        |      S # t        $ r |cY S w xY w)Nr   )rH   rI   rJ   float
ValueErrorrL   s      r!   read_env_floatrT   U   sI    IIdOE}+U| s   
9 AAc                    | j                  dd      j                  dd      j                  dd      j                  d      D cg c]  }|j                          }}|D cg c]  }|s|	 c}S c c}w c c}w )N
r9   ; )r2   splitrJ   )rawitem
candidatess      r!   split_api_keysr]   _   sw     KKc*223<DDS#NTTUXY 	

J  (0T4D00	 1s   A0!A5)A5c                |    | xs dj                         }t        |      dk  rdt        |      z  S |d d  d|dd   S )Nr   
   *   z...)rJ   len)api_keycleans     r!   mask_keyrf   g   sL    ]!!#E
5zRSZBQi[E"#J<((r    c                ,   t        j                  d| xs dt         j                        }|sy |j                         \  }}}dD ]@  }	 t	        j
                  | d| d| |      }|j                         j                         c S  y # t        $ r Y Ow xY w)Nz/reset\s+on\s+([A-Za-z]+)\s+(\d{1,2}),\s+(\d{4})r   )z%B %d %Yz%b %d %YrX   )	researchIgroupsr   strptimer1   r(   rS   )textmatchr.   r/   r-   fmtparseds          r!   parse_reset_daterq   n   s    IIH$*RTVXVZVZ[E||~E3' 	&&%#av'>DF;;=**,,   		s   ;B	BBc           	        g }| xs dj                         D ]3  }|j                         }|r|j                  d      r(d|v r1d|vr-|j                  d      D cg c]  }|j                          }}nQd|v r=t	        t        j                  |g            }|D cg c]  }|j                          }}n|j                         }|s|d   j                         }d}g }|dd  D ]8  }d|v r|s|j                         }|j                  |j                                : |j                  ||dj                  d	 |D              d
d       6 |S c c}w c c}w )Nr   #|r9   r   r,   @rX   c              3  &   K   | ]	  }|s|  y wNr   ).0parts     r!   	<genexpr>z#parse_bulk_lines.<locals>.<genexpr>   s     !G4$$!Gs   Trd   emaillabelenabled)	
splitlinesrJ   
startswithrY   nextcsvreaderappendjoin)	rm   rowsraw_linelinery   partsrd   r|   label_partss	            r!   parse_bulk_linesr   |   sJ   !#DZR++- 
~~ts+$;3d?.2jjo>dTZZ\>E>D[TF+,E.34dTZZ\4E4JJLE(.."!#!"I 	1Dd{5

""4::<0		1
 	"!G;!GG		
-
< K3 ? 5s   EEc                      e Zd ZddZddZddZddddZd dZd!dZd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(dZ	 	 	 	 	 	 	 	 	 	 	 	 d)dZd*d+dZd,dZd-dZd.dZdd	 	 	 	 	 	 	 	 	 d/dZd0dZy)1KeyStorec                    || _         | j                   j                  j                  dd       | j                          y )NT)parentsexist_ok)db_pathparentmkdirinit_db)selfr   s     r!   __init__zKeyStore.__init__   s/    !!$!>r    c                    t        j                  t        | j                        d      }t         j                  |_        |j                  d       |j                  d       |S )N   timeoutzPRAGMA journal_mode=WALzPRAGMA foreign_keys=ON)sqlite3connectstrr   Rowrow_factoryexecuter   conns     r!   r   zKeyStore.connect   sF    s4<<0"=";;./-.r    c                    | j                         5 }|j                  d       |j                  d       d d d        y # 1 sw Y   y xY w)Na  
                CREATE TABLE IF NOT EXISTS api_keys (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    api_key TEXT NOT NULL UNIQUE,
                    email TEXT NOT NULL DEFAULT '',
                    label TEXT NOT NULL DEFAULT '',
                    enabled INTEGER NOT NULL DEFAULT 1,
                    status TEXT NOT NULL DEFAULT 'active',
                    last_checked_at TEXT,
                    last_success_at TEXT,
                    last_failure_at TEXT,
                    last_status_code INTEGER,
                    last_error TEXT,
                    quota_reset_at TEXT,
                    quota_email_id TEXT,
                    quota_email_subject TEXT,
                    quota_email_seen_at TEXT,
                    notes TEXT NOT NULL DEFAULT '',
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL
                )
                a  
                CREATE TABLE IF NOT EXISTS events (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    key_id INTEGER NOT NULL,
                    event_type TEXT NOT NULL,
                    message TEXT NOT NULL DEFAULT '',
                    metadata_json TEXT NOT NULL DEFAULT '{}',
                    created_at TEXT NOT NULL
                )
                )r   r   r   s     r!   r   zKeyStore.init_db   sF    \\^ $	tLL0 LL	3$	 $	 $	s	   #=AFinclude_secretc                   t        |      }|j                  d      }t        |      |d<   |d d |d<   |dd  |d<   t        |j	                  d            |d<   |r||d<   |S )Nrd   
masked_keyra   
key_prefixrb   
key_suffixr~   )dictpoprf   boolget)r   rowr   r[   rd   s        r!   row_to_publiczKeyStore.row_to_public   sp    Cy((9%%g.\$Ra[\$RS\\txx	23Y%DOr    c                   t        |j                  d      xs d      j                         }|st        d      t	               }t        |j                  d      xs d      j                         }t        |j                  d      xs d      j                         }t        |j                  d      xs d      j                         }|j                  dd      rd	nd
}|rt
        nt        }| j                         5 }	|	j                  d||||||||f       |	j                  d|f      j                         }
| j                  |	t        |
d         ddi        | j                  |
      cd d d        S # 1 sw Y   y xY w)Nrd   r   api_key is requiredr|   r}   notesr~   Tr,   r   aU  
                INSERT INTO api_keys
                    (api_key, email, label, enabled, status, notes, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(api_key) DO UPDATE SET
                    email=excluded.email,
                    label=CASE WHEN excluded.label != '' THEN excluded.label ELSE api_keys.label END,
                    enabled=excluded.enabled,
                    status=CASE
                        WHEN excluded.enabled = 0 THEN 'disabled'
                        WHEN api_keys.status = 'disabled' AND excluded.enabled = 1 THEN 'active'
                        ELSE api_keys.status
                    END,
                    notes=CASE WHEN excluded.notes != '' THEN excluded.notes ELSE api_keys.notes END,
                    updated_at=excluded.updated_at
                &SELECT * FROM api_keys WHERE api_key=?idupsertz	key saved)r   r   rJ   rS   r)   STATUS_ACTIVESTATUS_DISABLEDr   r   fetchone	add_eventintr   )r   payloadrd   r&   r|   r}   r   r~   statusr   r   s              r!   add_or_update_keyzKeyStore.add_or_update_key   sH   gkk),2399;233iGKK(.B/557GKK(.B/557GKK(.B/557{{9d3!")\\^ 	+tLL  %SI#& ,,G'T]]_CNN4SY;K%%c*-	+ 	+ 	+s   <A-E33E<c                   g }g }|j                  d      xs dgd   j                         }|j                  d      xs dgd   j                         j                         }|j                  d      xs dgd   j                         }|r"|j                  d       |j                  |       |dv r(|j                  d       |j                  |d	v rd
nd       |r,|j                  d       d| d}|j	                  ||||g       d}|r|ddj                  |      z   z  }|dz  }| j                         5 }	|	j                  ||      j                         D 
cg c]  }
| j                  |
       c}
cd d d        S c c}
w # 1 sw Y   y xY w)Nr   r   r   r~   qz
status = ?>   0rC   rG   falsezenabled = ?>   rC   rG   r,   z@(email LIKE ? OR label LIKE ? OR notes LIKE ? OR api_key LIKE ?)%zSELECT * FROM api_keysz WHERE z AND z+ ORDER BY enabled DESC, status ASC, id DESC)
r   rJ   rK   r   extendr   r   r   fetchallr   )r   paramswherevaluesr   r~   r   likesqlr   r   s              r!   	list_keyszKeyStore.list_keys   sy   **X&.2$288:::i(0RD!4::<BBDZZ_$a(..0LL&MM&!11LL'MMw-7!Q?LL[\qc8DMM4tT23&9w||E222C<<\\^ 	]t7;||C7P7Y7Y7[\D&&s+\	] 	]\	] 	]s   6#FE=1F=FFc                   | j                         5 }|j                  d|f      j                         }|
	 d d d        y | j                  ||      cd d d        S # 1 sw Y   y xY w)N!SELECT * FROM api_keys WHERE id=?r   )r   r   r   r   )r   key_idr   r   r   s        r!   get_keyzKeyStore.get_key  sk    \\^ 	Jt,,BVINWWYC{	J 	J %%c.%I		J 	J 	Js   %AAA$c                D    |j                  d|f      j                         S )Nr   )r   r   )r   r   r   s      r!   get_key_rowzKeyStore.get_key_row  s    ||?&KTTVVr    c                P   h d}g }g }| j                  |      }|t        d      |j                  d      }d|v r|j                  d      st        }n3d|v r/|j                  d      r|j                  d      t        k(  rt        }|D ]`  }||vr|dk(  r||   }	|dk(  r|	rdnd}	nt        |	xs d      j                         }	|j                  | d       |j                  |	       b |E|j                  d	       |j                  t        |xs d      j                         xs t               |s|S |j                  d
       |j                  t                      |j                  |       | j                         5 }
|
j                  ddj                  |       d|      }|j                  dk(  rt        d      | j                  |
|      }| j                  |
|dddt!        |j#                               i       | j%                  |      cd d d        S # 1 sw Y   y xY w)N>   r|   r}   r   r   r~   key not foundr   r~   r,   r   r   z=?zstatus=?zupdated_at=?zUPDATE api_keys SET z, z WHERE id=?updatezkey updatedfields)r   KeyErrorr   r   r   r   rJ   r   STATUS_UNKNOWNr)   r   r   r   rowcountr   r   listkeysr   )r   r   r   allowedupdatesr   currentnext_statusfieldrO   r   resultr   s                r!   	patch_keyzKeyStore.patch_key!  s   B,,v&??++kk(+I(>)K'!gkk)&<XAVZiAi'K 	!EG# ENE	!"EKR(..0NNeWB<(MM% 	! "NN:&MM#k/R0668JNKN~&gi f\\^ 	+t\\$879K8LK"XZ`aF!#//""40CNN4=8TRYR^R^R`MaBbc%%c*	+ 	+ 	+s   BHH%c                D   | j                         5 }|j                  d|f      j                         }|
	 d d d        y| j                  ||dddt	        t        |d               i       |j                  d|f      }|j                  dkD  cd d d        S # 1 sw Y   y xY w)	Nz+SELECT id, api_key FROM api_keys WHERE id=?Fdeletezkey deletedr   rd   zDELETE FROM api_keys WHERE id=?r   )r   r   r   r   rf   r   r   )r   r   r   r   r   s        r!   
delete_keyzKeyStore.delete_keyI  s    \\^ 	't,,LviXaacC{	' 	' NN4=<QYZ]^abk^lZmQnBop\\"CfYOF??Q&	' 	' 	's   %BABBc                   | j                          | j                          t        t        t                    }dj                  d |D              }d| d}| j                         5 }|j                  ||      j                         }|D cg c]  }t        |d          }}|D cg c]  }| j                  |       }}||fcd d d        S c c}w c c}w # 1 sw Y   y xY w)Nr9   c              3      K   | ]  }d   yw)?Nr   )rx   _s     r!   rz   z'KeyStore.active_keys.<locals>.<genexpr>V  s     ==s   z6SELECT * FROM api_keys WHERE enabled=1 AND status IN (z[) AND COALESCE(last_status_code, 0) NOT IN (401, 403) ORDER BY last_success_at DESC, id ASCrd   )reset_expired_quota_marksquarantine_reported_failurestuplesortedACTIVE_STATUSESr   r   r   r   r   r   )	r   active_statusesplaceholdersr   r   r   r   r   itemss	            r!   active_keyszKeyStore.active_keysR  s    &&())+ 78xx=_==~ 44 	 \\^ 	t<<_5>>@D378CCI'8D88<=T'',=E=;		 	8=	 	s*   '%CC!C'C?C
CC!c                   t        j                  t        j                        j	                         j                         }t               }t               }| j                         5 }|j                  dt        t        |f      j                         }|D ]]  }t        |d   xs |      }|j                  dt        ||t        |d         f       | j!                  |t        |d         ddd|i       _ 	 d d d        y # 1 sw Y   y xY w)Na  
                SELECT id, quota_reset_at FROM api_keys
                WHERE enabled=1
                  AND status IN (?, ?)
                  AND last_status_code = 401
                  AND (quota_reset_at IS NULL OR quota_reset_at > ?)
                quota_reset_atzGUPDATE api_keys SET status=?, quota_reset_at=?, updated_at=? WHERE id=?r   auto_quarantinez+key had prior 401; removed from active pool)r   r&   r   r'   r1   r(   r7   r)   r   r   r   r   r   r   STATUS_QUOTA_EXHAUSTEDr   r   )r   r3   fallback_reset_atr&   r   r   r   reset_ats           r!   r   z%KeyStore.quarantine_reported_failuresc  s    X\\*//1;;=24i\\^ 	t<< 6	 hj   s#34I8IJ]+XsCD	NK D	N%A%x0	 	 	s   $BC<<Dc                   t        j                  t        j                        j	                         j                         }t               }| j                         5 }|j                  dt        |f      j                         }|D ]H  }|j                  dt        |t        |d         f       | j                  |t        |d         ddi        J |j                  dt        t        |f      j                         }|D ]C  }|j                  d|t        |d         f       | j                  |t        |d         ddi        E 	 d d d        y # 1 sw Y   y xY w)Nz
                SELECT id FROM api_keys
                WHERE enabled=1 AND status=? AND quota_reset_at IS NOT NULL AND quota_reset_at <= ?
                z[UPDATE api_keys SET status=?, last_status_code=NULL, last_error='', updated_at=? WHERE id=?r   	reset_duez'quota reset date reached; retry enableda  
                SELECT id FROM api_keys
                WHERE enabled=1
                  AND status IN (?, ?)
                  AND last_status_code = 401
                  AND quota_reset_at IS NOT NULL
                  AND quota_reset_at <= ?
                zQUPDATE api_keys SET last_status_code=NULL, last_error='', updated_at=? WHERE id=?)r   r&   r   r'   r1   r(   r)   r   r   r   r   r   r   r   r   )r   r3   r&   r   r   r   
reset_rowss          r!   r   z"KeyStore.reset_expired_quota_marks  sM   X\\*//1;;=i\\^ 	qt<< (/ hj   qq#S#c$i.9 tST^[Bkmopq  6
 hj  " qg#c$i.) tST^[Bkmopq5	q 	q 	qs   C'EEc                    	 |j                  |      S # t        $ r#}t               }|t        |      ddcY d }~S d }~ww xY w)Nmailbox_unavailable)r   lookup_errorfallback_reason)find_quota_emailr   r7   r   )r   mailboxaddressexcr   s        r!   find_quota_email_or_defaultz$KeyStore.find_quota_email_or_default  sG    	++G44! 	-/H$ #C#8 	s    	?:??c                6   |j                  d      r|S t        j                  t        j                        j                         j                         }t        |d   xs d      j                         }||kD  r|n	t               |d<   |j                  dd       |S )Nr   r   r   r   default_month_end)r   r   r&   r   r'   r1   r(   r   rJ   r7   
setdefault)r   r   
quota_infor3   existing_resets        r!   apply_default_quota_resetz"KeyStore.apply_default_quota_reset  s    >>*%X\\*//1;;=S!128b9??A3AE3IOeOg
:/1DEr    c           
     l    |j                  d|||t        j                  |d      t               f       y )Nz
            INSERT INTO events (key_id, event_type, message, metadata_json, created_at)
            VALUES (?, ?, ?, ?, ?)
            F)r;   )r   r>   r?   r)   )r   r   r   
event_typemessagemetadatas         r!   r   zKeyStore.add_event  s4     	 Z$**XE*RT[T]^	
r    c                    | j                         5 }|j                  d|t        dt        d|            f      j	                         }|D cg c]  }t        |       c}cd d d        S c c}w # 1 sw Y   y xY w)Nz<SELECT * FROM events WHERE key_id=? ORDER BY id DESC LIMIT ?r,      )r   r   maxminr   r   )r   r   limitr   r   r   s         r!   eventszKeyStore.events  sr    \\^ 	/t<<NQC01 hj  *..#DI.	/ 	/
 /	/ 	/s   ;A/A*A/*A//A8c                   t        |j                  d      xs d      j                         }|st        d      |j                  d      }	 |t	        |      nd }t        |j                  d      xs d      j                         }t        |j                  d      xs |j                  d      xs d      j                         }t               }| j                         5 }	|	j                  d|f      j                         }
|
d	d
dcd d d        S |xs t        }i }|dk(  s	|t        k(  rXt        }| j                  |
|      }|j                  d      dk(  r,|r| d|j                  d       nd|j                  d       }|	j                  d|||||d d |j                  d      |j                  d      |j                  d      |j                  d      |t	        |
d         f       | j                  |	t	        |
d         d|d d xs ||||d       |	j                  dt	        |
d         f      j                         }d| j                  |      dcd d d        S # t
        t        f$ r d }Y w xY w# 1 sw Y   y xY w)Nrd   r   r   status_coder   r   r  r   Fkey_not_foundsuccessr     r   r  z; default quota reset at r   zdefault quota reset at a  
                UPDATE api_keys
                SET status=?, last_checked_at=?, last_failure_at=?, last_status_code=?,
                    last_error=?, quota_reset_at=COALESCE(?, quota_reset_at),
                    quota_email_id=COALESCE(?, quota_email_id),
                    quota_email_subject=COALESCE(?, quota_email_subject),
                    quota_email_seen_at=COALESCE(?, quota_email_seen_at),
                    updated_at=?
                WHERE id=?
                  email_idsubjectseen_atr   runtime_report  )r   r  quotar   Tr  r[   )r   r   rJ   rS   r   	TypeErrorr)   r   r   r   STATUS_ERRORr   r  r   r   )r   r   r   rd   r  status_code_intrequested_statusr   r&   r   r   r   r  updateds                 r!   report_key_statuszKeyStore.report_key_status  s   gkk),2399;233kk-0	#2=2Ic+.tO w{{84:;AACGKK(HGKK	,BHbIOOQi\\^ 5	Jt,,G'T]]_C{#(?C5	J 5	J
 &5F)+J#%)9=S)S/ ";;CL
>>"348KK ! !'!::>>*;U:VW6z~~j7Q6RS  LL	 #%4LNN:.NN:.NN9-NN9-D	N2 NNCI ds%v!/JW ll#FSQUYHYZcceG#T-?-?-HIk5	J 5	J :& 	#"O	#
5	J 5	Js%   I"  (I:EI:"I76I7:Jc           
        t        |j                  d      xs d      j                         }|syt               }| j	                         5 }| j                  ||      }|
	 ddd       y|j                  d||j                  d      |j                  d      |j                  d      ||f       ddd       y# 1 sw Y   yxY w)zBPersist mailbox-derived quota metadata without changing key state.r   r   Na?  
                UPDATE api_keys
                SET quota_reset_at=?, quota_email_id=COALESCE(?, quota_email_id),
                    quota_email_subject=COALESCE(?, quota_email_subject),
                    quota_email_seen_at=COALESCE(?, quota_email_seen_at), updated_at=?
                WHERE id=?
                r  r  r  )r   r   rJ   r)   r   r   r   )r   r   r  r   r&   r   r   s          r!   update_quota_infozKeyStore.update_quota_info  s    z~~j17R8>>@i\\^ 	t""40C{	 	 LL NN:.NN9-NN9-		 	 	s   
B7)AB77C c           
        t        j                  t        j                        t	        t        d|            z
  j                  d      }| j                         5 }|j                  dt        |t        d|      f      j                         }d d d        D cg c]  }t        |d          c}S # 1 sw Y   %xY wc c}w )Ng        )r#   r#   r$   a	  
                SELECT id FROM api_keys
                WHERE enabled=1 AND status=?
                  AND (last_checked_at IS NULL OR last_checked_at <= ?)
                ORDER BY COALESCE(last_checked_at, '') ASC, id ASC
                LIMIT ?
                r,   r   )r   r&   r   r'   r   r  r(   r   r   r   r   r   )r   r  min_age_secondscutoffr   r   r   s          r!   recheck_candidate_idszKeyStore.recheck_candidate_ids/  s    LL&3sO;T)UU
)Y)
' 	 \\^ 
	t<<  Q7	 hj 
	 +//3CI//
	 
	 0s   2B1B=1B:Tlookup_mailboxc               2   t               }| j                         5 }| j                  ||      }|t        d      |d   s[|j	                  dt
        |||f       | j                  ||ddi        | j                  ||      }| j                  |      cd d d        S |j                  t        |d               }	i }
t        }|d   }|}|	j                  d      xs d	}|	d
   rt        }|}|d   }d	}n|	j                  d      dk(  rt        }|r"| j                  |t        |d   xs d	            }
| j                  ||
      }
|
j                  d      rd|
j                  d       }nZ|
j                  d      dk(  rFd|
j                  d       }n1|	j                  d      dv rt        }n|	j                  d      t         }|j	                  d|||||	j                  d      |d d |
j                  d      xs |d   |
j                  d      xs |d   |
j                  d      xs |d   |
j                  d      xs |d   ||f       | j                  ||dd|	|
d        | j                  ||      }| j                  |      cd d d        S # 1 sw Y   y xY w)!Nr   r~   zHUPDATE api_keys SET status=?, last_checked_at=?, updated_at=? WHERE id=?checkzkey disabledrd   last_success_atr   r   oklast_failure_atr  r  r|   r   z>401 from Mistral; mailbox unavailable, default quota reset at r   r   r  zO401 from Mistral; quota email reset date was not found; default quota reset at >     aL  
                UPDATE api_keys
                SET status=?, last_checked_at=?, last_success_at=?, last_failure_at=?,
                    last_status_code=?, last_error=?, quota_reset_at=?,
                    quota_email_id=?, quota_email_subject=?, quota_email_seen_at=?, updated_at=?
                WHERE id=?
                r  r   r  quota_email_idr  quota_email_subjectr  quota_email_seen_atzmistral validation completed)mistralr  )r)   r   r   r   r   r   r   r   validate_keyr   r"  r   r   r   r  r  r   )r   r   r8  r   r.  r&   r   r   r%  r   r  r   r1  r3  
last_errors                  r!   	check_onezKeyStore.check_one@  s    i\\^ F	/t""40C{//y>^$c37 tVWnbI**48))'2F	/ F	/ ))#c)n*=>F)+J!F!"34O!OG,2Jd|&"%"%&7"8
M*c1/!!%!A!A'3sSZ|Oa_aKb!cJ!;;CL
>>.1#ablbpbpq{b|a}!~J^^$56:MM#rs}  tB  tB  CM  tN  sO  "PJM*e3%M*2'LL ##JJ}-u%NN:.G#6F2GNN:.G#6F2GNN9-K5J1KNN9-K5J1K. NN."Z8 &&tV4G%%g.MF	/ F	/ F	/s   A6JG(JJc                h   | j                         5 }|j                  d      j                         }d d d        dddi d}D ]a  }t        |d         }|dxx   |z  cc<   |d   r|dxx   |z  cc<   n|dxx   |z  cc<   |d   j	                  |d	   d      |z   |d   |d	   <   c |S # 1 sw Y   xxY w)
NzPSELECT status, enabled, COUNT(*) AS count FROM api_keys GROUP BY status, enabledr   )totalr~   r   	by_statuscountr=  r~   r   r>  r   )r   r   r   r   r   )r   r   r   totalsr   r?  s         r!   summaryzKeyStore.summary  s    \\^ 	t<< rs||~D	+,XZ![ 	cCG%E7Ou$O9~y!U*!z"e+"171D1H1HXXY1Z]b1bF;H.	c 	 	s    B((B1N)r   r   returnNone)rB  sqlite3.ConnectionrB  rC  )r   sqlite3.Rowr   r   rB  Dict[str, Any])r   rG  rB  rG  )r   Dict[str, List[str]]rB  List[Dict[str, Any]])r   r   r   r   rB  Optional[Dict[str, Any]])r   rD  r   r   rB  zOptional[sqlite3.Row])r   r   r   rG  rB  rG  )r   r   rB  r   )rB  z&Tuple[List[str], List[Dict[str, Any]]])r   'MailboxClient'r   r   rB  rG  )r   rF  r  rG  rB  rG  )r   rD  r   r   r
  r   r  r   r  rG  rB  rC  )2   )r   r   r  r   rB  rI  )r   rG  r   rK  rB  rG  )r   r   r  rG  rB  rC  )r  r   r*  rR   rB  z	List[int])
r   r   r8  z'MistralClient'r   rK  r.  r   rB  rG  rB  rG  )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r   r  r&  r(  r,  r;  rA  r   r    r!   r   r      s    
%N IN 	 +D]. >C JW&+P'":"qH	
 
 
 	

 
 !
 

 /AJF80.  $O/O/ !O/ !	O/ O/ 
O/br    r   c                  $    e Zd ZddZddZddZy)MistralClientc                |    |j                  d      | _        |j                         | _        t	        d|      | _        y N/g      ?)rstripbase_urlrJ   proxyr  timeout_sec)r   rT  rU  rV  s       r!   r   zMistralClient.__init__  s.     ,[[]
sK0r    c                    | j                   st        j                  j                         S t        j                  j                  t        j                  j	                  | j                   | j                   d            S )N)httphttps)rU  urllibrequestbuild_openerProxyHandlerr   s    r!   openerzMistralClient.opener  sR    zz>>..00~~**NN''djj(QR
 	
r    c                   t         j                  j                  | j                   dd| dddd      }	 | j	                         j                  || j                        5 }|j                  d	      j                  d
d      }d|j                  cxk  xr dk  nc |j                  |d d dcd d d        S # 1 sw Y   y xY w# t         j                  j                  $ rL}|j                  d	      j                  d
d      }d|j                  |d d xs t        |      dcY d }~S d }~wt        $ r}dd t        |      dcY d }~S d }~ww xY w)Nz/modelsBearer application/jsonyunwo-mistral-key-manager/1.0)AuthorizationAccept
User-AgentGET)headersmethodr   i   r=   ignoreerrorsr  i,  r  )r2  r  bodyF)r2  r  r   )rZ  r[  RequestrT  r_  openrV  readdecoder   r   	HTTPErrorcoder   	Exception)r   rd   r[  responserm  r  s         r!   r9  zMistralClient.validate_key  s?   ..((}}oW%#*7)!4,=
  ) 
	I##GT5E5E#F p(}}T*11'(1K!X__:s:8??dhimjmdnop p p ||%% 	[88D>(((BD4:CYQTUXQYZZ 	Is3xHH	IsO   +C #AB:0	C :C?C C E#AD*$E*E6E
E
EN)rT  r   rU  r   rV  rR   rB  rC  )rB  zurllib.request.OpenerDirector)rd   r   rB  rG  )r   r   r   r   r_  r9  r   r    r!   rO  rO    s    1

Ir    rO  c                      e Zd Z	 	 	 	 d
	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdddddd	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZdddZdd	Zy)MailboxClientc                    |j                  d      | _        t        d|      | _        |j	                         | _        |j	                         | _        |j	                         | _        |j	                         | _        y rQ  )	rS  rT  r  rV  rJ   usernamepasswordtokenrd   )r   rT  rV  ry  rz  r{  rd   s          r!   r   zMailboxClient.__init__  sY     !,sK0 ( ([[]
}}r    rg  NTri  r   r   authenticated
retry_authc          	     z   |r| j                   s| j                          | j                   | }|r$| dt        j                  j                  |       }d }ddd}	|t        |      }d|	d<   |r$| j                   xs | j                  }
|
rd|
 |	d<   t        j                  j                  |||	|	      }	 t        j                  j                  || j                  
      5 }t        j                  |j                         j                  dd            cd d d        S # 1 sw Y   y xY w# t        j                   j"                  $ rp}|rh| j                   s\|rZ|j$                  dk(  rK| j&                  r?| j(                  r3d| _        | j                          | j+                  |||||d      cY d }~S  d }~ww xY w)Nr   rb  rc  )re  rf  application/json; charset=utf-8Content-Typera  rd  )r   rh  ri  r   r=   rj  rk  r  r   Fr|  )rd   ensure_tokenrT  rZ  parse	urlencoderA   r{  r[  rn  urlopenrV  r>   loadsrp  rq  r   rr  rs  ry  rz  request_json)r   pathri  r   r   r}  r~  urlrm  rh  bearerr[  ru  r  s                 r!   r  zMailboxClient.request_json  s    v&E6<<11&9:;C $(9
 g&D&GGN#\\/TZZF-4VH+=(..((4QW(X	''9I9I'J Thzz(--/"8"8"8"RST T T||%% 	T\\jSXXQT_Y]YfYfkokxkx
!!#((!!#"/$ )   	sC   8+D- #4D!	D- !D*&D- *D- -F:
A$F5.F:4F55F:c                   | j                   ry | j                  r| j                  st        d      | j	                  dd| j                  | j                  dd      }t        |t              r|j                  d      nd }t        |t              r|j                  d      nd }|st        d	      t        |      | _         y )
Nz:MAILBOX_API_USERNAME and MAILBOX_API_PASSWORD are requiredz/api/v1/auth/tokenPOST)ry  rz  F)ri  r   r}  r   r{  z(mailbox API did not return an auth token)	r{  ry  rz  RuntimeErrorr  
isinstancer   r   r   )r   r   r   r{  s       r!   r  zMailboxClient.ensure_token  s    ::}}DMM[\\## !%DMMJ	 $ 
 '1$&?w{{6"T%/d%;!IJJZ
r    c                L   |sg S 	 | j                   rc| j                  d|dt        t        d|      d      dd      }t	        |t
              r|j                  d      nd }t	        |t              r|S g S | j                  d|d|d	      }t	        |t
              r|j                  d      nd }t	        |t
              r|j                  d      nd }t	        |t              r|S g S # t        $ r4}d
| d| }t        |t        j                         t        |      |d }~ww xY w)Nz/api/emailsr,   d   inbox)r   pager  folder)r   emailsz/api/v1/messages)r   r  sizezMailbox API fetch failed for : filer   messages)rd   r  r  r  r  r   r   r   rt  printsysstderrr   )r   r   r  r   r  r  r  r   s           r!   fetch_emailszMailboxClient.fetch_emails  s%   I	7||++!#* !!$SD\3!7")	 ,  5?w4M7;;x0SW#-h#=xE2E''"#*AtD ( G '1$&?w{{6"T+5dD+A488J't%h5x=2=  	75gYbFG'

+$W-36	7s$   A,C& 3C& 5C& &	D#//DD#c           
       
 | j                  |      }|D ]%  }|
| j                  rg|j                  d      rV	 | j                  dt        j
                  j                  t        |d         d             }t        |t              ri ||
t        
j                  d      xs d      }d	j                  
fd
dD              }dj                  
fddD              }|j                         }	d|j                         vrd|	vrd|	v s	d|	v sd|	v s
j                  d      |t!        |      t#               dc S  i S # t        $ r;}t        d|j                  d       d| t        j                         Y d }~jd }~ww xY w)Nr   z/api/emails/r   )safez Mailbox detail fetch failed for r  r  r  rX   c              3  Z   K   | ]"  }t        j                  |      xs d        $ ywr   Nr   r   rx   r   r|   s     r!   rz   z1MailboxClient.find_quota_email.<locals>.<genexpr>=  s,       EIIe$*+   (+)sender	from_addr	from_namerV   c              3  Z   K   | ]"  }t        j                  |      xs d        $ ywr  r  r  s     r!   rz   z1MailboxClient.find_quota_email.<locals>.<genexpr>A  s,      	 EIIe$*+	r  )r  	text_body	html_bodytext_contenthtml_contentr8  zmonthly quotazreached 100%zquota will reset)r  r  r   r  )r  rd   r   r  rZ  r  quoter   r  r   rt  r  r  r  r   rK   rq   r)   )r   r   r  listed_emaildetailr  r  r  rm   loweredr|   s             @r!   r   zMailboxClient.find_quota_email.  s   ""7+" )	L E|| 0 0 6!..&v||'9'9#l4>P:QXZ'9'[&\]F "&$/ :< :6 : %))I.4"5GXX A F 99 		 	D jjlG.9G3K7*!W,%0!IIdO",T2"9	 I)	T 	C ! <\=M=Md=S<TTVWZV[\cfcmcmns   AE  	F	0E??F)r   r   r   r   )rT  r   rV  rR   ry  r   rz  r   r{  r   rd   r   rB  rC  )r  r   ri  r   r   rJ  r   rJ  r}  r   r~  r   rB  rG  rE  )   )r   r   r  r   rB  rI  )r   r   rB  rG  )r   r   r   r   r  r  r  r   r   r    r!   rw  rw    s    
 '' ' 	'
 ' ' ' 
'( +/,0",, 	,
 ), *, , , 
,\ ">8,r    rw  c                  <    e Zd ZddZd	dZd	dZd
dZddZddZy)Appc           	     `   || _         || _        || _        |j                         | _        t        dd      | _        t        j                         | _	        t               | _        t        dt        dd            | _        t        dt        t        dd	                  | _        t        dt        t        d
d                  }t        |d      | _        t        j$                         | _        t        j(                  | j*                  dd      | _        | j,                  j/                          y )N   zmistral-quota-mailbox)max_workersthread_name_prefixg      >@MISTRAL_KEY_MANAGER_RECHECK_SECg     r@r,   !MISTRAL_KEY_MANAGER_RECHECK_BATCHg      0@#MISTRAL_KEY_MANAGER_RECHECK_WORKERSg      @zmistral-key-recheckzmistral-key-recheck-schedulerT)targetrM   daemon)storer8  r   rJ   r{  r   _quota_lookup_executor	threadingLock_quota_lookup_lockset_quota_lookup_inflightr  rT   _recheck_interval_secr   _recheck_batch_size_recheck_executorEvent_recheck_stopThread_recheck_loop_recheck_threadstart)r   r  r8  r   r{  recheck_workerss         r!   r   zApp.__init__^  s
   
[[]
&86'
# #,.."203#%(<eD&
" $'BDIJ$
  DcJK
 "4'4"
 '__.(//%%0 

 	""$r    c                    |sy| j                   5  || j                  v r
	 ddd       y| j                  j                  |       ddd       | j                  j	                  | j
                  ||       y# 1 sw Y   1xY w)z<Enrich a 401 report without holding the parser request open.N)r  r  addr  submit_lookup_quota_email)r   r   r|   s      r!   schedule_quota_lookupzApp.schedule_quota_lookup  sw    $$ 	4444	4 	4 ''++F3	4 	##**4+C+CVUS		4 	4s   A4A44A=c                   	 | j                   j                  | j                  |      }|j                  d      r-|j                  d      s| j                   j	                  ||       | j                  5  | j                  j                  |       d d d        y # t
        $ r t        j                          Y Pw xY w# 1 sw Y   y xY w# | j                  5  | j                  j                  |       d d d        w # 1 sw Y   w xY wxY w)Nr   r   )r  r  r   r   r(  rt  	traceback	print_excr  r  discard)r   r   r|   r  s       r!   r  zApp._lookup_quota_email  s    	<??eTJ~~j)*..2P

,,VZ@ (( <++33F;< <  	"!	"< <(( <++33F;< < <sH   A$B 2B:B74C 6B77C :CDC8/	D8D=Dc                   | j                   j                         s	 | j                  j                          | j                  j	                  | j
                  | j                        }|D ](  }| j                  j                  | j                  |       * 	 | j                   j                  | j                         | j                   j                         sy y # t        $ r t        j                          Y aw xY wrw   )r  is_setr  r   r,  r  r  r  r  _recheck_keyrt  r  r  wait)r   key_idsr   s      r!   r  zApp._recheck_loop  s    $$++-	&

446**::,,.. & MF**11$2C2CVLM ##D$>$>? $$++-  &##%&s   A7C C65C6c                D   	 | j                   j                  || j                  | j                  d      }|j	                  d      t
        k(  r1|j	                  d      r| j                  |t        |d                y y y # t        $ r t        j                          Y y w xY w)NFr-  r   r|   )r  r;  r8  r   r   r   r  r   rt  r  r  )r   r   r[   s      r!   r  zApp._recheck_key  s    
	"::''$	 ( D xx!%;;@Q**63tG}3EF AR; 	"!	"s   A:A? ?BBc                    | j                   sy|j                  j                  dd      }|j                  j                  dd      }|j                  d      r|dd  j	                         }|| j                   k(  S )NTrd  r   zX-Admin-Tokenra     )r{  rh  r   r   rJ   )r   handlerauthr{  s       r!   
authorizedzApp.authorized  se    zz""?B7##OR8??9%HNN$E

""r    N)
r  r   r8  rO  r   rw  r{  r   rB  rC  )r   r   r|   r   rB  rC  rE  )r   r   rB  rC  )r  r	   rB  r   )	r   r   r   r   r  r  r  r  r  r   r    r!   r  r  ]  s#    !%FT	<@"#r    r  zOptional[App]APPc                      e Zd ZdZd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dZddZddZddZddZy)HandlerzYunwoMistralKeyManager/1.0c                f    t         j                  j                  dt               d||z  d       y )N[z] rV   )r  r  writer)   )r   ro   argss      r!   log_messagezHandler.log_message  s    

	3:>?r    c                   	 t        |      }| j                  |       | j                  dd       | j                  dt        t	        |                   | j                  dd       | j                  dd       | j                  dd	       | j                          | j                  j                  |       y # t        $ r Y y w xY w)
Nr  r  Content-LengthAccess-Control-Allow-Originr`   Access-Control-Allow-Headers*Authorization, X-Admin-Token, Content-TypeAccess-Control-Allow-Methods!GET, POST, PATCH, DELETE, OPTIONS)	rA   send_responsesend_headerr   rc   end_headerswfiler  BrokenPipeError)r   r   r   rm  s       r!   	send_jsonzHandler.send_json  s    	d#Dv&^-NO-s3t9~>:C@;=ij;=`aJJT" 	 	s   B3B6 6	CCc                0    | j                  d|d|       y )NFr  r   )r  )r   r   r  s      r!   send_error_jsonzHandler.send_error_json  s    57;FKr    c                    | j                  d       | j                  dd       | j                  dd       | j                  dd       | j                          y )N   r  r`   r  r  r  r  )r  r  r  r^  s    r!   
do_OPTIONSzHandler.do_OPTIONS  sQ    36<79ef79\]r    c                    t        | j                  j                  d      xs d      }|dk  ri S |dkD  rt        d      | j                  j                  |      }t        j                  |j                  d            S )Nr  r   i  P zrequest body is too larger=   )	r   rh  r   rS   rfilerp  r>   r  rq  )r   lengthrZ   s      r!   read_json_bodyzHandler.read_json_body  sk    T\\%%&67<1=Q;IO#899jjoof%zz#**W-..r    c                0    t         t        d      t         S )Nzapp not initialized)r  r  r^  s    r!   require_appzHandler.require_app  s    ;455
r    c                    | j                         }|j                  |       ry| j                  t        j                  d       y)NTunauthorizedF)r  r  r  r   UNAUTHORIZED)r   apps     r!   ensure_authzHandler.ensure_auth  s8     >>$Z44nEr    c           
        | j                         }|dk(  r|dk(  r| j                  dt               t        |j                  j
                        |j                  j                  |j                  j                  t        |j                        |j                  j                         d       y|j                  d      r| j                         sy|dk(  rW|dk(  rR| j                  d|j                  j                  |j                  j                  t        |j                        d       y|dk(  rN|dk(  rI|j                  j                  |      }| j                  d||j                  j                         d	       y|dk(  rE|d
k(  r@|j                  j                  | j!                               }| j                  d|dd       y|dk(  r|d
k(  r| j!                         }t#        t        |j%                  d      xs d            }|D 	cg c]  }	|j                  j                  |	       }}	| j                  dt'        |      |dd       y|dk(  rK|dk(  rF|j                  j)                         \  }
}| j                  d|
|t               t'        |
      d       y|dk(  r$|d
k(  r| j!                         }|j                  j+                  ||j                        }t        |j%                  d      xs d      j-                         dk(  }|j%                  d      r{|s|j%                  d      t.        k(  ra|j%                  d      xs i }	 t1        |j%                  d            }|r/|j%                  d      r|j7                  |t        |d                | j                  ||j%                  d      rd       yd       y|dk(  r!|d
k(  r| j!                         }t        |j%                  d d            }|rd!d"gini }|j                  j                  |      D cg c]  }t1        |d          }}g }|D ]  }|j                  j9                  ||j                  |j                  d#$      }|j%                  d      t.        k(  r/|j%                  d      r|j7                  |t        |d                |j;                  |        | j                  dt'        |      |d       yt=        j>                  d%|      }|rt1        |jA                  d&            }|dk(  rE|j                  jC                  |      }|| jE                  dd'       y| j                  d|d       y|d(k(  r?|j                  jG                  || j!                               }| j                  d|d       y|d)k(  rB|j                  jI                  |      r| j                  ddi       y| jE                  dd'       yt=        j>                  d*|      }|r|d
k(  rt1        |jA                  d&            }|j                  j9                  ||j                  |j                  d#$      }|j%                  d      t.        k(  r/|j%                  d      r|j7                  |t        |d                | j                  d|d       yt=        j>                  d+|      }|rM|dk(  rH|j                  jK                  t1        |jA                  d&                  }| j                  d|d,       yy#c c}	w # t2        t4        f$ r d}Y w xY wc c}w )-Nz/api/healthrg  T)r  timedbmailbox_api_basemistral_api_base	has_tokenrA  /api/z/api/config)r  r  r  r  z	/api/keys)r  r   rA  r  r      r  z/api/keys/bulkrm   r   )r  r?  r   z/api/keys/active)r  r   r   generated_atr?  z/api/keys/reportr  401r  r   r[   r   r   r|   r    z/api/keys/check-allenabled_onlyr~   rC   Fr-  z^/api/keys/(\d+)$r,   r   PATCHDELETEz^/api/keys/(\d+)/check$z^/api/keys/(\d+)/events$)r  r  )&r  r  r)   r   r  r   r   rT  r8  r   r{  rA  r   r  r   r   r   r   r   rc   r   r&  rJ   r   r   r!  rS   r  r;  r   rh   rn   groupr   r  r   r   r  )r   ri  r  queryr  r   r[   r   r   r   r   r   is_quota_reportr   r  r   idsrn   r  s                      r!   	route_apizHandler.route_api  sZ    = Vu_NN##Icii//0(+(<(<(+(<(<!%cii"yy002
 ??7#D,<,<,>= Vu_NN#(+(<(<(+(<(<!%cii	 ;6U?II''.ENNte		HYHYH[\];6V#399..t/B/B/DEDNNtT:3NG##&(8))+G#CF(;(Ar$BCDAEF#SYY005FEFNNtc%j5QZ]N^%%&E/))//1KD%NN# "$+I Y %%&F*:))+GYY00#++FF!'++m"<"BCIIKuTO

9%$H(=AW(Wzz&)/R $0F dhhw/--fc$w-6HINN6I1F#NP MPNP((Vv-=))+GND ABL+7i#'RF/2yy/B/B6/JKt3tDz?KCKE 	#yy**KKKK#(	 +  88H%)??DHHWDU--fc$w-6HIT"	# NNtc%j5QR-t4Q(Fyy((0<((o>  NNtT#BC yy**643F3F3HI4>?!99''/NNIt#45  ((o>3T:Vv%Q(F99&&$	 ' D xx!%;;@Q))&#d7m2DENNtT:;4d;Vu_YY%%c%++a.&9:FNNtv>? G8 ":. F Ls   "\&\+ )]+] ?] c                &    | j                  d       y )Nrg  handle_requestr^  s    r!   do_GETzHandler.do_GET  s    E"r    c                &    | j                  d       y )Nr  r  r^  s    r!   do_POSTzHandler.do_POST  s    F#r    c                &    | j                  d       y )Nr  r  r^  s    r!   do_PATCHzHandler.do_PATCH  s    G$r    c                &    | j                  d       y )Nr  r  r^  s    r!   	do_DELETEzHandler.do_DELETE  s    H%r    c                   	 t         j                  j                  | j                        }|j                  }t         j                  j	                  |j
                        }|j                  d      r&| j                  |||      s| j                  dd       y | j                  |       y # t        $ r%}| j                  dt        |             Y d }~y d }~wt        $ r%}| j                  dt        |             Y d }~y d }~wt        j                  $ r | j                  dd       Y y t        $ r Y y t         $ r9}t#        j$                          | j                  dt        |             Y d }~y d }~ww xY w)Nr  r  z	not foundi  zinvalid jsonr  )rZ  r  urlparser  parse_qsr  r   r  r  serve_staticr   r   rS   r>   JSONDecodeErrorr  rt  r  r  )r   ri  rp   r  r  r  s         r!   r  zHandler.handle_request  s   	0\\**4995F;;DLL))&,,7Ew'~~fdE:((k:d# 	0  c#h// 	0  c#h//## 	6  n5 	 	0!  c#h//	0sB   BB) B) )	E32CE3C>>(E3(E32E3:/E..E3c                @   |dv r
t         dz  }nt        j                  j                  |      j	                  d      }|j                  d      r|t        d      d  }t         |z  j                         }t        |      j                  t        t         j                                     s| j                  d       y |j                         r|j                         s| j                  d       y |j                  j                         }ddd	d
dj                  |d      }|j                         }| j!                  d       | j#                  d|       | j#                  dt        t        |                   | j%                          | j&                  j)                  |       y )N>   r   rR  z
index.htmlrR  zstatic/r4  r  ztext/html; charset=utf-8ztext/css; charset=utf-8z%application/javascript; charset=utf-8zimage/svg+xml)z.htmlz.cssz.jsz.svgzapplication/octet-streamr  r  r  )
STATIC_DIRrZ  r  unquotelstripr   rc   resolver   
send_errorexistsis_filesuffixrK   r   
read_bytesr  r  r  r  r  )r   r  	file_path
normalizedr2  content_typerm  s          r!   r(  zHandler.serve_static  sX   9"\1I--d3::3?J$$Y/'I(89
#j099;Iy>,,S1C1C1E-FG$!):):)<OOC !!'')/-:#	

 #f0
1 	 ##%36)3s4y>:

r    N)ro   r   r  r   rB  rC  )r  )r   r   r   r   rB  rC  )r   r   r  r   rB  rC  rE  rM  )rB  r  )rB  r   )ri  r   r  r   r  rH  rB  r   )ri  r   rB  rC  )r  r   rB  rC  )r   r   r   server_versionr  r  r  r  r   r  r  r  r  r   r"  r$  r  r(  r   r    r!   r  r    sQ    1N@ L/
JX#$%&0,r    r  c                   t        | j                  xs t        j                  d      xs t              }| j
                  xs t        j                  d      xs t        }| j                  xs t        j                  dt              }| j                  xs t        j                  dt              }| j                  xs t        j                  dd      }| j                  xs t        j                  dd      }| j                  xs t        j                  d      xs t        }| j                  xs0 t        j                  d	      xs t        j                  d
      xs d}t!        dd      }	| j"                  | j"                  nt        j                  dd      }
t%        t'        |      t)        |||	      t+        ||	||||      |
xs d      S )NMISTRAL_KEY_MANAGER_DBMAILBOX_API_BASEMAILBOX_API_USERNAMEMAILBOX_API_PASSWORDMAILBOX_API_TOKENr   MAILBOX_API_KEYMISTRAL_API_BASEMISTRAL_API_PROXYMISTRAL_OCR_PROXYMISTRAL_KEY_MANAGER_TIMEOUT_SECg      $@MISTRAL_KEY_MANAGER_TOKEN)r  r8  r   r{  )r   r
  rH   rI   DEFAULT_DB_PATHr  DEFAULT_MAILBOX_API_BASEmailbox_api_usernameDEFAULT_MAILBOX_API_USERNAMEmailbox_api_passwordDEFAULT_MAILBOX_API_PASSWORDmailbox_api_tokenmailbox_api_keyr  DEFAULT_MISTRAL_API_BASEmistral_proxyrT   r{  r  r   rO  rw  )r  r   mailbox_basemailbox_usernamemailbox_passwordmailbox_tokenrK  mistral_baserU  r   r{  s              r!   	build_apprS    sy   477Tbii(@AT_UG((eBII6H,IeMeL00sBII>TVr4s00sBII>TVr4s**Pbii8KR.PM**Nbii8I2.NO((eBII6H,IeMeLh")),?"@hBIINaDbhfhE>EG**0DJJbii@[]_6`EwlE7;
 kr r    c                   t        j                  dd      }|st        j                  dd      }t        j                  dd      }t        j                  dd      }t        |      D ]  }| j                  |||dd        y )	NMISTRAL_KEY_MANAGER_SEED_KEYSr   MISTRAL_OCR_API_KEYSMISTRAL_KEY_MANAGER_SEED_EMAILMISTRAL_KEY_MANAGER_SEED_LABELzenv-seedTr{   )rH   rI   r]   r   )r  rZ   r|   r}   rd   s        r!   seed_from_envrY    sw    
))3R
8Cii.3II6;EII6
CE!#& gGee`d efgr    c           
         t        j                  d      }|j                  dt        j                  dd             |j                  dt
        t        t        j                  dd	            
       |j                  dt        j                  dt        t                           |j                  dd        |j                  dt        j                  dt                     |j                  dt        j                  dt                     |j                  dt        j                  dt                     |j                  dt        j                  dd             |j                  dt        j                  dd             |j                  dt        j                  dt                     |j                  dt        j                  dt        j                  dd                   |j                  |       S )NzYunwo Mistral API key manager)descriptionz--hostMISTRAL_KEY_MANAGER_HOSTz	127.0.0.1)rN   z--portMISTRAL_KEY_MANAGER_PORT8097)typerN   z--dbr9  z--tokenz--mailbox-api-baser:  z--mailbox-api-usernamer;  z--mailbox-api-passwordr<  z--mailbox-api-tokenr=  r   z--mailbox-api-keyr>  z--mistral-api-baser?  z--mistral-proxyr@  rA  )argparseArgumentParseradd_argumentrH   rI   r   r   rD  rE  rG  rI  rL  
parse_args)argvparsers     r!   rc  rc    s   $$1PQF
"))4NP[*\]
sC		B\^d8e4fg
		2JCP_L`(ab
	40
,bii@RTl6mn
0"))DZ\x:yz
0"))DZ\x:yz
-ryyATVX7YZ
+RYY?PRT5UV
,bii@RTl6mn
)299=PRTR[R[\oqsRt3uvT""r    c           
     $   t        |       }t        |      at        dd      rt	        t        j
                         t        |j                  |j                  ft              }t        d|j                   d|j                   dt        j
                  j                   dt        j                  j                   d       	 |j                          |j!                          y
# t        $ r t        d	d       Y )w xY w# |j!                          w xY w)N!MISTRAL_KEY_MANAGER_SEED_ON_STARTFz(Mistral key manager listening on http://r:   z db=z	 mailbox=T)flushzshutting downr   )rc  rS  r  rP   rY  r  r
   hostportr  r  r   r   rT  serve_foreverKeyboardInterruptserver_close)rd  r  servers      r!   mainro    s    dD
D/C8%@cii  $))TYY!7AF	
2499+Qtyyk Jii 	#++*>*>)?	A
 		  +oT*+ 	s$    C! !C:7C= 9C::C= =D__main__)rB  r   )r   r   rB  bytes)F)rM   r   rN   r   rB  r   )rM   r   rN   rR   rB  rR   )rZ   r   rB  z	List[str])rd   r   rB  r   )rm   r   rB  zOptional[str])rm   r   rB  rI  )r  argparse.NamespacerB  r  )r  r   rB  rC  rw   )rd  Optional[List[str]]rB  rr  )rd  rs  rB  r   )K__doc__
__future__r   r`  r   r>   rH   rh   r   r  r  r  urllib.errorrZ  urllib.parseurllib.requestconcurrent.futuresr   r   r   r   rX  r   http.serverr	   r
   pathlibr   typingr   r   r   r   r   r   __file__r.  r   ROOT_DIRr+  rD  rE  rL  rG  rI  r   r   r   r   r"  r   r  r   r)   r7   rA   rP   rT   r]   rf   rq   r   r   rO  rw  r  r  __annotations__r  rS  rY  rc  ro  r   
SystemExitr   r    r!   <module>r     sw   #  
  	 	  
      1 2 2  C  = = >!!#** 
V#m3: 6 ! ! * 
 !/	 	D9 WD1) F~ ~BI IDX Xv[# [#| ] }$ }@4g# * z
TV
 r    