
    Hi.                        d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
Z
d dlmZ d dlmZmZ d dlmZ d dlmZmZ d dlmZmZmZmZmZ d dlmZmZ  ej>                  d      Z h d	Z!h d
Z"dZ#dZ$dd dZ%h dZ& G d de'      Z( G d de      Z)d Z*d Z+d Z,d Z-d Z.d Z/d Z0d Z1d Z2d Z3d Z4d Z5d  Z6d! Z7d" Z8d# Z9d$ Z:d% Z;d& Z<d' Z=d( Z>d) Z?d* Z@d+ ZAd, ZBd- ZCd. ZDd/ ZEd0 ZFd1 ZGd2 ZHd3 ZId4 ZJd5 ZKd6 ZLd7 ZMd8 ZNd9 ZOd: ZPd; ZQd< ZRd= ZSd> ZTd? ZUd@ ZVdA ZWdB ZXdC ZYdD ZZdE Z[dF Z\dG Z]e^dHk(  r e]        yy)I    N)escape)SimpleHTTPRequestHandlerThreadingHTTPServer)Path)	HTTPErrorURLError)parse_qsunquote	urlencodeurljoinurlparse)Requesturlopenz^[a-z0-9][a-z0-9-]*$>   heroeventquote>   .jpg.png.jpeg.webp.svgi   zinvitaciones.sqlite3 )access_token
expires_at>2   cbuhoratipoaliasfirmafrasesalonfooterfaviconmapsUrlnombresogImageogTitletitularwazeUrlfechaISOshareUrlwhatsapp	direccion	dresscode	heroImage	publicUrl
eventImage
fechaCortadresscodeNoteogDescriptionpaletteColor1paletteColor2paletteColor3paletteColor4paletteColor5dresscodeAvoid1dresscodeAvoid2dresscodeColor1dresscodeColor2dresscodeColor3whatsappRsvpTextwhatsappShareTextdresscodeAvoid1NamedresscodeAvoid2NamedresscodeColor1NamedresscodeColor2NamedresscodeColor3NamewhatsappLocationTextwhatsappOrganizerTextdresscodeAvoid1EnableddresscodeAvoid2EnableddresscodeColor1EnableddresscodeColor2EnableddresscodeColor3Enabledc                        e Zd Z fdZ xZS )
AdminErrorc                 @    || _         || _        t        |   |       y N)statusmessagesuper__init__)selfrQ   rR   	__class__s      scripts/admin-server.pyrT   zAdminError.__init__R   s    !    )__name__
__module____qualname__rT   __classcell__rV   s   @rW   rN   rN   Q   s    " "rX   rN   c                        e Zd ZU eed<    fdZd Z fdZd Zd Z	d Z
ddZdd	Zd
 Zd Zd Zd Zd Zd Zd Z xZS )AdminHandler	repo_rootc                     | j                  dd       | j                  dd       | j                  dd       | j                  dd       t        | 	          y )	NzCache-Controlzno-storezAccess-Control-Allow-Origin*zAccess-Control-Allow-MethodszGET, POST, DELETE, OPTIONSzAccess-Control-Allow-HeaderszContent-Type, X-Filename)send_headerrS   end_headers)rU   rV   s    rW   rd   zAdminHandler.end_headers[   sS    *56<79UV79STrX   c                 F    | j                  d       | j                          y )N   )send_responserd   )rU   s    rW   
do_OPTIONSzAdminHandler.do_OPTIONSb   s    3rX   c                    	 t        | j                        }t        |j                        }|dk(  s'|j                  d      s|dk(  s|j                  d      rt	        dd      |dk(  r'| j                  dt        | j                        i       y |d	k(  r'| j                  d
t        | j                        i       y |dk(  r'| j                  dt        | j                        i       y |dk(  r| j                  t        |             y t        j                  d|      }|r`t        |j                  d            }t        | j                  |t!        |             | j                  t#        | j                  |             y t        j                  d|      }|rat        |j                  d            }t        | j                  |t!        |             | j%                  t'        | j                  |      d       y t        j                  d|      }|rat        |j                  d            }t        | j                  |t!        |             | j%                  t)        | j                  |      d       y t        j                  d|      }|rxt        |j                  d            }t        | j                  |t!        |             t+        | j                  |      }| j                  |t-        |      t/        |      d       y t        j                  d|      }	|	rjt        |	j                  d            }t1        | j                  |      dz  }
|
j3                         st	        dd      | j                  t5        |
             y t6        | q          y # t        $ r4}| j                  d|j:                  d|j<                         Y d }~y d }~ww xY w)Nz/clientes/_accessz/clientes/_access/z/clientes/_dataz/clientes/_data/  zArchivo no encontradoz/api/templates	templates/api/clientesclientesz/api/archivados
archivadosz/api/spotify/searchz/api/cliente/([^/]+)/dashboard   z"/api/cliente/([^/]+)/dashboard.csvztext/csv; charset=utf-8z"/api/cliente/([^/]+)/dashboard.xlsz'application/vnd.ms-excel; charset=utf-8z/api/cliente/([^/]+))slugconfig
previewUrl/api/clientes/([^/]+)/configconfig.jsonConfig no encontradoFokerrorrQ   )r   pathr
   
