
    |Hjg                        d Z ddlZddlZddlZddlZddlZddlmZ ddl	m
Z
mZ ej                  j                  ej                  j                  e            Zej                  j#                  ed      Zej                  j#                  ed      Z eej*                        dkD  r'ej*                  d   d	k(  r eej*                  d         nd
ZdZh dZ ej4                  ed        G d de      Zedk(  rD e
defe      Z ed        ede d        ede         ed       	 ej?                          yy# e $ r  ed       Y yw xY w)u  
BloomWealth Board — local server
Serves dashboard.html and a simple tasks REST API backed by tasks.json.

Usage:
  python3 ~/board/server.py
  python3 ~/board/server.py --port 7788

Claude updates ~/board/tasks.json directly; the dashboard reads/writes via this server.
    N)date)
HTTPServerSimpleHTTPRequestHandlerz
tasks.jsonuploads      z--portil  i  >   .md.bmp.csv.doc.gif.jpg.key.pdf.png.ppt.rtf.svg.txt.xls.docx.heic.jpeg.pptx.webp.xlsx.pages.numbersT)exist_okc                   L     e Zd Z fdZd Zd Zd Z fdZd Zd Z	d Z
 xZS )	Handlerc                 0    t        |   |dt        i| y )N	directory)super__init__	BOARD_DIR)selfargskwargs	__class__s      /opt/board/server.pyr%   zHandler.__init__&   s    $>)>v>    c                      y )N )r'   fmtr(   s      r+   log_messagezHandler.log_message)   s    r,   c                 p    | j                  dd       | j                  dd       | j                  dd       y )NzAccess-Control-Allow-Origin*zAccess-Control-Allow-MethodszGET, POST, OPTIONSzAccess-Control-Allow-HeadersContent-Type)send_headerr'   s    r+   _corszHandler._cors,   s6    6<79MN7Hr,   c                 f    | j                  d       | j                          | j                          y )N   )send_responser6   end_headersr5   s    r+   
do_OPTIONSzHandler.do_OPTIONS1   s%    3

r,   c                    | j                   dv rd}t        j                   j                  t              r)t	        t        d      5 }|j                         }d d d        | j                  d       | j                  dd       | j                          | j                          | j                  j                  |       y | j                   dv r_| j                  d       | j                  dd       | j                          | j                          | j                  j                  d       y t        | 5          y # 1 sw Y   xY w)	Nz
/api/tasksz/api/tasks/s   []rb   r3   application/json)z/api/healthz/api/health/   {"ok":true})pathosexists
TASKS_FILEopenreadr9   r4   r6   r:   wfilewriter$   do_GET)r'   datafr*   s      r+   rJ   zHandler.do_GET6   s    9955Dww~~j)*d+ $q668D$s#^-?@JJLJJT"YY99s#^-?@JJLJJ^, GN!$ $s   D99Ec                 l   | j                   dv rt        | j                  j                  dd            }| j                  j                  |      }	 t        j                  |      }t        |t              sJ 	 t!        t"        dd	
      5 }t        j$                  ||dd       d d d        | j                  d       | j                  dd       | j'                          | j                          | j                  j                  d       y | j                   dv r| j)                          y | j                  d       | j                          y # t        $ rQ | j                  d       | j                  dd       | j                          | j                  j                  d       Y y w xY w# 1 sw Y   xY w)Nr=   Content-Lengthr     r3   r@   s   {"error":"invalid JSON"}wutf-8)encodingr   F)indentensure_asciir?   rA   )z/api/uploadz/api/upload/i  )rB   intheadersgetrfilerG   jsonloads
isinstancelist	Exceptionr9   r4   r:   rH   rI   rF   rE   dumpr6   _handle_upload)r'   lengthbodytasksrL   s        r+   do_POSTzHandler.do_POSTL   sj   9955))*:A>?FZZ__V,D

4(!%... j#8 BA		%15AB s#^-?@JJLJJ^,YY99! s#+  ""3'  1CD  "

  !<=B Bs   'E 
F)AF&%F&)F3c                    | j                  |       | j                  dd       | j                          | j                          | j                  j                  t        j                  |      j                  d             y )Nr3   r@   rQ   )	r9   r4   r6   r:   rH   rI   rY   dumpsencode)r'   codeobjs      r+   _jsonzHandler._jsonj   s[    4 );<



C//89r,   c           
         t        | j                  j                  dd            }|dk  r| j                  dddi      S |t        kD  r| j                  dddi      S t
        j                  j                  | j                  j                  dd	            }t        j                  j                  |      }| j                  j                  d
d      }t        j                  j                  |      d   j                         }|t        vr| j                  ddd|xs d di      S |}g }|dkD  rM| j                  j                  t!        |d            }|sn%|j#                  |       |t%        |      z  }|dkD  rMdj'                  |      }t)        j*                         j,                  |z   }		 t/        t        j                  j'                  t0        |	      d      5 }
|
j3                  |       d d d        | j                  dd|d|	z   |t%        |      t7        j8                         j;                         d       y # 1 sw Y   OxY w# t4        $ r!}| j                  ddd| i      cY d }~S d }~ww xY w)NrN   r   rO   errorz
empty bodyi  zfile too large (max 25 MB)z
X-Filenamefiler3   zapplication/octet-streamr   i  z
file type ?z not allowedi   r,   wbi  zsave failed: r?   Tz	/uploads/)oknameurltypesizeuploaded)rU   rV   rW   ri   
MAX_UPLOADurllibparseunquoterC   rB   basenamesplitextlowerALLOWED_EXTrX   rG   minappendlenjoinuuiduuid4hexrF   
UPLOAD_DIRrI   OSErrorr   today	isoformat)r'   r`   raw_namectypeext	remainingchunksbufrK   storedrL   es               r+   r_   zHandler._handle_uploadq   s    T\\%%&6:;Q;::cG\#:;;J::cG-I#JKK<<''(8(8v(NO77##H-<<##N4NO77##H-a0668k!::cGz#*\-R#STT 	!m**//#i"9:CMM#S!I !m xx!!C'	Cbggll:v6= 
 	

3'I

..0
 	  	C::cG}QC-@#ABB	Cs6   -.I H;-I ;I I 	I1I,&I1,I1)__name__
__module____qualname__r%   r0   r6   r;   rJ   rc   ri   r_   __classcell__)r*   s   @r+   r!   r!   %   s,    ?I

,<:'r,   r!   __main__	localhostz
  BloomWealth Boardu     → http://localhost:z/dashboard.htmlz	  Tasks: z  Press Ctrl+C to stop.
z
  Stopped.)!__doc__rY   rC   sysr   urllib.parserv   datetimer   http.serverr   r   rB   dirnameabspath__file__r&   r   rE   r   r   argvrU   PORTru   r|   makedirsr!   r   serverprintserve_foreverKeyboardInterruptr.   r,   r+   <module>r      sF  	  	 
    <ggoobggooh78	ggll9l3
ggll9i0
"%chh-!"3x8Oc#((1+UY
 J &s& sl zd+W5F	!#	#D6
9:	Ij\
"#	%'   ls   ,D> >EE