
    rKGjeO                    L   S r SSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKJrJr  SSKJr  SrSSSS	S
.rSS0rSSSSSSSSS.r0 qSq\R8                  " 5       r\R<                  R?                  \R<                  RA                  \!5      5      r"\R<                  RG                  \"S5      r$\RJ                  " \$SS9  Sr&Sr'\R<                  RG                  \$S5      r(0 q)\R8                  " 5       r*S r+S r,\," 5         \
R<                  R[                  S\"5         SSK.J/r/  S \1S!\1S"\1S#\14S$ jr2S%\14S& jr3S%\14S' jr4S( r5S \1S#\14S) jr6S \1S#\74S* jr8S \1S+\74S, jr9S \1S-\:S#\:4S. jr;SWS \1S0\1S1\<S2\<S#\:4
S3 jjr=S \1S4\1S#\:4S5 jr>S \1S4\1S#\?4S6 jr@SXS7 jrAS8 rB " S9 S:\R                  R                  5      rE " S; S<\\R                  R                  5      rG\HS=:X  Ga  \R                  " \"5        \J" S>5        \J" S?5        \J" S@\ 35        \J" SA\R                  " 5        35        \J" SB5        \J" S>5        \R                  " \R                  \R                  5      rN \NR                  SC\45        \NR                  5         \G" SC\4\E5      rC\J" SO\ 35        \J" SP\7" \R                  5       5       35        \J" SQ\ SR35        \J" SS\ ST35         \CR                  5         gg! \0 a    S r/ GNf = f! \Q a    \J" SD\ SE35        \
R                  SF:X  a  SSKSrS\SR                  " SG\ 3SSSSH9rU\UR                  R                  5       R                  SI5       Ho  rY\YR                  5       R                  5       rZ\[" \Z5      SJ:  d  M/  \ZSK   R                  5       (       d  MI  \SR                  " SL\ZSK    3SSSM9  \J" SN\ZSK    35        Mq     \R                  " S/5         GN]f = f! \` a#    \J" SU5        \CR                  5         \J" SV5         gf = f)Yu   
AI-Ad-Agent API Proxy Server
解决局域网浏览器访问时的 CORS 跨域问题
所有前端请求通过此代理转发到后端服务

v2.0 新增:
- 用户认证 (Excel 数据库)
- 内容记录 CRUD API
- 按用户隔离数据
    N)datetime	timedelta)ThreadingMixIni(#  zhttp://127.0.0.1:11434http://127.0.0.1:8188zhttp://127.0.0.1:8080zhttp://127.0.0.1:9880)z/api/ollama/z/api/comfyui/z/api/iopaint/z/api/gptsovits//api/digital-humanzhttp://127.0.0.1:8766)zhttp://127.0.0.1:11434/api/tagsGET   )z"http://127.0.0.1:8188/system_statsr      )zhttp://127.0.0.1:8080/HEADr	   )zhttp://127.0.0.1:9880/docsr      )z1http://127.0.0.1:8766/api/digital-human/v2/statusr   r   )zhttp://127.0.0.1:8383/r      )zhttp://127.0.0.1:18180/r   r   )zhttp://127.0.0.1:10095/r   r   )OllamaComfyUIIOPaintz
GPT-SoVITSu   数字人v2u   Duix视频合成zDuix.TTSzDuix.ASRdataTexist_okzhengfangyuan_ad_agent_2026   ztokens.jsonc                  h    [            0 n [        R                  5        H(  u  pUS   US   US   US   R                  5       S.X'   M*     SSS5        [	        [
        SSS	9 n[        R                  " W US
S9  SSS5        g! , (       d  f       N== f! , (       d  f       g= f! [         a     gf = f)u"   将当前 token 持久化到文件usernamedisplay_namerole
expires_atr   r   r   r   Nwutf-8encodingFensure_ascii)	_tokens_lock_active_tokensitems	isoformatopenTOKENS_FILEjsondump	Exception)r   tinfofs       proxy_server.py_save_tokens_to_filer/   G   s    D)//1 $Z 0$($8 L"&|"4">">"@	 2  +sW5IIdAE2 65 \ 65 sG   B$ ABB$ "B9B$ 
BB$ 
B!B$ !B$ $
B10B1c                  h    [         R                  R                  [        5      (       d  g[	        [        SSS9 n [
        R                  " U 5      nSSS5        [        R                  " 5       n0 nWR                  5        H7  u  pE[        R                  " US   5      nX&:  a  M%  US   US   US   US	.X4'   M9     [           UqSSS5        U(       a  [        S
[        U5       S35        gg! , (       d  f       N= f! , (       d  f       N@= f! [         a  n[        SU 35         SnAgSnAff = f)u?   从文件恢复 token（服务器重启后恢复登录状态）Nrr   r   r   r   r   r   r   u   [Auth] 已从文件恢复 u    个有效 tokenu   [Auth] 恢复 token 失败: )ospathexistsr'   r&   r(   loadr   nowr$   fromisoformatr"   r#   printlenr*   )r-   r   r6   restoredr+   r,   r   es           r.   _load_tokens_from_filer<   Z   s   2ww~~k**+sW599Q<D 6llnzz|GA!//\0BCJ , $^ 4V(	HK	 $ %N .s8}o=MNO ! 65 \  2,QC0112sL   (D D C-A0D C>'D -
C;7D >
DD 
D1D,,D1)verify_userc                     g N )r   passwords     r.   r=   r=   ~   s        r   r   r   returnc           	         U  S[         R                  " 5       R                   S[        R                  " 5        3n[        R
                  " [        R                  5       UR                  5       [        R                  5      R                  5       SS nU SU 3n[           U UU[        R                  " 5       [        [        S9-   S.[         U'   SSS5        [#        5         [%        5         U$ ! , (       d  f       N$= f)u   生成 session token:N    .)hoursr   )uuiduuid4hextimehmacnewTOKEN_SECRETencodehashlibsha256	hexdigestr"   r   r6   r   TOKEN_EXPIRE_HOURSr#   _clean_expired_tokensr/   )r   r   r   rawsigtokens         r.   generate_tokenrY      s    Ja

(()499;-
8C
((<&&(#**,
G
Q
Q
STWUW
XCe1SENE	 (",,.9;M+NN	!
u 
 L 
s   0C--
C;rX   c                    U (       d  g[            [        R                  U 5      nU(       d
   SSS5        g[        R                  " 5       US   :  a  [        U 	  SSS5        gUS   US   US   S.sSSS5        $ ! , (       d  f       g= f)u)   验证 token，返回用户信息或 NoneNr   r   r   r   )r   r   r   )r"   r#   getr   r6   )rX   r,   s     r.   validate_tokenr\      sw    	!!%( 
 <<>D..u% 
 Z( 0L
 
s   A;$A;#A;;
B	c                     [            [        R                  U S5        SSS5        [        5         g! , (       d  f       N= f)u   使 token 失效N)r"   r#   popr/   )rX   s    r.   invalidate_tokenr_      s(    	5$' 
 
s   1
?c                      [         R                  " 5       n [        R                  5        VVs/ s H  u  pXS   :  d  M  UPM     nnnU(       a  U H
  n[        U	 M     [	        5         ggs  snnf )u   清理过期 tokenr   N)r   r6   r#   r$   r/   )r6   r+   iexpireds       r.   rU   rU      s]    
,,.C+113M3TQs|_7Lq3GMAq!   Ns
   A+A+c                 P    [         R                  R                  [        U  S35      $ )u   获取用户记录文件路径z_records.json)r2   r3   joinDATA_DIR)r   s    r.   get_records_filerf      s    77<<XJm"<==rB   c                 "   [        U 5      n[        R                  R                  U5      (       d  / $  [	        USSS9 n[
        R                  " U5      sSSS5        $ ! , (       d  f       g= f! [
        R                  [        4 a    / s $ f = f)u   加载用户记录r1   r   r   N)	rf   r2   r3   r4   r&   r(   r5   JSONDecodeErrorIOError)r   fpathr-   s      r.   load_recordsrk      sl    X&E77>>%  	%w/199Q< 0//  '* 	s.   A/ A	A/ 
A,(A/ ,A/ /BBrecordsc                     [        U 5      n[        USSS9 n[        R                  " XSSS9  SSS5        g! , (       d  f       g= f)u   保存用户记录r   r   r   Fr	   )r!   indentN)rf   r&   r(   r)   )r   rl   rj   r-   s       r.   save_recordsro      s6    X&E	eS7	+q		'5; 
,	+	+s	   7
Arecordc                    [         R                  " 5       R                  SS US'   XS'   [        R                  " 5       R                  S5      US'   [        U 5      nUR                  SU5        [        U5      S:  a  USS n[        X5        U$ )	u   添加一条记录N   idr   z%Y-%m-%d %H:%M:%S
created_atr     )
rI   rJ   rK   r   r6   strftimerk   insertr9   ro   )r   rp   rl   s      r.   
add_recordrx      s~    ::<##CR(F4L!:#<<>223FGF<8$GNN1f
7|c$3-#MrB      record_typepage	page_sizec           	          [        U 5      nU Vs/ s H  oUR                  S5      U:X  d  M  UPM     nn[        U5      nUS-
  U-  nX-   n	XhU	 UUU[        SXs-   S-
  U-  5      S.$ s  snf )u-   获取指定类型的记录列表（分页）typery   )rl   totalr{   r|   total_pages)rk   r[   r9   max)
r   rz   r{   r|   rl   r1   filteredr   startends
             r.   get_records_by_typer      s    8$G"C7aeeFm{&B7HCMEAX"E

C#&1u014BC 	 Ds
   A'A'	record_idc                 ^    [        U 5      nU H  nUR                  S5      U:X  d  M  Us  $    g)u   获取单条记录rs   N)rk   r[   )r   r   rl   r1   s       r.   get_record_by_idr      s0    8$G55;)#H  rB   c                     [        U 5      nU Vs/ s H  o3R                  S5      U:w  d  M  UPM     nn[        U5      [        U5      :X  a  g[        X5        gs  snf )u   删除记录rs   FT)rk   r[   r9   ro   )r   r   rl   r1   new_recordss        r.   delete_recordr      sR    8$G%Bgt	)A1gKB
;3w<''	 Cs
   AAc                 l    US:X  a   [         R                  R                  U5      nOBUS:X  a  [         R                  R                  USS9nO[         R                  R                  XS9n[         R                  R                  XCS9 nSSKn[        SU  SUR                   3UR                  S	9  UR                  S
:  sSSS5        $ ! , (       d  f       g= f! [         R                  R                   aP  nSSKnUR                  S
:  n[        SU  SU(       a  SOS SUR                   S3UR                  S	9  Us SnA$ SnAf[         R                  R                   GaH  n	SSKnSSKJn
  U
" U5      nUR                  =(       d    SnUR                  =(       d    UR                   S:X  a  SOSn SSKnUR"                  " UR$                  UR&                  5      nUR)                  U5        UR+                  X45      nUR-                  5         US:X  a#  [        SU  SU SU S3UR                  S	9   Sn	A	g[        SU  SU SU 3UR                  S	9   Sn	A	g! [.         a>  n[        SU  S[1        U5      R2                   SU 3UR                  S	9   SnA Sn	A	gSnAff = fSn	A	f[.         Ga~  nSSKnSSKJn
  U
" U5      nUR                  =(       d    SnUR                  =(       d    UR                   S:X  a  SOSn SSKnUR"                  " UR$                  UR&                  5      nUR)                  U5        UR+                  X45      nUR-                  5         US:X  a3  [        SU  S[1        U5      R2                   S3UR                  S	9   SnAg[        SU  S[1        U5      R2                   SU 3UR                  S	9   SnAg! [.         aQ  n[        SU  S[1        U5      R2                   S[1        U5      R2                   3UR                  S	9   SnA SnAgSnAff = fSnAff = f)u$   检查单个后端服务健康状态r   r   )methodtimeoutr   Nz	[HEALTH] z OK: )fileru    OKFAILz (HTTP ))urlparsez	127.0.0.1httpsi  P   z OK (TCP connect to rE   Tz FAIL: TCP connect failed to Fz FAIL: z: z OK (TCP fallback after z + )urllibrequestRequesturlopensysr8   statusstderrerror	HTTPErrorcodeURLErrorurllib.parser   hostnameportschemesocketAF_INETSOCK_STREAM
settimeout
connect_excloser*   r~   __name__)nameurlr   r   reqrespr   heokuer   parsedhostr   r   sockresulte2r;   s                      r.   check_backend_healthr     ss   <U?..((-Cv..((V(<C..(((<C^^##C#9TIdV56SZZH;;$ :99 <<!! WWs]	$q 8yJQTQ[Q[\	<<   )#-+{{Gfmmw&>sB	==1C1CDDOOG$__d\2FJJL{	$';D64&JQTQ[Q[\	$'DTF!D6RY\YcYcd 	IdV748+<+<*=RtD3::V5	  )#-+{{Gfmmw&>sB	==1C1CDDOOG$__d\2FJJL{	$'?Q@P@P?QQRSZ]ZdZde	$wtAw/?/?.@1#FSZZX 	IdV747+;+;*<CR@Q@Q?RSZ]ZdZde5	's   BC 7C>	C 
CC C P37AE<P3"P3$AJ'3BI:I
J$&0JJ'J$$J''P35AP.BO0O
P+AP&P.&P++P..P3c                  v   [         R                   " 5       n [           U [        -
  S:  a  [        R	                  5       sSSS5        $  SSS5        0 nSSKJnJn  U" SS9 n[        R                  5        VVVVs0 s H!  u  nu  pgnUR                  [        XVXx5      U_M#     n	nnnnU" U	5       H  n
X   n U
R                  5       X'   M     SSS5        [           UqU qSSS5        U$ ! , (       d  f       N= fs  snnnnf ! [         a    SX'    Me  f = f! , (       d  f       NQ= f! , (       d  f       U$ = f)uB   获取所有后端服务健康状态（并发检查，带缓存）r   Nr   )ThreadPoolExecutoras_completedr   )max_workersF)rL   _health_cache_lock_health_cache_time_health_cachecopyconcurrent.futuresr   r   BACKEND_CHECKSr$   submitr   r   r*   )r6   r   r   r   executorr   r   r   r   future_to_namefutures              r.   get_all_backend_healthr   B  s"    ))+C	##a' %%' 
	' 
 FC		*h 1?0D0D0F
0F,,sG OO0$VMtS0F 	 
 #>2F!)D%%}} 3 
+ 
  
 M- 
	
  %$% 
+	* 
	 MsY   !C* D:(C;"D7D	DD)*
C8;DDDDD
D&)
D8c                   
  ^  \ rS rSrU 4S jrU 4S jrS rS rS rS r	S r
S	 rS
 rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS r S r!S  r"S(S! jr#S" r$S# r%S$ r&S% r'S& r(S'r)U =r*$ ))ProxyHandleri`  c                 2   > [         TU ]  " US[        0UD6  g )N	directory)super__init__BASE_DIRselfargskwargs	__class__s      r.   r   ProxyHandler.__init__b  s    $=(=f=rB   c                   > U R                  U R                  5      n[        R                  R                  U5      (       a$  [        R                  R	                  U5      (       a  [
        TU ]  5       $  [        R                  nU R                  U5      n[        US5       nU R                  S5        U R                  SU5        U R                  S[        U" [        R                  " U[        R                  5      5      R                  5      5        U R                  SS5        U R                  SS5        U R                  S	S
5        U R                  5         U R!                  5         U R"                  R%                  UR'                  5       5        SSS5        g! , (       d  f       g= f! [(         a    [
        TU ]  5       s $ f = f)uU   静态文件服务，添加 Cache-Control: no-cache 确保浏览器加载最新版本rb   zContent-typeContent-LengthCache-Control#no-cache, no-store, must-revalidatePragmazno-cacheExpires0N)translate_pathr3   r2   r4   isdirr   do_GETfstat
guess_typer&   send_responsesend_headerstrO_RDONLYst_size_send_cors_headersend_headerswfilewritereadr*   )r   r3   fsctyper-   r   s        r.   _serve_static_fileProxyHandler._serve_static_filee  sI   ""499-ww~~d##rww}}T':':7>##	$BOOD)EdD!Q""3'  7  !13r"''$:T7U7]7]3^_  2WX  :6  C0'')  "

  * "!!  	$7>##	$s1   4-F, !C1FF, 
F)%F, )F, ,GGc                   ^  U R                   R                  S5      (       d  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R	                  5       $ U R                   S:X  a  U R                  5       $ U R                   R                  S5      (       a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   R                  S5      (       a  U R                  5       $ U R                   R                  S	5      (       a  U R                  5       $ U R                   R                  S
5      (       a  U R                   R                  S5      S   mTS;   a  U R                  S5        g [        U4S jS 5       5      (       a  U R                  S5        g U R                  5       nU(       d  g U R                  SUS9  g U R                  S5        g ! [         Ga4  nSS KnSS KnUR%                  5       n UR                   R'                  [(        S5      n[+        USSS9 nUR-                  US-   S-   S-   5        S S S 5        O!! , (       d  f       O= f! [         a     Of = f U R/                  S5        U R1                  5         U R3                  SS5        U R5                  5         U R6                  R-                  [8        R:                  " [=        U5      US.5      R?                  5       5         S nAg ! [         a      S nAg f = fS nAff = f)N/api//api/health/api/start-servicesz/api/auth/checkz/api/recordsz/api/knowledge/listz/api/users/list/api/voice/file/z/api/comfyui/statusr   ?r   )z/api/digital-human/v2/statusz/api/digital-human/statusr   c              3   ,   >#    U  H	  oT;   v   M     g 7fr?   r@   ).0x
clean_paths     r.   	<genexpr>&ProxyHandler.do_GET.<locals>.<genexpr>  s     ^0]1J0]   )z
/v2/video/z/v2/voice/audio/z/v2/bg/userzproxy_exception.logar   r   
z2==================================================ru   Content-Typeapplication/json; charset=utf-8)r   	traceback) r3   
startswithr   _handle_health_handle_start_services_handle_auth_check_handle_records_get_handle_knowledge_list_handle_users_list_handle_voice_file_handle_comfyui_statussplit_proxy_requestany_require_authr*   r  r2   
format_excrd   r   r&   r   r   r   r   r   r   r(   dumpsr   rP   )	r   r  _er  r2   err_msglog_path_fr   s	           @r.   r   ProxyHandler.do_GETz  s   7	99''00..00yyM)**,,yy112244yy--..00yy##N33//11yy112244yy--..00yy##$677..00yy##$9::2244yy##$899!YY__S1!4
!^^''.^0]^^^''.))+##E#5##E* 	 **,G77<<2GH(C':bHHWt^f4t;< ;:: ""3''')  1RS  "

  c"gG,T!U!\!\!^__ 	s   /H  H  H  3H  /H  H  #H  /H  3/H  #AH  9+H  %H  =H  H   M+M+J$/J
J$M
J!	J$ M!J$$
J1.M0J11M5BM
MMMMMc                 8   U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R	                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S	:X  a  U R                  5       $ U R                   S
:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   S:X  a  U R                  5       $ U R                   R                  S5      (       a)  U R!                  5       nU(       d  g U R#                  SUS9  g U R#                  S5        g )Nr   /api/auth/loginz/api/auth/logoutz/api/auth/change-pwdz/api/records/savez/api/users/addz/api/iopaint/remove_watermarkz/api/iopaint/detect_and_inpaintz/api/iopaint/inpaint_maskz/api/iopaint/brush_inpaintz/api/iopaint/remove_backgroundz/api/voice/savez/api/knowledge/uploadz/api/knowledge/generater   POSTr   )r3   r	  _handle_login_handle_logout_handle_change_password_handle_records_save_handle_users_add _handle_iopaint_remove_watermark"_handle_iopaint_detect_and_inpaint_handle_iopaint_inpaint_mask_handle_iopaint_brush_inpaint!_handle_iopaint_remove_background_handle_voice_save_handle_knowledge_upload_handle_knowledge_generater  r  r  r   r  s     r.   do_POSTProxyHandler.do_POST  s   99--..0099))%%''99**&&((99..//1199++,,..99(())++ 997788::9999::<<9933446699445577998899;;99))**,, 99//002299112244 99 455%%'DT2'rB   c                 8   U R                   R                  S5      (       a  U R                  5       $ U R                   R                  S5      (       a  U R                  5       $ U R                   R                  S5      (       a  U R	                  5       $ U R                   R                  S5      (       a(  U R                  5       nU(       d  g U R                  SUS9$ U R                   R                  S5      (       a  U R                  S5      $ U R                  S5        g )	Nz/api/records/deletez/api/knowledge/deletez/api/users/deleter   DELETEr   r   i  )r3   r  _handle_records_delete_handle_knowledge_delete_handle_users_deleter  r  
send_errorr+  s     r.   	do_DELETEProxyHandler.do_DELETE  s    99 566..00 99 788002299 344,,.. 99 455%%'D&&xd&;;YY!!'**&&x00rB   c                 f    U R                  S5        U R                  5         U R                  5         g)u   CORS 预检请求r   N)r   r   r   r   s    r.   
do_OPTIONSProxyHandler.do_OPTIONS   s(    3!rB   c                    U R                   R                  SS5      nUR                  S5      (       a  USS n[        U5      nU(       a  U$ SSKnUR
                  R                  U R                  5      nUR
                  R                  UR                  5      nUR                  SS/5      S   nU(       a  [        U5      $ g)u   从请求头提取并验证 token，返回用户信息或 None。
支持两种认证方式（Header 优先）：
1. Authorization: Bearer <token> 请求头
2. ?token=<token> URL 查询参数（用于 <video>/<audio> 等无法自定义请求头的元素）
Authorization Bearer    Nr   rX   )
headersr[   r  r\   r   parser   r3   parse_qsquery)r   auth_headerrX   r  r   r   qsquery_tokens           r.   _get_auth_userProxyHandler._get_auth_user  s     ll&&;!!),,OE!%(D 	&&tyy1\\""6<<0ffWtf-a0!+..rB   c                 8   U R                  5       nU(       d  U R                  S5        U R                  5         U R                  SS5        U R	                  5         U R
                  R                  [        R                  " SS0SS9R                  5       5        gU$ )	u"   要求认证，未认证返回 401  r  r  r   u-   未登录或登录已过期，请重新登录Fr    N)
rF  r   r   r   r   r   r   r(   r  rP   r+  s     r.   r  ProxyHandler._require_auth  s    ""$s###%^-NOJJTZZH)!##)68- rB   c                 n   [        U R                  R                  SS5      5      nUS:  a  U R                  R	                  U5      OSn [
        R                  " U5      nUR                  SS5      R                  5       nUR                  SS5      nU(       a  U(       d  U R                  SSS05        g
[        XE5      nU(       d  U R                  SSS05        g
[        US   US   US   5      nU R                  SSUUS.5        g
! [
        R                   a    U R                  SSS	05         g
f = f)zPOST /api/auth/loginr   r      {}r   r<  rA     r      请求格式错误N   用户名和密码不能为空rI  u   用户名或密码错误r   r   r   T)successrX   r  )intr?  r[   rfiler   r(   loadsstriprh   _json_responser=   rY   )r   content_lengthbodyr   r   rA   r  rX   s           r.   r  ProxyHandler._handle_login.  s%   T\\--.>BC2@12Dtzz~.%	::d#Dxx
B/557Hxx
B/H
 xg/O%PQ8.g/I%JKtJ/n1EtF|TC"
 	 ## 	g/C%DE	s   
AD	 	(D43D4c                     U R                   R                  SS5      nUR                  S5      (       a  [        USS 5        U R	                  SSSS	.5        g)
zPOST /api/auth/logoutr;  r<  r=  r>  Nr   Tu	   已登出rP  message)r?  r[   r  r_   rU  )r   rC  s     r.   r  ProxyHandler._handle_logoutK  sL    ll&&;!!),,[_-CTk!JKrB   c                    U R                  5       nU(       d  g[        U R                  R                  SS5      5      nUS:  a  U R                  R                  U5      OSn [        R                  " U5      nUR                  SS5      nUR                  SS5      nU(       a  U(       d  U R                  SS	S05        g[        U5      S:  a  U R                  SS	S05        gSSKJnJnJn	  U" US   5      n
U
(       a  U" US   U5      (       d  U R                  SS	S05        gU	" US   U5      (       a  U R                  SSSS.5        gU R                  SS	S05        g! [        R                   a    U R                  SS	S
05         gf = f)zPOST /api/auth/change-pwdNr   r   rL  old_passwordr<  new_passwordrM  r   rN  u!   旧密码和新密码不能为空   u   新密码长度至少4位)r=   	find_userreset_passwordr   rI  u   旧密码错误r   Tu   密码修改成功rZ  ru   u   密码修改失败)r  rQ  r?  r[   rR  r   r(   rS  rh   rU  r9   user_managerr=   ra  rb  )r   r  rV  rW  r   r^  r_  r=   ra  rb  	full_users              r.   r   $ProxyHandler._handle_change_passwordR  s{   !!#T\\--.>BC2@12Dtzz~.%	::d#D88NB7L88NB7L
 <g/R%ST|q g/J%KL 	HGd:./	D,<l K Kg/@%AB $z*L99BV%WXg/C%DE/ ## 	g/C%DE	s   ":E! !(FFc           	      Z   U R                  5       nU(       d  gUR                  S5      S:w  a  U R                  SSS05        gSSKJn  U" 5       n/ nU H6  nUR                  US	   US
   UR                  SS5      US   US   S.5        M8     U R                  SU[        U5      S.5        g)u$   GET /api/users/list — 仅管理员Nr   admin  r   u!   仅管理员可查看用户列表r   )get_all_usersr   r   phoner<  rt   )r   r   rj  r   rt   r   )usersr   )r  r[   rU  rc  ri  appendr9   )r   r  ri  rk  
safe_usersus         r.   r  ProxyHandler._handle_users_listx  s    !!#88Fw&g/R%ST.
AjM !. 1w+&	o   	C:J!PQrB   c                    U R                  5       nU(       d  gUR                  S5      S:w  a  U R                  SSS05        g[        U R                  R                  SS5      5      nUS:  a  U R
                  R                  U5      OS	n [        R                  " U5      nUR                  S
S5      R                  5       nUR                  SS5      nUR                  SU5      nUR                  SS5      nUR                  SS5      n	U(       a  U(       d  U R                  SSS05        g[        U5      S:  a  U R                  SSS05        gSSKJn
Jn  U
" U5      (       a  U R                  SSSU S305        gU" XVXxU	5      (       a  U R                  SSSU S3S.5        gU R                  SSS05        g! [        R                   a    U R                  SSS05         gf = f)u$   POST /api/users/add — 仅管理员Nr   rg  rh  r   u   仅管理员可添加用户r   r   rL  r   r<  rA   r   rj  r  rM  rN  rO  r`  u   密码长度至少4位)ra  add_useri     用户 'u   ' 已存在   Tu   ' 添加成功rZ  ru   u   添加用户失败)r  r[   rU  rQ  r?  rR  r   r(   rS  rT  rh   r9   rc  ra  rq  )r   r  rV  rW  r   r   rA   r   rj  r   ra  rq  s               r.   r"  ProxyHandler._handle_users_add  s   !!#88Fw&g/L%MNT\\--.>BC2@12Dtzz~.%		::d#Dxx
B/557Hxx
B/H88NH=LHHWb)E88FF+D
 xg/O%PQx=1g/G%HI4Xg(;/O%PQHTBBHXJVdBe%fgg/C%DE) ## 	g/C%DE	s   A>F= =(G('G(c                    U R                  5       nU(       d  gUR                  S5      S:w  a  U R                  SSS05        gSSKJnJn  U" U R                  5      nU" UR                  5      nUR                  S	S/5      S   nU(       d  U R                  S
SS05        gXaS	   :X  a  U R                  S
SS05        gSSKJ	n  U" U5      (       a  U R                  SSSU S3S.5        gU R                  SSSU S305        g)u6   DELETE /api/users/delete?username=xxx — 仅管理员Nr   rg  rh  r   u   仅管理员可删除用户r   r   rA  r   rM  u   缺少 username 参数u   不能删除自己)delete_userr   Trr  u   ' 已删除rZ    u   ' 不存在或删除失败)
r  r[   rU  r   r   rA  r3   rB  rc  rw  )r   r  r   rA  r   paramstargetrw  s           r.   r2  !ProxyHandler._handle_users_delete  s   !!#88Fw&g/L%MN3$))$&,,'J/2g/G%HI*%%g/C%DE,vHVHT_B`%abg&A[/\%]^rB   c                     U R                  5       nU(       a  U R                  SSUS.5        gU R                  SSS05        g)zGET /api/auth/checkr   T)authenticatedr  r}  FN)rF  rU  r+  s     r.   r
  ProxyHandler._handle_auth_check  s=    ""$tT%JKou%=>rB   c                 |   U R                  5       nU(       d  gSSKJnJn  U" U R                  5      nU" UR
                  5      nUR                  S:X  ar  UR                  SS/5      S   nU(       d  U R                  SSS05        g[        US	   U5      nU(       d  U R                  S
SS05        gU R                  SSU05        gUR                  SS/5      S   n[        UR                  SS/5      S   5      n	[        UR                  SS/5      S   5      n
[        US	   XU
5      nU R                  SU5        g)u(   GET /api/records 和 /api/records/detailNr   rv  z/api/records/detailrs   rM  r      缺少 id 参数r   rx     记录不存在r   rp   r~   chatr{   1r|   20)r  r   r   rA  r3   rB  r[   rU  r   rQ  r   )r   r  r   rA  r   ry  r   rp   rz   r{   r|   r   s               r.   r   ProxyHandler._handle_records_get  s0   !!#3$))$&,,' ;;//

4$03I##C'3E)FG%d:&6	BF##C'3D)EFh%78 jj&2156::fse,Q/0

;7:;	$T*%5{)TC(rB   c                    U R                  5       nU(       d  g[        U R                  R                  SS5      5      nUS:  a  U R                  R                  U5      OSn [        R                  " U5      nUR                  SS	5      nUS
;  a  U R                  SSS05        gUUR                  SS5      SS UR                  SS5      UR                  SS5      SS UR                  S0 5      S.n[        US   U5      nU R                  SSUS.5        g! [        R                   a    U R                  SSS05         gf = f)zPOST /api/records/saveNr   r   rL  rM  r   rN  r~   r  )r  	knowledgecopywritingscriptimagevoicecontentu   无效的记录类型titler<  d   r  previewr   metadata)r~   r  r  r  r  r   rs  T)rP  rp   )r  rQ  r?  r[   rR  r   r(   rS  rh   rU  rx   )r   r  rV  rW  r   rz   rp   saveds           r.   r!  !ProxyHandler._handle_records_save   s>   !!#T\\--.>BC2@12Dtzz~.%	::d#D
 hhvv.iig/F%GH  XXgr*4C0xx	2.xx	2.t4R0
 4
+V4CTU!CD% ## 	g/C%DE	s   "D (EEc                 l   U R                  5       nU(       d  gSSKJnJn  U" U R                  5      nU" UR
                  5      nUR                  SS/5      S   nU(       d  U R                  SSS05        g[        US   U5      (       a  U R                  S	S
SS.5        gU R                  SSS05        g)z!DELETE /api/records/delete?id=xxxNr   rv  rs   rM  r   r  r   r   Tu	   已删除rZ  rx  r  )	r  r   r   rA  r3   rB  r[   rU  r   )r   r  r   rA  r   ry  r   s          r.   r0  #ProxyHandler._handle_records_delete  s    !!#3$))$&,,'JJtdV,Q/	g/A%BCj)955+%NOg/@%ABrB   c                 &   U R                  U5        U R                  5         U R                  SS5        U R                  SS5        U R                  5         U R                  R                  [        R                  " USS9R                  5       5        g)u   发送 JSON 响应r  r  r   r   Fr    N)	r   r   r   r   r   r   r(   r  rP   )r   r   r   s      r.   rU  ProxyHandler._json_response5  sp    6"!)JK*OP

Du=DDFGrB   c           
         [        5       n[        UR                  5       5      nU R                  U(       a  SOS5        U R	                  5         U R                  SS5        U R                  5         U R                  R                  [        R                  " U(       a  SOSS[        [        R                  " 5       U R                  R                  -
  S5      US	.S
S9R                  5       5        g)u   健康检查 APIr      r  r  healthydegradedrunningr   )r   proxyuptimebackendsFr    N)r   allvaluesr   r   r   r   r   r   r(   r  roundrL   server
start_timerP   )r   r  all_healthys      r.   r  ProxyHandler._handle_health>  s    )+(//+,+337!)JK

#.iJDIIK$++*@*@@!D 	%

 
  &vx	)rB   c                 0   SSK n[        R                  R                  [        S5      n[        R                  R                  [        S5      n[        R                  R                  U5      (       aU  Sn UR                  " SSSS	XB/[        UR                  UR                  UR                  S
9  U R                  SSSSS.5        g[        R                  R                  U5      (       aS   UR                  " SSSSU/[        UR                  UR                  UR                  S
9  U R                  SSSSS.5        gU R                  SSSS.5        g! [         a     Nf = f! [         a(  nU R                  SS[        U5      S.5         SnAgSnAff = f)u   一键启动所有后端服务r   Nzservice_guardian.pyzstart_all_services.batz%C:\Program Files\Python311\python.execmdz/cr   zService-Guardian)cwdstdoutr   creationflagsr   Tu<   服务守护进程已启动！服务崩溃后将自动重启u/   请等待30-60秒后刷新状态查看各服务)rP  r[  hintzService-LauncheruB   启动脚本已执行，请查看弹出的命令窗口了解进度u;   启动完成后请点击"刷新状态"查看各服务状态ru   F)rP  r   uB   启动脚本不存在，请通过桌面"启动工作台.bat"启动)
subprocessr2   r3   rd   r   r4   PopenDEVNULLCREATE_NEW_PROCESS_GROUPrU  r*   r   )r   r  guardian_pathbat_path
python_exer;   s         r.   r	  #ProxyHandler._handle_start_servicesN  s   X/DE77<<*BC77>>-((AJ  D'+=zY %--%--","E"E ##C#]M* 
  77>>(##  D'+=xH %--%--","E"E ##C#cY* 
 
 	CY"
 	-  $  ##CUSV)LMs,   4AE +AE# 
E E #
F-FFc                 Z
   [         R                  R                  U R                  5      n[         R                  R	                  UR
                  5      nUR                  SS/5      S   nU(       d  U R                  SSS05        gSn [         R                  R                  U SU 35      n[         R                  R                  US	S
9 n[        R                  " UR                  5       5      nSSS5        W(       GaO  X7;   GaI  Xs   nUR                  S0 5      n	UR                  S0 5      n
U
R                  S5      S:X  aG  U R                  SSUU
R                  S5      (       a  U
R                  SS//5      S   S   OSS.5        g/ nU	R                  5        H  u  pUR                  S5      (       d  M  US    Hr  nUS   nUR                  SS5      nS[         R                  R                  U5       S[         R                  R                  U5       S3nUR!                  UUUS.5        Mt     M     U R                  SSUUS.5        g[         R                  R                  U S35      n[         R                  R                  US	S
9 n[        R                  " UR                  5       5      nSSS5        WR                  S/ 5      nUR                  S / 5      nU GH  n[#        U5      S!:  d  M  US"   U:X  d  M   [#        U5      S#:  a  [#        US"   5      OSn [         R                  R                  U S$35      n[         R                  R                  US	S
9 n[        R                  " UR                  5       5      nSSS5        WR                  S%0 5      nUR                  S&S5      nSnUS:  a  ['        S	[)        S'S(US	-  -
  5      5      nU R                  SS)UUSS*.5          g   [+        U5       HO  u  nn[#        U5      S!:  d  M  US"   U:X  d  M"  U R                  SS+UUS"-   [#        U5      [#        U5      S,.5          g   U R                  SS-US.S.5        g! , (       d  f       GN~= f! , (       d  f       GN= f! , (       d  f       GN= f! [$         a    Sn Nf = f! [         R,                  R.                   a-  nU R                  S/SUS0UR0                   3S.5         SnAgSnAf[$         a)  nU R                  S1SU[3        U5      S.5         SnAgSnAff = f)2u  GET /api/comfyui/status?prompt_id=xxx
实时查询 ComfyUI 中任务的状态：
- queued: 排队中
- running: 执行中，返回进度百分比
- completed: 已完成，返回输出图片URL
- error: 执行失败
- not_found: prompt_id 不存在（已完成并被清理）
	prompt_idNr   rM  r   u   缺少 prompt_id 参数r   z	/history/r   r   outputsr   
status_strr   messagesu   执行失败u   未知错误)r   r  r[  imagesfilename	subfolderr<  z/api/comfyui/view?filename=z&subfolder=z&type=output)r   r  node_id	completed)r   r  
image_urlsz/queuequeue_runningqueue_pendingr	   ry   r
   z/prompt	exec_infoqueue_remainingZ   r  r  )r   r  progressqueue_positionqueued)r   r  r  total_pendingrunning_count	not_foundu5   任务已完成或 prompt_id 无效，请检查结果  u   无法连接 ComfyUI: ru   )r   r@  r   r3   rA  rB  r[   rU  r   r   r   r(   rS  r   r$   quoterl  r9   r*   r   min	enumerater   r   reasonr   ) r   r   ry  r  comfyui_basehist_reqr   	hist_data
hist_entryr  status_datar  r  outputimgr  r  img_url	queue_req
queue_datar  pendingitem
node_count
prompt_reqp_respprompt_datar  r  r  idxr;   s                                    r.   r  #ProxyHandler._handle_comfyui_status  s    &&tyy1&&v||4JJ{TF3A6	g/H%IJ.n	~~--i	{.STH''!'< JJtyy{3	 = Y3&1
$..B7(nnXr: ??<0G;''")%.]h]l]lmw]x]x;??:@P?Q#RSU#VWX#Y  M. 
   
'.}}OGzz(++#)(#3C'*:H(+R(@I(CFLLDVDVW_D`Caalmsmymymm  AJ  nK  mL  LX  'YG&--'.,4+2/ 	 $4 (7 ##C)!*",* 
  ..,v/FGI''	1'=!ZZ		4
 > !nn_b9G nn_b9G  t9>d1g&:14TaT!WQJ,%+^^%;%;|nG<T%U
#^^33J3Jf*.**V[[]*CK K$/OOK$D	*3--8I1*M  "H&*#&q#b#!8K2K*L#M''"+%.$,*+	.  9  > 'w/	Tt9>d1g&:''"*%.*-'),W),W.   0 %&R& u =<P >= KJ % ,*+,J ||$$ 	!&3AHH:>&  
  	!&q6&  	s   AR. 
%Q&/BR. 	+R. 8BR. A R. %Q83?R. 6R. R. !A R!%R
,R2<R. / R. R. .R. R. &
Q50R. 8
RR. 

R	RR+(R. *R++R. .T*#S44T*T%%T*c                    U R                   R                  SS5      nSU;  a  g[        U R                   R                  SS5      5      nUS:X  a  gU R                  R	                  U5      nSSKnSnUR                  S5       H?  nUR                  5       nUR                  S	5      (       d  M+  US
S R                  S5      n  O   U(       d  gUR                  S5      nUR                  SU-   5      n0 n	0 n
U H  nU(       a  US:X  d  US:X  a  M  UR                  S5      nUS:X  a  M1  USU R                  SSS9nXkS-   S nUR                  S5      (       a  USS n[        R                  " SU5      nU(       d  M  UR                  S5      nUR                  S5      nU(       a  UU4X'   M  UR                  SSS9X'   M     X4$ )u=   解析 multipart/form-data 请求体，返回 (fields, files)r  r<  zmultipart/form-dataNNr   r   N;z	boundary=	   "r   s   --s   --
s   

r  ignore)errorsr`  s   
zPContent-Disposition:\s*form-data;\s*name="([^"]*)"(?:\s*;\s*filename="([^"]*)")?ry   r	   )r?  r[   rQ  rR  r   email.parserr  rT  r  rP   finddecodeendswithresearchgroup)r   content_typerV  rW  emailboundarypartboundary_bytespartsfieldsfiles
header_endheaders_rawr  
disp_match
field_namer  s                    r.   _parse_multipart_form"ProxyHandler._parse_multipart_form  s   ||''; 4T\\--.>BCQzz~. 	 &&s+D::<D{++8>>#.	 , !1

5>12D45=DI,= ;/JR{
+22782LK>?+G((!#2, #v  yD  EJ#))!,J!''*H%-w$7!%,^^GH^%M"5 8 }rB   c                 $    U R                  5         g)u,   去水印 - 直接使用智能检测+inpaintN)r$  r7  s    r.   r#  -ProxyHandler._handle_iopaint_remove_watermarkC  s    //1rB   c                 >
  ^ ^^^^^^ ^!^"^#^$ SSK mSSKJmJm  SSKm"SSKm#SSKn T R                  5       u  p#U(       a  SU;  a  T R                  SSS05        gUS   u  pEUR                  SS	5      R                  5       nTR                  T"R                  U5      5      m!T!R                  S
:w  a  T!R                  S
5      m!T!R                  u  pxSJUU!U"U#4S jjmU 4S jm$UUUU!U U$4S jm UUU U!U$4S jn	U(       d  [!        SU SU S3SS9  U	" 5         g[!        SUSS  S3SS9  TR#                  U5      R%                  5       n
SU SU SU SUS-
   SUS-
   S3n T#R'                  SSUU
/SS S!S".S#.S$S%9nUR(                  S&:w  a   [!        S'UR(                   S(3SS9  U	" 5         gUR                  5       R                  S)S*5      n[!        S+USS&  3SS9  UR                  5       nUR+                  S,5      (       a0  [,        R.                  " S-S	U5      n[,        R.                  " S.S	U5      n[,        R0                  " S/U5      nS0S0mU(       ar   UR3                  UR5                  5       5      m[7        U4S1 jS2 5       5      (       d  S0S0mTR                  S05      (       a  [7        U4S3 jS4 5       5      (       d  S0S0mTR                  S05      (       d  [!        S5SS9  U	" 5         g[=        TS   5      [=        TS6   5      [=        TS7   5      [=        TS8   5      4u  nnnn[=        US9-  5      [=        US9-  5      nn[?        SUU-
  5      n[?        SUU-
  5      n[A        T!RB                  U-
  UUS:-  -   5      n[A        T!RD                  U-
  UUS:-  -   5      n[!        S;U S<U S=U S>U 3SS9  TRG                  S?T!R                  S5      nTRI                  U5      nURK                  UUUU-   UU-   /S@SA9  T" U5      nT$" U5      (       a  [!        SBSS9  g[!        SCSS9  T " 5         g! UR8                  [:        4 a    S0S0m GNf = f! T#RL                  RN                   a    [!        SDSS9  U	" 5          g[P         a3  n[!        SEU SF3SS9  SSK)nURU                  5         U	" 5          SnAgSnAff = f! [P         aW  n[!        SGU 3SS9  SSK)nURU                  5          T R                  SHSSI[W        U5       305         SnAg!     SnAg= fSnAff = f)Ku:  v8.0: AI智能去水印 - 自动识别或根据描述精准去除

策略:
1. 无描述词: 使用cv2图像处理自动识别水印区域（快速<1s，不依赖Ollama）
2. 有描述词: 使用Ollama视觉模型精准识别（30s超时，失败回退到自动检测）
3. 最终回退: 四角+边缘区域inpaint
r   N)Image	ImageDrawr  rM  r      未上传图片descriptionr<  RGBc                 f  > TR                  5       nTR                  USS9  TR                  UR                  5       5      R	                  S5      nTR                  5       nU R                  USS9  TR                  UR                  5       5      R	                  S5      nT	R                  SX5SUS.SS9$ )	u@   调用IOPaint inpaint API（使用更小的radius减少涂抹）PNGformatr   $http://127.0.0.1:8080/api/v1/inpaint
INPAINT_NSr  maskcv2_flag
cv2_radiusx   r(   r   )BytesIOsave	b64encodegetvaluer  post)

mask_imageradiusbufimg_b64mask_bufmask_b64base64r  iorequestss
         r.   
do_inpaintCProxyHandler._handle_iopaint_detect_and_inpaint.<locals>.do_inpaintf  s    jjlU+ **3<<>:AA'J::<7!++H,=,=,?@GGP}}:#*,flm %  rB   c                   > U R                   S:X  a  U R                  nTR                  S5        TR                  5         TR	                  SS5        TR	                  S[        U5      5        TR                  5         TR                  R                  U5        gU R                  (       a  U R                  SS O[        U R                   5      n[        SU R                    SU 3SS	9  g
)u   发送inpaint结果r   r  	image/pngr   TNu   [IOPaint] inpaint失败(): flushF)status_coder  r   r   r   r9   r   r   r   textr   r8   )r   
result_img	error_msgr   s      r.   send_resultDProxyHandler._handle_iopaint_detect_and_inpaint.<locals>.send_resultt  s    ##s*!%J&&s+++-$$^[A$$%5s:G$$&JJ$$Z03799		$3#dFVFVBWI4T5E5E4Fc)U]ab rB   c                    > TR                   u  pT	R                  STR                   S5      nT
R                  U5      n[        U S-  5      n[        US-  5      n[        US-  5      n[        US-  5      nUR	                  SSXE/SS9  UR	                  X-
  SX/SS9  UR	                  SX-
  XA/SS9  UR	                  X-
  X-
  X/SS9  UR	                  SX-
  X/SS9  UR	                  SSX/SS9  [        SU  S	U S
3SS9  T" U5      nT" U5      (       a  [        SSS9  gTR                  SSSUR                   305        g)u*   保守回退 - 处理边缘窄条带区域Lr   g
ףp=
?皙?{Gz?   fillu    [Fallback] 边缘条带inpaint (r   r   Tr&  u   [Fallback] 去水印完成ru   r   u   IOPaint去水印失败: N)sizerN   DrawrQ  	rectangler8   rU  r(  )r   hr  	mask_drawcorner_wcorner_hbottom_htop_hr   r  r  r!  r  r   r,  s            r.   fallback_inpaintIProxyHandler._handle_iopaint_detect_and_inpaint.<locals>.fallback_inpaint  sk   xxyychh2%NN40	q4x=q4x=q4x=AH##Q8$>S#I##Q\1a$B#M##Qh$B#M##Q\1<$FS#Q##Qa$;##F##Q1$43#?81QCqAN!$'t$$6tD''g9QRVRbRbQc7d-efrB   c                    >  SSK n SSKnUR                  T*5      nU R                  X R                  5      nUR
                  u  pEU R                  USS5      nU R                  U R                  S5      nU R                  XgSS9nUR                  U5      n	SS[        US	-  5      SUS
4S[        US-  5      USUS
4SSUS[        US-  5      S
4SSU[        US-  5      US
4/n
U
 H}  u  ppnnXU2X24   nUR                  S:X  a  M#  UR                  US:  5      UR                  -  nUU:  d  ML  U R                  XU2X24   U5      XU2X24'   [        SU SUS 3SS9  M     [        US-  5      n[        US-  5      nSSUU4UU-
  SUU4SUU-
  UU4UU-
  UU-
  XT4/nU H  u  nnnnUUU2UU24   nUR                  S:X  a  M%  UR                  US:  5      UR                  -  nUS:  d  MN  U R                  U	UU2UU24   U5      U	UU2UU24'   [        SU SU SU SU SUS 3
SS9  M     U R!                  U	SS9u  nnnnUR                  U	5      nSn[        XE-  S-  5      n [#        S U5       H/  n!UU!U R$                  4   n"UU"s=::  a  U ::  d  M#  O  M'  S!UUU!:H  '   M1     U R                  UUSS9nUR                  US:  5      S:  aK  T'R'                  US"S#9n#[        S$SS9  T(" U#S%S&9n$T+" U$5      (       a  [        S'SS9  g[        S(SS9  T)" 5         g[        S)SS9  T)" 5         g! [(         a3  n%[        S*U% S+3SS9  SSKn&U&R-                  5         T)" 5          Sn%A%gSn%A%ff = f),ue   自动检测水印区域 - 增强版：更精准的边缘检测 + 扩大检测范围
                r   N   r  )r  r  r	   
iterationstopgQ?gQ?bottomg?leftg333333?rightg333333?z[Auto] u$   边缘检测到文字/水印 密度=.2%Tr&  皙?gQ?u   [Auto] 角落水印: (,z)-(u	   ) 密度=r   )connectivity   r1  ry   r2  r/  modeu/   [Auto] 检测到水印区域，执行inpaint...r   )r  u   [Auto] 自动去水印完成u   [Auto] inpaint失败，回退u4   [Auto] 未检测到明显水印，使用边缘回退u   [Auto] 自动检测异常: u	   ，回退)cv2numpyarraycvtColorCOLOR_RGB2GRAYshapeCannygetStructuringElement
MORPH_RECTdilate
zeros_likerQ  r5  sum
bitwise_orr8   connectedComponentsWithStatsrangeCC_STAT_AREA	fromarrayr*   r  	print_exc),rO  np	img_arraygrayr8  r   edgeskerneledges_dilated
mask_arrayedge_regionsr   y1y2x1x2	thresholdregiondensityr:  r;  cornerscx1cy1cx2cy2
num_labelslabelsstats	centroidsrefined_maskmin_areamax_areara   arear  r   r;   r  r  r!  r>  r  r,  s,                                          r.   auto_detect_watermarkNProxyHandler._handle_iopaint_detect_and_inpaint.<locals>.auto_detect_watermark  s    V & "I<<	3E3EFD::DA  IIdB4E !66s~~vNF$'JJuJ$KM!#t!4J 3q4x=!Q=!3q4x=!Q4@Aq#a$h-> !QAHq$?	$L <H7""i!."ube|!<!;;!+$"$&&!"4v{{"B"Y.7:~~jTVQVXZX]Q]F^`f7gJ"ube|4!GD61UV]^aUb"ckop <H  #1t8}H"1t8}HAx2Xq!X6AL(A6Xq8|Q:	G /6*S#s!.s3wC/?!@!;;!+$"$&&!"4v{{"B"T>;>>>*UXY\U\^abe^eUeJfhn;oJs3wC'78!$:3%qSQseS\]deh\i"jrvw /6 <?;[;[\fuv;[;w8Jy#%==#<L!H"154<0H"1j1$Q(8(8%89#t7x778;L15 2 $'::lFq:#QLvvlQ./!3%*__\_%L
 OX\])*Q?&t,,!$@N#'!$A$O,.#' T]ab(*#   7s)DDQ$'')$& s9   D*L2 1B>L2 3B%L2 A1L2 L2  L2 2
M/<)M**M/u*   [v8.0] 无描述词，自动检测水印 (r   r   Tr&  u+   [v8.0] 有描述词，Ollama vision识别: <   z...z:You are a watermark detection expert. Look at this image (z pixels) and find: aJ  .

A watermark is any text, logo, icon, pattern, or semi-transparent overlay that was added to the original image. Common locations: bottom-right, bottom-left, top-right, top-left corners, or centered.

Return ONLY a valid JSON object (no markdown, no explanation) with the bounding box in pixel coordinates:
{"found": true, "x": <int>, "y": <int>, "width": <int>, "height": <int>}

If you find multiple watermarks, return the bounding box that covers ALL of them combined.
If absolutely no watermark exists, return: {"found": false}

Coordinates must be within the image boundaries (0-ry   z, 0-z).z#http://127.0.0.1:11434/api/generatezqwen2.5vl:7bFr0     )temperaturenum_predict)modelpromptr  streamoptionsrA  r  r   u   [Detect] Ollama失败(u   )，回退到自动检测responsez{}u   [Detect] Ollama返回: z```z^```(?:json)?\s*\n?z
\n?```\s*$z\{[\s\S]*?\}foundc              3   ,   >#    U  H	  oT;   v   M     g 7fr?   r@   r   kbboxs     r.   r   BProxyHandler._handle_iopaint_detect_and_inpaint.<locals>.<genexpr>/  s     "@i9ir   )r  c              3   ,   >#    U  H	  oT;   v   M     g 7fr?   r@   r  s     r.   r   r  1  s     8jLiqdLir   )r   ywidthheightu3   [Detect] 未识别到水印，回退到自动检测r  r  r  rI  r	   u   [Detect] 水印区域: x=z, y=z, w=z, h=r/  r2  r3  u   [Detect] 精准去水印完成u   [Detect] inpaint失败，回退u2   [Detect] Ollama超时(30s)，回退到自动检测u   [Detect] 检测失败: u   ，回退到自动检测u   [IOPaint] 去水印失败: ru      去水印失败: )r
   ),r  PILr  r  r  r   r(   r  rU  r[   rT  r&   r  rN  convertr5  r8   r  r  r  r(  r  r  subr  rS  r  r  rh   
ValueErrorrQ  r   r  r  r  rN   r6  r7  
exceptionsTimeoutr*   r  r`  r   )%r   json_modr  r  r  
image_datar  img_wimg_hr}  
img_base64vision_promptvision_respvision_resultcleaned
json_matchr   r  r   r8  pad_wpad_hr  r9  r   r;   r  r  r  r  r  r!  r>  r  r  r   r,  s%   `                          @@@@@@@@@@r.   r$  /ProxyHandler._handle_iopaint_detect_and_inpaintG  sS    	(L	 668MFG50##C'3D)EF#(> H **]B7==?K**RZZ
34Cxx5 kk%(88LE ! g g.Y  Y | B5'5'QRS[_`%' ?CR@P?QQTU]ab))*5<<>J Z[`Zaabchbii|  ~I  }J 
J4 5:!G9Dq	
OMI(&mm9!/"/#-,"'36s#K  , 
 **c12;3J3J2KKdemqr)+ + 0 0 2 6 6z4 H/ds0C/DETR (--/%%e,, ff%;RIG ff]B@GYY@
'0'~~j.>.>.@A""@i"@@@$+U#3D88G,,S8jLi8j5j5j$+U#3D xx((OX\])+ c^Sc^Sg=OQTUYZbUcQdd
1a"1s7|SS\u1u9%1u9%		Aq519}5

QEAI61!D4s$qcJRVWyychh2%NN40	##Q1q5!a%$8s#C!$'t$$:4H;DI$&9 %44jA 0 '/0< &&.. (JSWX%' (/s2JKSWX ##%%''	(  	/s34@!##C'5Fs1vh3O)PQQ	s   3R; B;R; AR; A
Q )B(Q A1P. 'Q +D0Q Q .QQ QQ +R8:R; <	R8)R3.R; 3R88R; ;
T!T' TTTTTc           	          U R                  5       u  pU(       a  SU;  a  U R                  SSS05        gUS   n[        U[        5      (       a  Uu  pCSnSU;   a  US   n[        U[        5      (       a  Uu  pEUc=  UR	                  SS5      n[        U[        5      (       a  [        U5      S:  a  US   OSnS	SKnS	S
KJn  S	SK	nS	SK
n	UR                  UR                  U5      5      n
U
R                  S:w  a  U
R                  S5      n
UR                  5       nU
R                  USS9  UR!                  UR#                  5       5      R%                  S5      nU(       a  UR                  UR                  U5      5      nUR                  S:w  a  UR                  S5      nUR&                  U
R&                  :w  a&  UR)                  U
R&                  UR*                  5      nUR                  5       nUR                  USS9  UR!                  UR#                  5       5      R%                  S5      nOkUR-                  SU
R&                  S5      nUR                  5       nUR                  USS9  UR!                  UR#                  5       5      R%                  S5      nUUSSS.nU	R/                  SUSS9nUR0                  S:X  a  UR2                  nU R5                  S5        U R7                  5         U R9                  SS5        U R9                  S[        U5      5        U R;                  5         U R<                  R?                  U5        g[A        SUR0                   SURB                  SS  3SS9  U R                  SSS UR0                   305        g! [D         a7  n[A        S!U 3SS9  U R                  SSS"[G        U5       305         SnAgSnAff = f)#u   局部修复 - 接受图片和用户圈选的Mask进行精准去水印
前端传递: image (原始图) + mask (用户绘制的修复区域，白色=修复，黑色=保留)
r  rM  r   r  Nr  r  ry   r   )r  r  r  r	  r   r/  r2  r  r
   r  r  r  r  r   r  r$  r   u   [IOPaint] 局部Inpaint 失败(r%  Tr&  ru   u   IOPaint 局部修复失败: u   [IOPaint] 局部修复失败: u   局部修复失败: )$r  rU  
isinstancetupler[   r9   r  r  r  r  r   r&   r  rN  r  r  r  r  r  r5  resizeNEARESTrN   r  r(  r  r   r   r   r   r   r   r8   r)  r*   r   )r   r  r  r  _	mask_datar  r  r  r   r  r  r  mask_imgr  mask_base64r  inpaint_payloadr   r*  r;   s                        r.   r%  )ProxyHandler._handle_iopaint_inpaint_maskc  ss   S	Q 668MFG50##C'3D)EFwJ*e,, * I!&M	i//#,LA  !IIfl;	i//03I0B	!I! **RZZ
34Cxx5 kk%(**,CHHSH'))#,,.9@@IJ  ::bjj&;<==C''//4H==CHH,'sxxGH::<hu5$..x/@/@/BCJJ7S yychh4::<		(5	1$..x/@/@/BCJJ7S $#(	O ==6$ ! D 3&!\\
""3''')  =  !13z?C  "

  ,78H8H7ITYYW[X[_L]^fjk##C'5QRVRbRbQc3d)ef 	Q21#6dCg1Ec!fX/N%OPP	Qs$   3N LN AN 
O-OOc                 
    U R                  5       u  pU(       a  SU;  a  U R                  SSS05        gUS   n[        U[        5      (       a  Uu  pCSnSU;   a  US   n[        U[        5      (       a  Uu  pEUc  U R                  SSS05        gSSKnSS	KJnJn  SSKn	SSK	n
UR                  U	R                  U5      5      nUR                  S
:w  a  UR                  S
5      nUR                  u  pU	R                  5       nUR                  USS9  UR!                  UR#                  5       5      R%                  S5      nUR                  U	R                  U5      5      nUR                  S:w  a  UR                  S5      nUR                  UR                  :w  a&  UR'                  UR                  UR(                  5      nSSKnUR-                  U5      nUR/                  US:  5      nUX-  -  n[1        SUS SU SU SU S3	SS9  [3        S[5        [7        X5      S-  5      5      nUS:  a   SSKnUR;                  UR<                  US-  S-   US-  S-   45      nUR?                  UUSS9n[3        SU5      nURA                  UUS-  S-   US-  S-   4S5      nURC                  USSURD                  5      u  nnURG                  USS9n[1        SU SU S 3SS9  U	R                  5       nUR                  USS9  UR!                  UR#                  5       5      R%                  S5      nUUS#S$S%.n[1        S&U SU S'US( S3SS9  U
RO                  S)US*S+9nURP                  S,:X  a  URR                  nU RU                  S,5        U RW                  5         U RY                  S-S.5        U RY                  S/[[        U5      5        U R]                  5         U R^                  Ra                  U5        [1        S0SS9  g[1        S1URP                   S2URb                  SS,  3SS9  U R                  S3SS4URP                   305        g! [H         aa    URK                  URM                  US-  S-   5      5      nURK                  URA                  U5      5      n[1        S!US-  S-    S"U S3SS9   GNf = f! [d         aK  n[1        S5U 3SS9  SSK3n U Ri                  5         U R                  S3SS6[k        U5       305         SnAgSnAff = f)7u+  v9.0: 画笔涂抹去水印 - 接受图片+用户涂抹mask，智能修复
处理流程:
1. 接收图片 + 用户涂抹的mask（白色=水印区域）
2. mask膨胀 + 边缘羽化，确保完整覆盖水印
3. 仅在被mask标记的区域执行IOPaint inpaint
4. 图片其余内容100%保持不变
r  rM  r   r  Nr  u   未提供画笔maskr   )r  ImageFilterr  r  r	  r   r/  u   [BrushInpaint] Mask覆盖率: rH  z (zpx / r   r   Tr&  ry   gMbP?r	   rB     r2  rM  u   [BrushInpaint] mask膨胀u   px + 羽化pxz'[BrushInpaint] PIL fallback: MaxFilter(z) + GaussianBlur(INPAINT_TELEAr
   r  u*   [BrushInpaint] 发送IOPaint请求 (原图u   , mask覆盖率z.1%r  r  r  r   r  r$  r   u6   [BrushInpaint] 去水印完成 (仅修复涂抹区域)u   [BrushInpaint] IOPaint失败(r%  ru   u   IOPaint修复失败: u   [BrushInpaint] 失败: r  )6r  rU  r  r  r  r  r  r  r  r   r&   r  rN  r  r5  r  r  r  r  r  r  rP  rQ  rZ  r8   r   rQ  r  rO  rV  MORPH_ELLIPSErX  GaussianBlurrm  THRESH_BINARYr_  ImportErrorfilter	MaxFilterr  r(  r  r   r   r   r9   r   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  mask_arrmask_pixels
mask_ratio	dilate_pxrO  re  dilatedfeather_radiusr  r  r  r   r*  r;   r  s!                                    r.   r&  *ProxyHandler._handle_iopaint_brush_inpaint  s&   t	N 668MFG50##C'3D)EFwJ*e,, *I!&M	i//#,LA ##C'3H)IJ. **RZZ
34Cxx5 kk%(88LE**,CHHSH'))#,,.9@@IJ zz"**Y"78H}}##++C0}}(#??388U]]C xx)H&&A.K$6J2:c2B"[MQVW\V]]^_d^eefgost As3u#4u#<=>IQ~ 66s7H7H9UV;WX=ZcdeZefgZgJhiF!jj6ajHG &)I%6N!..w9I!9K^\]M]^_M_8`bcdG "%wCARAR!SJAw$wSAH5i[NK[[]^fjk zz|HMM(5M1 **8+<+<+>?FFwOK $#+	O >ugQug_]ghk\llmnvz{==6$ ! D 3&!\\
""3''')  =  !13z?C  "

  ,NW[\5d6F6F5Gs499UYVY?J[\dhi##C'5J4K[K[J\3])^_M # ~'{/D/DYq[QR]/STH'{/G/G	/RSHCIaKPQM?Rcdmcnnopx|}	~P  	N+A3/t<!g1B3q6(/K%LMM		NsS   3R4 AR4 F!R4 4B.Q "DR4 =AR4 A'R1-R4 0R11R4 4
T	>ATT	c                     U R                  5       u  pU(       a  SU;  a  U R                  SSS05        gUS   u  p4SSKnSSKnUR	                  U5      R                  S5      nSUS	.nUR                  S
USS9n	U	R                  S:X  a  U	R                  n
O6[        SU	R                   SU	R                  SS  S3SS9   SSKJn  U" USS9n
U R!                  S5        U R#                  5         U R%                  SS5        U R%                  S['        U
5      5        U R)                  5         U R*                  R-                  U
5        g! [         a    U R                  SSS05         g[         a8  nU R                  SSSU	R                   S[        U5       S305         SnAgSnAff = f! [         a7  n[        SU 3SS9  U R                  SSS[        U5       305         SnAgSnAff = f)u9   抠图 - 使用 IOPaint RemoveBG 插件 (briaai/RMBG-1.4)r  rM  r   r  Nr   r   RemoveBG)r   r  z1http://127.0.0.1:8080/api/v1/run_plugin_gen_imager  r  r   u   [IOPaint] RemoveBG插件失败(r%  u   , 回退到本地rembgTr&  )remove)force_return_bytesru   uE   抠图服务不可用：IOPaint RemoveBG插件和rembg库均不可用u   抠图失败（IOPaint: z	, rembg: u   ）r  r$  r   u   [IOPaint] 抠图失败: u   抠图失败: )r  rU  r  r   r  r  r  r(  r  r8   r)  rembgr  r  r*   r   r   r   r   r9   r   r   r   )r   r  r  r  r  r  r   r  plugin_payloadr   result_datar  r   r;   s                 r.   r'  .ProxyHandler._handle_iopaint_remove_background:  s   3	K 668MFG50##C'3D)EF#(> H  ))*5<<WEJ ##N ==C# ! D 3&"ll 78H8H7ITYYW[X[_L]]st  }A  B,"("MK s###%^[9-s;/?@JJ[) # ''g7~-  A  ''g9QRVRbRbQcclmpqsmtluux7y-z{  	K,QC0=gAx/H%IJJ	KsT   3F, B
F, E A9F, F)&F, (	F)1.F$F, $F))F, ,
G-6-G((G-c           	      t   U R                  5       nU(       d  g U R                  5       u  p#U(       a  SU;  a  U R                  SSS05        gUS   u  pESSKn[	        UR                  " 5       5      n[
        R                  R                  U5      S   =(       d    SnUS	    S
U U 3n	[
        R                  R                  [        SUS	   5      n
[
        R                  " U
SS9  [
        R                  R                  X5      n[        US5       nUR                  U5        SSS5        SUS	    SU	 3nU R                  SSUU	[        U5      S.5        [        SU 3SS9  g! , (       d  f       NG= f! [         a7  n[        SU 3SS9  U R                  SSS[!        U5       305         SnAgSnAff = f)u7   POST /api/voice/save - 保存 TTS 生成的音频文件NaudiorM  r   u   未上传音频文件r   ry   .wavr   r  voicesTr   wbr   /r   )rP  r   r  r5  u   [Voice] 保存音频: r&  u   [Voice] 保存音频失败: ru   u   保存音频失败: )r  r  rU  rL   rQ  r2   r3   splitextrd   re   makedirsr&   r   r9   r8   r*   r   )r   r  r  r  r  
audio_datarL   tsextsave_filename	voice_dir	save_pathr-   file_urlr;   s                  r.   r(  ProxyHandler._handle_voice_saveq  s   !!#"	Q 668MFG50##C'3J)KL#(> HTYY[!B''""8,Q/96C#J/0"cU;M Xxj9IJIKK	D1Y>Ii&!
# ' *$z*:);1]OLH)J	&  *9+6dC '&  	Q04DAg1Ec!fX/N%OPP	Qs7   3E6 CE6 E%$A E6 %
E3/E6 6
F7 -F22F7c                     SSK nU R                  R                  S5      n[        U5      S:  a  U R	                  SS5        gUS   nSR                  USS 5      nUR                  R                  U5      nS	U;   d  UR                  S5      (       a  U R	                  S
S5        g[        R                  R                  [        SX45      n[        R                  R                  U5      (       d  U R	                  SS5        gU R                  5       nU(       a  US   U:X  d  UR                  S5      S:X  a  OU R                  S
5        U R                  5         U R!                  SS5        U R#                  5         U R$                  R'                  [(        R*                  " SS0SS9R-                  5       5        g[        R                  R/                  U5      S   R1                  5       nSSSSS.nUR                  US5      n	[3        US5       n
U
R5                  5       nSSS5        U R                  S 5        U R                  5         U R!                  SU	5        U R!                  S![        W5      5        U R!                  S"S#5        U R#                  5         U R$                  R'                  U5        g! , (       d  f       N= f! [6         a2  n[9        S$U 3S%S&9  U R	                  S'[;        U5      5         SnAgSnAff = f)(uG   GET /api/voice/file/{username}/{filename} - 提供保存的音频文件r   Nr     rM  zInvalid pathr`  r   z..rh  	Forbiddenr  rx  zFile not foundr   r   rg  r  zapplication/jsonr   u   无权限访问此文件Fr    ry   z	audio/wavz
audio/mpegz	audio/oggz
audio/flac)r  z.mp3z.oggz.flaczapplication/octet-streamr   r   r   Accept-Rangesbytesu"   [Voice] 提供音频文件失败: Tr&  ru   )r   r3   r  r9   r3  rd   r@  unquoter  r2   re   isfilerF  r[   r   r   r   r   r   r   r(   r  rP   r  lowerr&   r   r*   r8   r   )r   r   
path_partsr   r  	file_pathr  r  
mime_typesr  r-   	file_datar;   s                r.   r  ProxyHandler._handle_voice_file  s   ;	)-J:"^4!!}Hxx
12/H||++H5H x8#6#6s#;#;[1XxLI77>>),,%56 &&(Dj)X5&9IW9T ""3''')  1CD  "

  W6P,Q`e!f!m!m!op ''""8,Q/557C#$#%	J &>>#/IJLi&!FFH	 ' s###%^\:-s9~>_g6JJY' '&  	)6qc:$GOOCQ((	)sM   A K A"K &AK B7K :AK J4 BK 4
K>K 
L(K<<Lc                     U R                  SS5        U R                  SS5        U R                  SS5        U R                  SS5        g )	NzAccess-Control-Allow-Origin*zAccess-Control-Allow-MethodszGET, POST, DELETE, OPTIONSzAccess-Control-Allow-Headersz=Content-Type, Authorization, Content-Length, X-Requested-WithzAccess-Control-Max-Age86400)r   r7  s    r.   r   ProxyHandler._send_cors_headers  sH    6<79UV79xy17;rB   c           	      >   Sn[         R                  5        Hq  u  pEU R                  R                  U5      (       d  M'  US:X  a4  SU R                  ;  a$  U R                  R	                  SSS5      nU U 3nOU U R                   3n  O   U(       d^  [
        R                  5        HF  u  pEU R                  R                  U5      (       d  M'  U R                  [        U5      S nU SU 3n  O   U(       d   U R                  SSU R                   35        gS	nSn	[        US-   5       GHw  n
 [        U R                  R                  S
S5      5      nUS:  a  U R                  R                  U5      OSn0 nS H+  nU R                  R                  U5      nU(       d  M'  XU'   M-     U(       a*  UR                  SS5      US'   UR                  SS5      US'   [        R                  R!                  UUUUS9nSU R                  ;   a  SnOxSU R                  ;   aC  SU R                  ;   d0  SU R                  ;   d   SU R                  ;   d  SU R                  ;   a  SnO%SU R                  ;   d  SU R                  ;   a  SnOSn[        R                  R#                  UUS9 nUR                  5       nU R%                  UR&                  5        S H9  nUR                  R                  U5      nU(       d  M'  U R)                  UU5        M;     U R+                  5         S
UR                  R                  5        VVs1 s H  u  nnUiM
     snn;  a  U R)                  S
[        U5      5        U R-                  5         U R.                  R1                  U5         SSS5          g   [G        U	S!5      (       a  [I        U	RJ                  5      O
[I        U	5      nU R%                  S"5        U R+                  5         U R-                  5         U R.                  R1                  [L        RN                  " S#U 3US$.5      RQ                  5       5        gs  snnf ! , (       d  f       GM8  = f! [        R2                  R4                   a  nU R%                  UR6                  5        U R+                  5         U R-                  5         UR8                  (       a3  U R.                  R1                  UR8                  R                  5       5         SnA  gSnAf[        R2                  R:                  [<        [>        4 a0  nUn	X:  a  [@        RB                  " S 5         SnAGM.   SnAGM5  SnAf[D         a0  nUn	X:  a  [@        RB                  " S 5         SnAGMf   SnAGMm  SnAff = f)%u   代理请求到后端服务

Args:
    method: HTTP 方法 (GET/POST/DELETE)
    user:   认证用户信息 dict (可选)，含 username/display_name/role
            如果提供，会通过 X-Auth-User 头传给后端
Nr   z/v2z/api/digital-human/v2ry   r  rx  zNo route for r	   r   r   )r  r;  Acceptz
User-AgentRanger   r<  zX-Auth-Userr   r  zX-Auth-Role)r   r   r?  z/api/gptsovitsiX  generatetrainr  taskz/api/comfyuiz/api/iopaintr  rA  r   )r  r   zContent-Ranger  zContent-Dispositionr   ETagzLast-Modifiedg      ?r  r  zBackend unreachable: )r   backend))FULL_PATH_ROUTESr$   r3   r  replacePROXY_ROUTESr9   r3  r]  rQ  r?  r[   rR  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   fpr   ConnectionRefusedErrorOSErrorrL   sleepr*   hasattrr   r  r(   r  rP   )r   r   r  backend_urlprefixrz  new_pathsub_pathmax_retries
last_errorattemptrV  rW  fwd_headersr8  vr   proxy_timeoutr   response_bodyrhr  r  r;   r+  s                            r.   r  ProxyHandler._proxy_request  s    .446NFyy##F++ 11e4996L#yy001EG^`abH%+HXJ"7K%+HTYYK"8K 7 "."4"4"699''//#yyV6H%+HAhZ"8K	 #7 OOC=!<=
[1_-GH!$T\\%5%56F%J!K:H1:Ltzz~6RV ![A((+Aq)*A \ 15*b1IK.15&&1IK.nn,,!'	 -  $tyy0$'M)TYY6J$))<SW^bfbkbkWkovz~  {D  {D  pD  HN  RV  R[  R[  H[$'M#tyy0Ndii4O$'M$&M^^++C+G4$(IIKM&&t{{3` LL,,R01 ,,R3	`
 ++-'dll>P>P>R/S>Rda>R/SS(()93};MN$$&JJ$$]3 HGM .V /6j(.K.KC
))*QTU_Q`	3!

,YK8"%
  68	= 0T HG" <<)) ""166*'')  "44JJ$$QTTYY[1LL))+A7K 
(JJsO )  
(JJsO )sr   /A/Q%"DQ%$AQ9AQ=QAQQ%Q
Q"	Q%"Q%%VA?T,V4UV,VVc                     U(       a  US   OSnS[        U5      ;   a  [        SU 3SS9  gS[        U5      ;  a  [        SU 3SS9  gg)	u   简化日志r   r<  r   z[PROXY] Tr&  200N)r   r8   )r   r
  r   msgs       r.   log_messageProxyHandler.log_messageW  sN    d1g2c#hHSE"$/#c("HSE"$/ #rB   c           
      t    U R                  5       u  pU(       a  SU;  a  U R                  SSS05        gUS   nUS   nUS   nSSKn[        UR                  " 5       5      nU S	U 3nS
U 3n	SSKn
U
R
                  " U
R                  R                  U	5      SS9  [        U	S5       nUR                  U5        SSS5        Sn[        R                  " U5      nUR                  5       nUR                  SS/5      S   nUR                  SXIUR                  S5      S   U[!        U5      S45        UR"                  nUR%                  5         UR'                  5         U R                  SSUS.5        g! , (       d  f       N= f! [(         a'  nU R                  SS[+        U5      05         SnAgSnAff = f)zPOST /api/knowledge/uploadr   rM  r   zNo fileNr  r   r   r  z-D:/AI-Ad-Agent/portal/data/knowledge_uploads/Tr   r  'D:/AI-Ad-Agent/portal/data/knowledge.dbcategorymisczcINSERT INTO files (filename, filepath, filetype, category, filesize, metadata) VALUES (?,?,?,?,?,?)rG   r  r<  r   )rP  file_idru   )r  rU  rL   rQ  r2   r  r3   dirnamer&   r   sqlite3connectcursorr[   executer  r9   	lastrowidcommitr   r*   r   )r   r  r  	file_infor  r  rL   	timestampsafe_filenamefilepathr2   r-   db_pathconncr  r  r;   s                     r.   r)  %ProxyHandler._handle_knowledge_uploada  s   	8 668MFF%/##C'9)=>fI ,H!&)IDIIK(I(k8*5MF}oVHKK1DAh%	" &?G??7+DAzz*vh7:HII{(..*=b*A8SQZ^]_`bkkGKKMJJL'%JK &%  	8gs1v%677	8s6   3F A3F )E5;B9F 5
F?F 
F7F22F7c           	          Sn[         R                  " U5      nUR                  5       nUR                  S5        UR	                  5       nUR
                   Vs/ s H  oUS   PM	     nn/ nU He  n0 n	[        Xh5       H@  u  p[        U[        [        [        [        45      (       d  Uc  XU
'   M3  [        U5      X'   MB     UR                  U	5        Mg     UR                  5         U R                  SSUS.5        gs  snf ! [         a<  n U R                  SS	[        U5      05         SnAg! [         a      SnAgf = fSnAff = f)
zGET /api/knowledge/listr  z-SELECT * FROM files ORDER BY upload_time DESCr   Nr   T)rP  r  ru   r   )r  r  r  r  fetchallr  zipr  r   rQ  floatboolrl  r   rU  r*   )r   r  r  r   rowsdesccolumns
safe_filesrowsafe_rowcolvalr;   s                r.   r  #ProxyHandler._handle_knowledge_list  s!   	?G??7+DAIIEF::<D+,==9=4Aw=G9J #G 1HC!#S%'>??3;(+(+C	 !2
 !!(+  JJL
%KL :  	##C'3q6):;; 	sC   AD C<'BD <D 
ED..
D?8E>D??EEc                 
    SSK JnJn  U" U R                  5      nU" UR                  5      nUR                  SS/5      S   nU(       d  U R                  SSS05        gSSKnSSKnSnUR                  " U5      n	U	R                  5       n
U
R                  S	U45        U
R                  5       nU(       aC  US   (       a9  UR                  R                  US   5      (       a   UR                  " US   5        U
R                  S
U45        U	R!                  5         U	R#                  5         U R                  SSS05        g! [         a     NTf = f! [         a'  nU R                  SS[%        U5      05         SnAgSnAff = f)z#DELETE /api/knowledge/delete?id=xxxr   rv  rs   NrM  r   z
Missing idr  z%SELECT filepath FROM files WHERE id=?zDELETE FROM files WHERE id=?r   rP  Tru   )r   r   rA  r3   rB  r[   rU  r2   r  r  r  r  fetchoner4   r  r*   r  r   r   )r   r   rA  r   ry  r  r2   r  r  r  r   r+  r;   s                r.   r1  %ProxyHandler._handle_knowledge_delete  sD   	87dii(Ffll+Fjjv.q1G##C'<)@A?G??7+DAII=zJ**,Cs1v"''..Q"8"8IIc!f% II4wjAKKMJJLi%67 !   	8gs1v%677	8sD   AE  BE $E 9AE 
EE EE 
FE==Fc                     U R                  5       u  pUR                  SS/5      S   nUR                  SS/5      S   nU R                  SSSS	.5        g! [         a'  nU R                  S
S[	        U5      05         SnAgSnAff = f)u?   POST /api/knowledge/generate — 基于上传文件生成知识r  r<  r   r  r  r   Tu   知识生成功能开发中)rP  r   ru   r   N)r  r[   rU  r*   r   )r   r  r  r  r  r;   s         r.   r*  'ProxyHandler._handle_knowledge_generate  s    	8 668MFZZ2$/2Fzz*vh7:HA^%_` 	8gs1v%677	8s   AA 
B BBr@   r?   )+r   
__module____qualname____firstlineno__r   r   r   r,  r4  r8  rF  r  r  r  r   r  r"  r2  r
  r  r!  r0  rU  r  r	  r  r  r#  r$  r%  r&  r'  r(  r  r   r  r  r)  r  r1  r*  __static_attributes____classcell__r   s   @r.   r   r   `  s    >$*8t4(l,0 :L$FLR0(FT_:?)>E>C,H) 0hAJ:x2ZxWQr|N|5Kn(QT=)~<un08>888	8 	8rB   r   c                   4   ^  \ rS rSrSrSrSrU 4S jrSrU =r	$ )ProxyServeri  u0   多线程代理服务器，带启动时间记录Tc                 Z   > [         TU ]  " U0 UD6  [        R                  " 5       U l        g r?   )r   r   rL   r  r   s      r.   r   ProxyServer.__init__  s"    $)&)))+rB   )r  )
r   r5  r6  r7  __doc__daemon_threadsallow_reuse_addressr   r8  r9  r:  s   @r.   r<  r<    s    :N& &rB   r<  __main__z7=======================================================u7     正方元智能广告工作台 - 代理服务器 v2.0u
     端口: u     静态目录: u&     用户认证: 已启用 (users.xlsx)z0.0.0.0u   
[警告] 端口 u%    已被占用，尝试强制释放...win32znetstat -ano | findstr :)shellcapture_outputr)  r  r   r  ztaskkill /F /PID )rD  rE  u     已终止 PID=u)   
代理服务器运行在 http://0.0.0.0:u   路由: u   健康检查: http://127.0.0.1:r   u   登录接口: http://127.0.0.1:r  u   
正在关闭...u   代理服务器已停止)ry   r  )r   r	   )br?  http.serverhttpr  urllib.requestr   urllib.errorr   r(   r  r   r2   r   rL   	threadingrQ   rM   rI   r   r   socketserverr   PORTr  r  r   r   r   Lockr   r3   r  abspath__file__r   rd   re   r  rO   rT   r'   r#   r"   r/   r<   rw   rc  r=   r  r   rY   r\   r_   rU   rf   listrk   ro   dictrx   rQ  r   r   r&  r   r   r   r  SimpleHTTPRequestHandlerr   
HTTPServerr<  r   chdirr8   getcwdr   r   r   bindr   r  platformr  runr   r  rT  r  liner  r9   isdigitr  keysserve_foreverKeyboardInterruptshutdownr@   rB   r.   <module>r_     s  	       	 
 	       ( '6555	 1  BD9;V<99	  ^^%  77??277??84577<<&) Ht $ - ggll8]3~~&2:   8 (S  3 3 $
# 
$C >s >s >
	3 	4 	<3 < < d t # C s SV `d  s s t C C D >B<a84;;77 a8F3&.$++"8"8 & zHHX	(O	
CD	Jtf
	RYY[M
*+	24	(O ==););<D		9d#$

  )T*L9F	6tf
=>	HT,++-./
01	+D6
=>	+D6
AB*K g:  D;  "4&(MNO<<7"^^*4&14dF ++-33D9

**,u:?uRy'8'8':':NN%6uRyk#B$_cd,U2YK89	 :
 JJqM.  *!"()*sC   8K9 6#L	 'O: 9	LL	BO7O77<O76O7:&P#"P#