startswithrN   	send_jsonlist_templatesr`   list_clientslist_archived_clientsspotify_search_responsere	fullmatchvalidate_sluggrouprequire_client_tokentoken_from_queryclient_dashboard	send_textclient_dashboard_csvclient_dashboard_xlsread_client_configfilter_client_configclient_preview_url
client_direxists	read_jsonrS   do_GETrR   rQ   )rU   parsedroutedashboard_matchrp   	csv_match	xls_matchclient_panel_matchrq   matchconfig_pathrx   rV   s               rW   r   zAdminHandler.do_GETf   s#   D	Wdii(FFKK(E,,##$89--##$67 &=>>((^DNN-KLM'
L,HIJ)).CDNN.STU--6v>? ll+LeTO$_%:%:1%=>$T^^T;KF;ST/EF%JERI$Y__Q%78$T^^T;KF;ST3DNNDIKde%JERI$Y__Q%78$T^^T;KF;ST3DNNDIKtu!#.Eu!M!$%7%=%=a%@A$T^^T;KF;ST+DNNDA 26:"4T":  
 LL!@%HE$U[[^4(>N"))+$S*@AAy56GN 	WNN%%--@NVV	WsS   BN +N =+N )N 	A7N A8N :A8N 3BN BN N 	O*OOc                 :   	 t        | j                        }t        |j                        }|dk(  r| j                          y t	        j
                  d|      }|r1| j                  |j                  d      |j                  d             y t	        j
                  d|      }|r\t        |j                  d            }t        | j                  |t        |             | j                  ||j                  d             y t	        j
                  d|      }|r+| j                  |j                  d      t        |             y t	        j
                  d|      }|r1| j                  |j                  d      |j                  d             y t	        j
                  d|      }|r!| j                  |j                  d             y t	        j
                  d	|      }	|	r1| j                  |	j                  d      |	j                  d             y t	        j
                  d
|      }
|
r!| j!                  |
j                  d             y | j#                  dddd       y # t$        $ r4}| j#                  d|j&                  d|j(                         Y d }~y d }~ww xY w)Nrl   z/api/public/([^/]+)/(rsvp|song)ro      z#/api/cliente/([^/]+)/assets/([^/]+)z/api/cliente/([^/]+)/configz$/api/clientes/([^/]+)/assets/([^/]+)rs   z&/api/clientes/([^/]+)/(deploy|restart)z/api/archivados/([^/]+)/restoreFEndpoint no encontradorv   rj   ry   )r   rz   r
   create_clientr   r   save_public_entryr   r   r   r`   r   
save_assetsave_client_panel_configsave_configrun_composerestore_clientr|   rN   rR   rQ   )rU   r   r   public_rsvp_matchclient_asset_matchrp   client_config_matchasset_matchconfig_matchdeploy_matchrestore_matchrx   s               rW   do_POSTzAdminHandler.do_POST   s:   /	Wdii(FFKK(E'""$ "-OQV W &&'8'>'>q'ACTCZCZ[\C]^!#.TV[!\!$%7%=%=a%@A$T^^T;KF;ST&8&>&>q&AB"$,,/Mu"U"--.A.G.G.JL\]cLde,,'NPUVK 1 1! 4k6G6G6JK<<(GOL  !3!3A!67<<(QSXYL  !3!3A!68J8J18MNLL)KUSM##M$7$7$:;NN%2JKTWNX 	WNN%%--@NVV	WsL   ?I AI A3I ?AI AI 8I AI 8I I 	J&*JJc                    	 t        j                  d| j                        }|s| j                  dddd       y t	        |j                  d            }t        | j                  |      }|j                         st        dd      | j                  d	z  d
z  }|j                  dd       || dt        j                  d       z  }t        j                  t        |      t        |             | j                  dt        |j!                  | j                              d       y # t        $ r4}| j                  d|j"                  d|j$                         Y d }~y d }~ww xY w)Nz/api/clientes/([^/]+)Fr   rv   rj   ry   ro   Cliente no encontradorm   _archivadosTparentsexist_ok-z%Y%m%d-%H%M%S)rw   archivedPath)r   r   rz   r|   r   r   r   r`   r   rN   mkdirtimestrftimeshutilmovestrrelative_torR   rQ   )rU   r   rp   sourcearchive_roottargetrx   s          rW   	do_DELETEzAdminHandler.do_DELETE   s-   	WLL!9499EEe6NOX[\ Q0D5F==? &=>>>>J6FLtd;!tfAdmmO.L-M$NNFKKFS[1NN #F$6$6t~~$F G   	WNN%%--@NVV	Ws   8D( C,D( (	E%1*E  E%c                    t        | j                  j                  dd            }| j                  j	                  |      }	 t        j                  |j                  d      xs d      S # t
        j                  $ r t        dd      w xY w)NContent-Length0utf-8z{}  zJSON invalido)
intheadersgetrfilereadjsonloadsdecodeJSONDecodeErrorrN   )rU   lengthraw_bodys      rW   read_json_bodyzAdminHandler.read_json_body   st    T\\%%&6<=::??6*	3::hoog6>$??## 	3S/22	3s   'A* * B
c                 4   t        j                  |d      j                  d      }| j                  |       | j	                  dd       | j	                  dt        t        |                   | j                          | j                  j                  |       y )NFensure_asciir   Content-Typezapplication/json; charset=utf-8r   )
r   dumpsencoderg   rc   r   lenrd   wfilewrite)rU   payloadrQ   bodys       rW   r|   zAdminHandler.send_json   ss    zz'6==gF6")JK)3s4y>:

rX   c                 
   |j                  d      }| j                  |       | j                  d|       | j                  dt        t	        |                   | j                          | j                  j                  |       y )Nr   r   r   )r   rg   rc   r   r   rd   r   r   )rU   textcontent_typerQ   r   s        rW   r   zAdminHandler.send_text  se    {{7#6"6)3s4y>:

rX   c                 N   | j                         }t        |j                  dd            }t        |j                  dd            }t        |j                  dd            j	                         }| j
                  dz  |z  }|j                         st        dd      t        | j
                  |      }|j                         rt        dd	      t        j                  ||       |d
z  }t        |      }||d<   t        |       |d<   t        |      }	|	r0|j                  d      xs |	|d<   |j                  d      xs |	|d<   |r||d<   | d|d<   ||d<   t        ||       t        ||       | j!                  dt#        | j
                  |      t%        |      dd       y )Nrp   r   templatenamerk   rj   zTemplate no encontrado  zEl cliente ya existert   
clientSlugbackendApiUrlr0   r+   r%   z - InvitacionseoTitler"   Trw   clienteurl   ry   )r   r   r   r   stripr`   r   rN   r   r   copytreer   admin_base_urlconfigured_client_public_urlprepare_social_preview
write_jsonr|   client_summaryclient_public_url)
rU   r   rp   r   display_nametemplate_pathr   r   rq   
public_urls
             rW   r   zAdminHandler.create_client  s   %%'W[[45 Z!<=7;;vr2399;4x?##%S":;;DNND1==?S"899v.},;'#|"0"61$7
"(**["9"GZF;!'J!7!E:F: ,F9$0>!?F:+F8vv.;'%dnnd;$T*
 	 	 	rX   c                 D   t        |      }| j                         }t        | j                  |      }|j	                         st        dd      |dz  }t        ||       t        ||       | j                  dt        |j                  | j                              d       y )Nrj   r   rt   Trw   rz   )r   r   r   r`   r   rN   r   r   r|   r   r   )rU   rp   r   
target_dirr   s        rW   r   zAdminHandler.save_config1  s    T"%%'5
  "S"9::m+z7367#dC0B0B4>>0R,STUrX   c                    t        |      }t        | j                  ||       | j                         }t	        | j                  |      }|j                         D ]  \  }}|t        v s|||<    t        | j                  |      dz  }t        t        | j                  |      |       t        ||       | j                  dt        |      d       y )Nrt   T)rw   rq   )r   r   r`   r   r   itemsCLIENT_PANEL_FIELDSr   r   r   r|   r   )rU   rp   tokenr   current_configkeyvaluer   s           rW   r   z%AdminHandler.save_client_panel_config<  s    T"T^^T59%%'+DNNDA!--/ 	,JC))&+s#	, DNND1MAz$..$?P6>*d.B>.RSTrX   c                 F   t        |      }t        | j                  |      j                         st	        dd      | j                         }|j                  d      xs t        j                  d      |d<   t        | j                  |||       | j                  ddi       y )Nrj   r   fecha%Y-%m-%dT%H:%M:%Srw   T)r   r   r`   r   rN   r   r   r   r   store_public_entryr|   )rU   rp   kindr   s       rW   r   zAdminHandler.save_public_entryI  s    T"$..$/668S"9::%%'";;w/U4==AT3U4>>4w?d|$rX   c                    t        |      }|t        vrt        dd      t        | j                  |      }|j                         st        dd      | j                  j                  dd      }t        |      j                  j                         }|t        vrt        dd      t        | j                  j                  dd	            }|d
k  s	|t        kD  rt        dd      |dz  }|j                  d       || | z  }|d| | dz  }	|	j                  | j                   j#                  |             t%        j&                  |	|       | j)                  dd|j*                   d       y )Nr   zSlot de imagen invalidorj   r   z
X-Filenamer   z0Formato no permitido. Usar jpg, png, webp o svg.r   r   r   z Archivo vacio o demasiado grandeassetsTr   ..tmpassets/r   )r   ASSET_SLOTSrN   r   r`   r   r   r   r   suffixlowerASSET_EXTENSIONSr   MAX_ASSET_BYTESr   write_bytesr   r   osreplacer|   r   )
rU   rp   slotr   original_name	extensionr   
assets_dirr   
tmp_targets
             rW   r   zAdminHandler.save_assetR  sT   T"{"S";<<5
  "S"9::((r:'..446	,,S"TUUT\\%%&6<=Q;&?2S"DEE(*
$'yk22AdVI;d";;
tzzv67


:v&dgfkk],CDErX   c                     t        |      }t        | j                  |      j                         st	        dd      | j                  ddd       y )Nrj   r   Tz_No requiere publicacion manual por cliente: el contenedor web sirve la carpeta automaticamente.)rw   output)r   r   r`   r   rN   r|   )rU   rp   actions      rW   r   zAdminHandler.run_composel  sH    T"$..$/668S"9::w
 	rX   c                    t        |      }t        | j                  |      }|j                         st	        dd      t        |      }t        | j                  |      }|j                         rt	        dd      t        j                  t        |      t        |             |dz  }|j                         rnt        |      }t        |       |d<   t        |      }|r0|j                  d      xs ||d<   |j                  d      xs ||d<   t        ||       t        ||       | j!                  d	t#        | j                  |      t%        |      d
       y )Nrj   zArchivado no encontrador   z(Ya existe un cliente activo con ese slugrt   r   r0   r+   Tr   )validate_archive_namearchive_dir_for_namer`   r   rN   slug_from_archive_namer   r   r   r   r   r   r   r   r   r   r|   r   r   )rU   archive_namearchive_dirrp   r   r   rq   r   s           rW   r   zAdminHandler.restore_clientu  s(   ,\:*4>><H!!#S";<<%l3DNND1==?S"LMMC$c&k2},{+F&4T&:F?#5d;J&,jj&=&K{#%+ZZ
%;%Izz""662{F+%dnnd;$T*
 	rX   )   )ztext/plain; charset=utf-8r  )rY   rZ   r[   r   __annotations__rd   rh   r   r   r   r   r|   r   r   r   r   r   r   r   r   r\   r]   s   @rW   r_   r_   X   s]    OEWN0WdW*3#J	VU%F4rX   r_   c                     t        | xs d      j                         }t        j                  |      st	        dd      |S )Nr   r   zSlug invalido)r   r   SLUG_REr   rN   )r   rp   s     rW   r   r     s9    u{!!#DT"o..KrX   c                     t        | xs d      j                         }t        j                  d|      st	        dd      |S )Nr   [a-z0-9][a-z0-9-]*-\d{8}-\d{6}r   Nombre de archivado invalido)r   r   r   r   rN   )r   r  s     rW   r  r    s<    u{#))+L<<9<H<==rX   c                 ~    t        j                  d|       }|st        dd      t        |j	                  d            S )Nz(.+)-\d{8}-\d{6}r   r%  ro   )r   r   rN   r   r   )r  r   s     rW   r  r    s7    LL,l;E<==Q((rX   c                     | dz  |z  j                         }| dz  j                         }||j                  vrt        dd      |S )Nrm   r   zRuta de cliente invalidaresolver   rN   )r`   rp   rz   clients_roots       rW   r   r     sL    
"T)224D
*335L4<<'899KrX   c                     | dz  dz  |z  j                         }| dz  dz  j                         }||j                  vrt        dd      |S )Nrm   r   r   zRuta de archivado invalidar(  )r`   r  rz   r   s       rW   r  r    sV    
"]2\AJJLD
*]:CCEL4<<':;;KrX   c                 L    t        j                  | j                  d            S )Nr   encoding)r   r   	read_text)rz   s    rW   r   r     s    ::dnngn677rX   c                 n    t        | |      dz  }|j                         st        dd      t        |      S )Nrt   rj   ru   )r   r   rN   r   )r`   rp   r   s      rW   r   r     s8    Y-=K455[!!rX   c                     | j                  d| j                   d      }|j                  t        j                  |dd      dz   d       t        j                  ||        y )	Nr  r  Fr   )r   indent
r   r-  )	with_namer   
write_textr   r   r  r  )rz   r   r  s      rW   r   r     sX    !DII;d 34J

7q9D@   JJz4 rX   c                 h    t        t              D ci c]  }|| j                  |d       c}S c c}w )Nr   )sortedr   r   )rq   r   s     rW   r   r     s,    067J0KLCC$$LLLs   /c                 6    t        t        | xs d      d      S )Nr   T)r   )html_escape_libr   r   s    rW   	html_attrr;    s    3u{+488rX   c                     | j                  d      xsO dj                  d | j                  d      | j                  d      fD              xs | j                  d      xs dS )Nr'   z - c              3   &   K   | ]	  }|s|  y wrP    ).0parts     rW   	<genexpr>zsocial_title.<locals>.<genexpr>  s     at\`das   r%   r2   r   
Invitacionr   joinrq   s    rW   social_titlerF    s\    

9 	::a

9(=vzz,?W'Xaa	::j!	 	rX   c                 R    | j                  d      xs | j                  d      xs dS )Nr4   seoDescriptionzGTe invitamos a celebrar. Confirma asistencia y mira todos los detalles.)r   rE  s    rW   social_descriptionrI    s/    

?# 	U::&'	UTrX   c                     t        j                  dt        | xs d            }|syt        |      dk(  r|d   d d j	                         S |d   d   |d   d   z   j	                         S )Nu(   [A-Za-zÁÉÍÓÚÜÑáéíóúüñ0-9]+r   IVro   r   r   )r   findallr   r   upper)r   wordss     rW   initials_from_namerP    sk    JJBCQSDTUE
5zQQx|!!##!HQK%)A,&--//rX   c                     t        |xs d      }t        | j                  d      xs d      j                         }|rt        j                  d|      r|S t        |j                  d      dz   |      S )Nr   r0   z
^https?:///)r   r   r   r   r   r   rstrip)rq   rz   r   s      rW   public_asset_urlrT    sb    tzr?DVZZ,2399;J-6:$$S)C/66rX   c                     | j                  dd       | j                  dd       | j                  dd       | j                  dd       | j                  dd       y )	Nr0   r   r'   r4   r&   zassets/og-image.svgr#   assets/favicon.svg)
setdefaultrE  s    rW   ensure_social_config_defaultsrX    sT    
k2&
i$
or*
i!67
i!56rX   c                 P    t        |       t        | |       t        | dz  |       y )Nz
index.html)rX  write_social_assetsupdate_social_head)client_pathrq   s     rW   r   r     s#    !&)V,{\16:rX   c                    | dz  }|j                  d       t        |j                  d      xs d      }|j                  d      rEt	        |      j
                  j                         dk(  r| |z  j                  t        |      d	       t        |j                  d
      xs d      }|j                  d      rGt	        |      j
                  j                         dk(  r | |z  j                  t        |      d	       y y y )Nr  Tr  r#   r   r  r   r   r-  r&   )
r   r   r   r{   r   r
  r  r5  render_favicon_svgrender_og_image_svg)r\  rq   r  r#   og_images        rW   rZ  rZ    s    x'Jd#&**Y'-2.G)$g)=)=)C)C)E)O	w	**+=f+EPW*X6::i(.B/H9%$x.*?*?*E*E*G6*Q	x	++,?,GRY+Z +R%rX   c                 Z    t        t        | j                  d                  }d| d| dS )Nr%   zS<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="z">
  <rect width="64" height="64" rx="14" fill="#241f19"/>
  <circle cx="50" cy="14" r="18" fill="#c8a55f" opacity=".42"/>
  <text x="32" y="39" text-anchor="middle" fill="#fffaf3" font-family="Georgia, serif" font-size="22" font-weight="700"></text>
</svg>
)r;  rP  r   )rq   initialss     rW   r^  r^    sI    +FJJy,ABCHbckbl my zB  yC C rX   c                    t        t        |             }t        | j                  d      xs d      }t        | j                  d      xs d      }t        | j                  d      xs d      }d| d| d| d	| d
| dS )Nr   rB  r2   r   r!   z|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">
  <title id="title">a  </title>
  <desc id="desc">Imagen para compartir invitacion.</desc>
  <defs>
    <linearGradient id="bg" x1="0" x2="1" y1="0" y2="1">
      <stop offset="0" stop-color="#fffaf3"/>
      <stop offset=".48" stop-color="#c8a55f"/>
      <stop offset="1" stop-color="#241f19"/>
    </linearGradient>
  </defs>
  <rect width="1200" height="630" fill="url(#bg)"/>
  <circle cx="1040" cy="80" r="210" fill="#fffaf3" opacity=".18"/>
  <circle cx="110" cy="560" r="260" fill="#241f19" opacity=".2"/>
  <rect x="70" y="70" width="1060" height="490" fill="none" stroke="#fffaf3" stroke-width="6" opacity=".76"/>
  <text x="600" y="182" text-anchor="middle" fill="#fffaf3" font-family="Arial, sans-serif" font-size="30" font-weight="700" letter-spacing="7">z</text>
  <text x="600" y="318" text-anchor="middle" fill="#fffaf3" font-family="Georgia, serif" font-size="92" font-style="italic">z</text>
  <text x="600" y="408" text-anchor="middle" fill="#fffaf3" font-family="Arial, sans-serif" font-size="38" font-weight="700" letter-spacing="5">zs</text>
  <text x="600" y="475" text-anchor="middle" fill="#fffaf3" font-family="Arial, sans-serif" font-size="28">rb  )r;  rF  r   )rq   title
event_typedateplaces        rW   r_  r_    s    l6*+E6::f-=>JVZZ-34Dfjj)/R0EG Q R\  Q] ]} ~C  }D DQ RV  QW Wllqkr s# rX   c                    | j                         sy | j                  d      }t        |      }t        |      }t	        ||j                  d            }|j                  d      xs d}t        |j                  d      xs d      j                         }|r|j                  d      dz   nd}t        |||||      }	|j                  d	      }
|j                  d
|
      |j                  d|
      |j                  d|
      |j                  d|
      g}|D cg c]
  }|dk7  s	| }}|
dk7  r|rt        |      }|d |
 |	z   ||d  z   }n|j                  dd|	z   d      }| j                  |d       y c c}w )Nr   r-  r&   r#   rV  r0   r   rR  	  <title>z  <link rel="preconnect"z  <link rel="stylesheet"z  <stylez</head>rL  z[  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
ro   )r   r/  rF  rI  rT  r   r   r   rS  social_head_blockfindminr  r5  )
index_pathrq   htmlre  descriptionr`  r#   r   og_url
head_blockstartend_markersindexend_candidatesendupdateds                   rW   r[  r[  5  s}   1D E$V,K

9(=>Hjj#;';GVZZ,2399;J-7Zs#c)RF"5+xQJIIk"E		.6		.6		*e$		)U#	K *5DeDND{~.!v,+d34j8,,nnq{{

 'G4 Es   
E4#E4c                    dt        |        ddt        |       dddt        |        ddt        |       dg}|r|j                  dt        |       d       |r|j                  d	t        |       d       |j                  d
dt        |        ddt        |       dg       |r|j                  dt        |       d       |j                  dt        |       ddg       dj                  |      dz   S )Nrj  z</title>z$  <meta name="description" content="z">z-  <meta property="og:type" content="website">z%  <meta property="og:title" content="z+  <meta property="og:description" content="z#  <meta property="og:url" content="z%  <meta property="og:image" content="z:  <meta name="twitter:card" content="summary_large_image">z&  <meta name="twitter:title" content="z,  <meta name="twitter:description" content="z&  <meta name="twitter:image" content="z  <link rel="icon" href="z" type="image/svg+xml">z-  <meta name="theme-color" content="#c9a96e">r3  )r;  appendextendrD  )re  rp  r`  rq  r#   liness         rW   rk  rk  T  s/   
Ie$%X.
1)K2H1IM;
29U3C2DCH
8;9O8PPSTE =i>O=PPSTU?	(@S?TTWXY	LLH
3Ie4D3ESI
9)K:P9QQTU 
 @8AT@UUXYZ	LL
&y'9&::TU;  99Ud""rX   c                 T    t        | j                        j                  ddg      d   S )Nr   r   r   )r	   queryr   )
parsed_urls    rW   r   r   n  s'    J$$%))'B48;;rX   c                    t         j                  j                  dd      j                         }|r|j	                  d      S | j
                  j                  dd      j                  d      d   j                         xs d}| j
                  j                  d      xs | j
                  j                  d	d
      }| d| S )NADMIN_PUBLIC_URLr   rR  zX-Forwarded-Protohttp,r   zX-Forwarded-HostHostz127.0.0.1:8099z://)r  environr   r   rS  r   split)handler
configuredprotohosts       rW   r   r   r  s     2B7==?J  %%OO 3V<BB3GJPPR\V\E??12cgoo6I6I&Rb6cDWCvrX   c                     t         j                  j                  dd      j                         }|sy|j	                  d       d|  dS )NCLIENTS_PUBLIC_URLr   rR  
/clientes/)r  r  r   r   rS  )rp   bases     rW   r   r   {  sC    ::>>.399;Dkk#z$q11rX   c                 (    t        |       xs d|  dS )Nzhttp://127.0.0.1:8092/clientes/rR  )r   rp   s    rW   r   r     s    '-Z3RSWRXXY1ZZrX   c                     d|  dS )Nr  z/index.htmlr>  r  s    rW   r   r     s    v[))rX   c                 J    | dz  dz  }|j                  dd       |t        z  S )Nrm   _dataTr   )r   SQLITE_FILENAME)r`   	data_roots     rW   sqlite_pathr    s,    J&0IOOD4O0&&rX   c                     t        j                  t        |       d      }t         j                  |_        |j                  d       |j                  d       t        |       |S )N
   timeoutzPRAGMA journal_mode=WALzPRAGMA foreign_keys=ON)sqlite3connectr  Rowrow_factoryexecuteensure_db_schemar`   conns     rW   open_dbr    sJ    ??;y12>D{{DLL*+LL)*TKrX   c                 &    | j                  d       y )Na  
        CREATE TABLE IF NOT EXISTS access_tokens (
          slug TEXT PRIMARY KEY,
          token TEXT NOT NULL,
          created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        );

        CREATE TABLE IF NOT EXISTS public_entries (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          slug TEXT NOT NULL,
          kind TEXT NOT NULL CHECK (kind IN ('rsvp', 'song')),
          fecha TEXT NOT NULL,
          payload_json TEXT NOT NULL,
          created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        );

        CREATE INDEX IF NOT EXISTS idx_public_entries_slug_kind_id
          ON public_entries (slug, kind, id);
        )executescript)r  s    rW   r  r    s    	rX   c                 t    t        |       5 }t        | |       t        | |       d d d        y # 1 sw Y   y xY wrP   )r  migrate_legacy_access_storemigrate_legacy_public_entriesr  s     rW   initialize_storager    s5    		 7t#It4%i67 7 7s   .7c                 h   | dz  dz  dz  }|j                         sy 	 t        |      }t	        |t
              sy |j                         D ]M  \  }}t        j                  t        |            s%|s(|j                  dt        |      t        |      f       O y # t        j                  $ r Y y w xY w)Nrm   _accesszclient-panel.jsonz?INSERT OR IGNORE INTO access_tokens (slug, token) VALUES (?, ?))r   r   r   r   
isinstancedictr   r"  r   r   r  )r`   r  rz   accessrp   r   s         rW   r  r    s    z!I-0CCD;;=4 fd#||~ eSY'ELLQTCJ'	  s   B B10B1c           	      8   | dz  dz  }|j                         sy t        |j                               D ]  }|j                         rt        j                  |j                        s3dD ]  \  }}|j                  d|j                  |f      j                         r3||z  }|j                         sI	 t        |      }t        |t              sf|D ]+  }t        |t              st        ||j                  ||       -   y # t        j                  $ r Y w xY w)Nrm   r  ))rsvpz
rsvps.json)songz
songs.jsonz@SELECT 1 FROM public_entries WHERE slug = ? AND kind = ? LIMIT 1)r   r7  iterdiris_dirr"  r   r   r  fetchoner   r   r   r  listr  store_public_entry_in_conn)	r`   r  r  slug_dirr  filenamerz   entriesr   s	            rW   r  r    s   J&0I9,,./ S (9(9(--(HN 	SND(||R% hj h&D;;=#D/ gt," Sgt,.tX]]D'RS	SS '' s   2DDDc           
          t        |      }t        |j                  d      xs t        j                  d            }||d<   | j                  d|||t        j                  |d      f       y )Nr   r   zj
        INSERT INTO public_entries (slug, kind, fecha, payload_json)
        VALUES (?, ?, ?, ?)
        Fr   )r  r   r   r   r   r  r   r   )r  rp   r  r   r   s        rW   r  r    sa    7mGG$J6I(JKEGGLL	 
tUDJJwUCDrX   c                     t        |       5 }|j                  d|f      j                         }|r|d   cd d d        S t        j                  d      }|j                  d||f       |cd d d        S # 1 sw Y   y xY w)N.SELECT token FROM access_tokens WHERE slug = ?r      z5INSERT INTO access_tokens (slug, token) VALUES (?, ?))r  r  r  secretstoken_urlsafe)r`   rp   r  rowr   s        rW   ensure_client_tokenr    s~    		 	tllKdWU^^`w<	 	 %%b)C5M	
 	 	 	s   (A2*A22A;c                     t        |       5 }|j                  d|f      j                         }d d d        r|d   nd}|rt        j                  t        |      |      st        dd      y # 1 sw Y   @xY w)Nr  r   r   i  zAcceso no autorizado)r  r  r  r  compare_digestr   rN   )r`   rp   r   r  r  expecteds         rW   r   r     sx    		 atllKdWU^^`a"s7|H711#e*hG455 Ha as   "A--A6c                 D   t        |       5 }|j                  d||f      j                         }d d d        g }D ]=  }	 t        j                  |d         }t        |t              s-|j                  |       ? |S # 1 sw Y   OxY w# t        j
                  $ r Y cw xY w)Nz
            SELECT payload_json
            FROM public_entries
            WHERE slug = ? AND kind = ?
            ORDER BY id
            payload_json)	r  r  fetchallr   r   r   r  r  rz  )r`   rp   r  r  rowsr  r  r   s           rW   read_public_entriesr    s    		 	t|| 4L
 (* 		 G $	jj^!45G gt$NN7#$ N%	 	 ## 		s   #A=B	=B	BBc                 `    t        |       5 }t        ||||       d d d        y # 1 sw Y   y xY wrP   )r  r  )r`   rp   r  r   r  s        rW   r  r    s/    		 >t"4tW=> > >s   $-c                    t        | j                        }|j                  ddg      d   xs dj                         }t	        |      dk  rt        dd      t	        |      dkD  rt        dd      	 t        |j                  d	d
g      d   xs d      }t        t        |d      d      }dt        ||      dS # t        $ r d}Y 2w xY w)Nqr   r   r   r   zEscribi al menos 2 caracteresx   zLa busqueda es demasiado largalimit6   ro      T)rw   tracks)r	   r~  r   r   r   rN   r   
ValueErrorrm  maxspotify_search_tracks)r   paramsr~  r  s       rW   r   r   !  s    fll#FZZbT"1%+224E
5zA~=>>
5zC>??FJJw.q16Q7 E1q!E"7u"EFF  s   3#B; ;C	C	c           	         t               }t        j                  j                  dd      j	                         j                         xs d}dt        | d|t        |      d      z   }t        |dd| i      }	 t        |d	
      5 }t        j                  |j                         j                  d            }d d d        j                  di       j                  dg       }
|
D cg c]  }|j                  d      st)        |      ! c}S # 1 sw Y   VxY w# t        $ r}t        |      }	t!        dd|	       d }~wt"        t$        t        j&                  f$ r}t!        dd|       d }~ww xY wc c}w )NSPOTIFY_MARKETARz"https://api.spotify.com/v1/search?track)r  typemarketr  AuthorizationzBearer )r   r  r  r     z$Spotify no respondio correctamente: zNo se pudo consultar Spotify: r  r   uri)spotify_access_tokenr  r  r   r   rN  r   r   r   r   r   r   r   r   r   spotify_error_messagerN   r   TimeoutErrorr   spotify_track_payload)r~  r  r   r  r   requestresponsedatarx   rR   r  r  s               rW   r  r  0  s^    "EZZ^^,d399;AACKtF
.U	< 2 C cOwug5F#GHGHWa( 	?H::hmmo44W=>D	? XXh#''4F6<QU		%@P!%(QQ	? 	? P'. DWINOOlD$8$89 H >ugFGGH RsH   :D 3D:D (E6?E6DD 	E3$D>>!E3E..E3c                  "   t         j                  j                  dd      j                         } t         j                  j                  dd      j                         }| r|st	        dd      t        t        j                               }t        d   rt        d   |dz   kD  r	t        d   S t        j                  |  d	| j                  d
            j                  d      }t        dt        ddi      j                  d
      d| ddd      }	 t        |d      5 }t        j                   |j#                         j                  d
            }d d d        j                  d      }	|	st	        dd      |	t        d<   |t        |j                  dd            z   t        d<   |	S # 1 sw Y   XxY w# t$        $ r}t'        |      }t	        dd|       d }~wt(        t*        t        j,                  f$ r}t	        dd|       d }~ww xY w)NSPOTIFY_CLIENT_IDr   SPOTIFY_CLIENT_SECRETi  zSpotify no esta configurador   r   <   :r   asciiz&https://accounts.spotify.com/api/token
grant_typeclient_credentialszBasic z!application/x-www-form-urlencoded)r  r   POST)r  r   methodr  r  r  zNo se pudo autenticar Spotify: z Spotify no devolvio access_token
expires_ini  )r  r  r   r   rN   r   r   SPOTIFY_TOKEN_CACHEbase64	b64encoder   r   r   r   r   r   r   r   r   r  r   r  r   )
	client_idclient_secretnowcredentialsr  r  r  rx   rR   r   s
             rW   r  r  G  s   

2B7==?IJJNN#:B?EEGMM;<<
diik
C>*/B</PSVY[S[/[">22""i[-#A#H#H#QRYYZabK0&:;<CCGL%k]3?
 GIWa( 	?H::hmmo44W=>D	? 88N+L@AA*6'(+c$((<2N.O(O%	? 	? K'. ?yIJJlD$8$89 I ?wGHHIs<   F6  3F*F6 *F3/F6 6	H?G!H:H		Hc                    	 t        j                  | j                         j                  d            }|j                  di       }t        |t              r1|j                  d      xs t        |j                  d      xs |       S |j                  d      xs t        |      xs t        |       S # t        $ r t        |       cY S w xY w)Nr   rx   rR   rQ   error_description)	r   r   r   r   	Exceptionr   r   r  r  )rx   r   spotify_errors      rW   r  r  l  s    **UZZ\009: KK,M-&  +Xs=3D3DX3N3WRW/XX;;*+Os=/AOSZO  5zs   2B2 2C	C	c                    | j                  d      xs i }| j                  dg       D cg c]&  }|j                  d      s|j                  dd      ( }}|j                  d      xs g }| j                  dd      | j                  dd      dj                  |      |j                  dd      |r|d   d	   nd| j                  d
d      | j                  d      xs i j                  dd      | j                  dd      dS c c}w )Nalbumartistsr   r   imagesidz, rL  r   r  external_urlsspotifyduration_msr   )r  r   artistr  imagespotify_urispotify_urlr  rC  )r  r  r  r  r   s        rW   r  r  w  s    IIg$"E49IIi4Lc&PVPZPZ[aPbvzz&"%cGcYYx &BFiib!		&"%))G$62&&,E""yy+		/28b==iLyy2	 	 ds   DDc                 P    t        | |d      }t        | |d      }t        ||d      S )Nr  r  local)r  build_dashboard_payload)r`   rp   local_rsvpslocal_songss       rW   r   r     s-    %iv>K%iv>K";WEErX   c           
         g }g }i }d}| D ]  }t        |j                  dd            j                         j                         }|dv }	|	r|j	                  |       R|j	                  |       	 |t        |j                  d      xs d      z  }t        |j                  dd      xs d      j                         xs d}
|j                  |
d      dz   ||
<    d	|t        |      |t        |      t        |      d
t        |j                               D cg c]
  \  }}||d c}}| |dS # t        t        f$ r |dz  }Y w xY wc c}}w )Nr   
asistenciar   >   no puedo	no asistonocantidadro   menuzSin menuT)confirmedResponsestotalPeopledeclinedResponses	songCount)r   count)rw   r   summarymenusrsvpssongs)r   r   r   r  rz  r   	TypeErrorr  r   r7  r   )r  r  r   	confirmeddeclinedr  total_peopler  r  is_declinedr  r   r  s                rW   r  r    s\   IHEL -r2399;AAC
 $CCOOC 	C
 3 8q99L 37762&4*5;;=Kiia(1,d- "%i.'!$XU	
 EK5;;=DYZ[T54%0Z  :& 	AL	 [s   /!D1E1EEc                 "   t        | |      }t        j                         }t        j                  |      }|j                  g d       |d   D ]y  }|j                  |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  d	d      g       { |j                  g        |j                  d
g       |j                  g d       |d   D ]y  }|j                  |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      g       { |j                         S )N)FechaNombre
AsistenciaCantidadMenuMensajer  r   r   nombrer  r  r  mensajezCanciones sugeridas)r$  CancionArtistaAlbumzSpotify URIzSpotify URLr  r  r  r  r  r  )r   ioStringIOcsvwriterwriterowr   getvalue)r`   rp   	dashboardr  r2  r  s         rW   r   r     s`    D1I[[]FZZF
OOTU! GGGR GGHb!GGL"%GGJ#GGFBGGIr"
 	 OOB
OO*+,
OOZ[! GGGR GGFBGGHb!GGGR GGM2&GGM2&
 	 ??rX   c                    t        | |      }g d}|d   D ]  }|j                  dt        |j                  dd             dt        |j                  dd             dt        |j                  dd             dt        |j                  d	d             dt        |j                  d
d             dt        |j                  dd             d        |j	                  g d       |d   D ]  }|j                  dt        |j                  dd             dt        |j                  dd             dt        |j                  dd             dt        |j                  dd             dt        |j                  dd             dt        |j                  dd             d        |j                  d       dj                  |      S )N)z/<html><head><meta charset="utf-8"></head><body>z<h1>Confirmaciones RSVP</h1><table border="1">zg<tr><th>Fecha</th><th>Nombre</th><th>Asistencia</th><th>Cantidad</th><th>Menu</th><th>Mensaje</th></tr>r  z<tr><td>r   r   z	</td><td>r*  r  r  r  r+  z
</td></tr>)z</table>z<h1>Canciones sugeridas</h1>r7  zm<tr><th>Fecha</th><th>Cancion</th><th>Artista</th><th>Album</th><th>Spotify URI</th><th>Spotify URL</th></tr>r  r  r  r  r  r  z</table></body></html>r3  )r   rz  html_escaper   r{  rD  )r`   rp   r5  r  r  s        rW   r   r     s    D1ID ! 

swww345 6swwx456 7sww|R89: ;swwz2678 9swwvr234 5swwy"567 8		


 	KK   ! 

swww345 6swwvr234 5swwx456 7swww345 6sww}b9:; <sww}b9:; <		


 	KK()99T?rX   c                     t        | xs d      j                  dd      j                  dd      j                  dd      j                  dd	      S )
Nr   &z&amp;<z&lt;>z&gt;"z&quot;)r   r  r:  s    rW   r8  r8    sH    u{##C199#vFNNsTZ[ccdgiqrrrX   c                     | dz  }|j                         sg S t        |j                               D cg c]2  }|j                         r |j                  |dz  j                         d4 c}S c c}w )Nrk   rt   )r   	hasConfig)r   r7  r  r  r   )r`   templates_rootrz   s      rW   r}   r}     sm    ,N  "	 >1134;;= $*>)F)F)HI  s   7A-c                    | dz  }|j                  d       t        |j                               D cg c]C  }|j                         r1|j                  j                  d      st        | |j                        E c}S c c}w )Nrm   Tr  _)r   r7  r  r  r   r{   r   )r`   r*  rz   s      rW   r~   r~     sr    z)L% <//12;;=!5!5c!: 	y$)),  s   AA>c                 Z   | dz  dz  }|j                         sg S g }t        |j                               D ]  }|j                         r t	        j
                  d|j                        s4t        |j                        }|dz  }i }d}|j                         r	 t        |      }d}|j                  |j                  ||j                  d|      |j                  dd	      t        |j                  |             ||j                  d
d  d        |S # t        j                  $ r d}Y w xY w)Nrm   r   r$  rt   FTr%   r   r   i)archiveNamerp   r   r   rz   configValid
archivedAt)r   r7  r  r  r   r   r   r  r   r   r   rz  r   r   r   )r`   r   archivesrz   rp   r   rq   config_valids           rW   r   r     s&   z)M9L 	H|++-. {{}BLL1RTXT]T]$^%dii0]*%";/# 	99JJy$/JJvr*((34'))CD/
 	, O '' %$%s   DD*)D*c                    t        | |      }|dz  }i }d}|j                         r	 t        |      }d}t        | |      }||j                  d|      |j                  dd      t        |      t        |j                  |             ||dz  j                         dt        |      d| d	| d

S # t        j                  $ r d}Y w xY w)Nrt   FTr%   r   r   r  z/cliente/?slug=z&token=)
rp   r   r   r   rz   rE  	hasAssetsisPublishedrr   clientPanelUrl)r   r   r   r   r   r  r   r   r   r   r   )r`   rp   rz   r   rq   rH  r   s          rW   r   r   +  s    i&D&KFL	!{+FL  	40E

9d+

62& &D$$Y/0#Xo--/(.+D6@  ## 	! L	!s   B. .CCc                     t        j                  d      } | j                  dd       | j                  dt        d       | j	                         }t        t              j                         j                  d	   }t        j                  |       t        |       |t        _        t        |j                  |j                   ft              }t#        d
|j                   d|j                    d       t#        d       |j%                          y )Nz3Servidor admin local para administrar invitaciones.)rp  z--hostz	127.0.0.1)defaultz--porti  )r  rN  ro   zAdmin: http://r  z/admin/zCortar con Ctrl+C)argparseArgumentParseradd_argumentr   
parse_argsr   __file__r)  r   r  chdirr  r_   r`   r   r  portprintserve_forever)parserargsr`   servers       rW   mainr[  E  s    $$1fgF
+6
sD9DX&&(003IHHYy!&L $))TYY!7FF	N499+Qtyyk
9:	

rX   __main__)_rO  r  r1  r/  r   r  r   r  r   r  r   ro  r   r9  http.serverr   r   pathlibr   urllib.errorr   r   urllib.parser	   r
   r   r   r   urllib.requestr   r   compiler"  r	  r  r  r  r  r   r  rN   r_   r   r  r  r   r  r   r   r   r   r;  rF  rI  rP  rT  rX  r   rZ  r^  r_  r[  rk  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   r8  r}   r~   r   r   r[  rY   r>  rX   rW   <module>rc     s     
 	  	 	     * E  , H H + "**,
-(= "('); 3 l" "w+ wt	)8"!M9077;
[65>#4<2[*'07$S6

6,>
GR."JP FD:%Ps<4" zF rX   