+
    ÞDjP  ã                   óš  € R t ^ RIt^ RIt^ RIt^ RIt^ RIHtHtHtH	t	H
t
HtHtHt ^ RIHtHtHtHtHtHt ]! R]4      tR]R]R]R]R	]R
]P.                  P0                  /t^tR tR tR tR tR tR t R t!R t"R t#R t$]PK                  R4      R 4       t&]PK                  RRR.R7      R 4       t']PK                  R4      R 4       t(]PK                  RRR.R7      R 4       t)R  t*]PK                  R!R.R7      R" 4       t+]PK                  R#4      R$ 4       t,]PK                  R%4      R& 4       t-]PK                  R'R.R7      R( 4       t.R# ))z™
OTA Update Provider: manage multiple Arduino-style projects and .bin firmware uploads.
Uses shared MySQL config (config.py); stores .bin files on disk.
N)Ú	BlueprintÚrequestÚredirectÚurl_forÚrender_templateÚ	send_fileÚflashÚResponse)Ú
MYSQL_HOSTÚ
MYSQL_PORTÚMYSQL_DATABASEÚ
MYSQL_USERÚMYSQL_PASSWORDÚOTA_FIRMWARE_DIRÚotaÚhostÚportÚuserÚpasswordÚdatabaseÚcursorclassc                óþ   € T ;'       g    RP                  4       p  \        ;QJ d#    . R V P                  R4       4       F  NK  	  5# ! R V P                  R4       4       4      #   \        \        3 d    Ru # i ; i)z7Parse 'X.Y.Z' or 'X.Y' to tuple of ints for comparison.Ú c              3   óf   "  € T F'  qP                  4       '       g   K  \        V4      x € K)  	  R # 5i)N)ÚstripÚint)Ú.0Úxs   & Ú6/workplace/Workspace/Software/Server/blueprints/ota.pyÚ	<genexpr>Ú parse_version.<locals>.<genexpr>&   s   é € Ð?¡\ ·W±W·Y”V”S˜—V�V£\ùs   ‚1ž1Ú.)é    )r   ÚtupleÚsplitÚ
ValueErrorÚAttributeError)Úss   &r   Úparse_versionr(   "   sd   € à	
�ˆˆb�‰Ó€AðßŒuÑ? Q§W¡W¨S¤\Ó?�uÐ?ˆuÑ? Q§W¡W¨S¤\Ó?Ó?Ð?øÜœÐ'ô ØŠðús   œ
A& §!A& Á	A& Á&A<Á;A<c                ó  € \        V 4      p\        V4      p\        \        \        V4      \        V4      4      4       FI  pV\        V4      8  d	   W$,          M^ pV\        V4      8  d	   W4,          M^ pWV8  d    R# WV8”  g   KH   R# 	  R# )z7True if a is strictly less than b (e.g. 1.0.0 < 1.0.1).TF)r(   ÚrangeÚmaxÚlen)Úa_strÚb_strÚaÚbÚiÚaiÚbis   &&     r   Úversion_lessr4   +   so   € ä�eÓ€AÜ�eÓ€AÜ”3”s˜1“vœs 1›vÓ&Ö'ˆØœ˜Q›”ZˆQŽT QˆØœ˜Q›”ZˆQŽT QˆØŒ7ÚØŽ7Úñ (ñ ó    c                  ó6   € \         P                  ! R/ \        B # )N© )ÚpymysqlÚconnectÚOTA_DB_CONFIGr7   r5   r   Úget_dbr;   9   s   € Ü�?Š?Ñ+œ]Ñ+Ð+r5   c                 ó¶  € V P                  4       ;_uu_ 4       pVP                  R 4       VP                  R4       VP                  R4        VP                  R4        VP                  R4        VP                  R4        VP                  R4        VP                  R4       RRR4       V P	                  4        R#   \        P                   d     Lyi ; i  \        P                   d     L‚i ; i  \        P                   d     L‹i ; i  \        P                   d     L”i ; i  \        P                   d     L�i ; i  + '       g   i     L©; i)	a  
            CREATE TABLE IF NOT EXISTS ota_projects (
                id INT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255) NOT NULL,
                slug VARCHAR(255) NOT NULL UNIQUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        aÚ  
            CREATE TABLE IF NOT EXISTS ota_firmware (
                id INT AUTO_INCREMENT PRIMARY KEY,
                project_id INT NOT NULL,
                version VARCHAR(64) NOT NULL,
                original_filename VARCHAR(255) NOT NULL,
                stored_path VARCHAR(512) NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (project_id) REFERENCES ota_projects(id) ON DELETE CASCADE
            )
        aü  
            CREATE TABLE IF NOT EXISTS ota_update_log (
                id INT AUTO_INCREMENT PRIMARY KEY,
                project_id INT NOT NULL,
                project_name VARCHAR(255) NULL,
                project_slug VARCHAR(255) NULL,
                version VARCHAR(64) NOT NULL,
                client_ip VARCHAR(64) NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (project_id) REFERENCES ota_projects(id) ON DELETE CASCADE
            )
        zDALTER TABLE ota_update_log ADD COLUMN project_name VARCHAR(255) NULLzDALTER TABLE ota_update_log ADD COLUMN project_slug VARCHAR(255) NULLz?ALTER TABLE ota_update_log ADD COLUMN node_id VARCHAR(255) NULLzAALTER TABLE ota_update_log ADD COLUMN node_name VARCHAR(255) NULLzCALTER TABLE ota_update_log ADD COLUMN from_version VARCHAR(64) NULLN)ÚcursorÚexecuter8   ÚOperationalErrorÚcommit)ÚconnÚcurs   & r   Úensure_ota_tablesrC   =   s9  € Ø	�‰�Œ˜#Ø�‰ð ô 	ð 	�‰ð 
ô 
	ð 	�‰ð ô 	ð	Ø�K‰KÐ^Ô_ð	Ø�K‰KÐ^Ô_ð	Ø�K‰KÐYÔZð	Ø�K‰KÐ[Ô\ð	Ø�K‰KÐ]Ô^÷c 
ðh 	‡K�K†Møô% ×'Ñ'ô 	Ùð	ûô ×'Ñ'ô 	Ùð	ûô ×'Ñ'ô 	Ùð	ûô ×'Ñ'ô 	Ùð	ûô ×'Ñ'ô 	Ùð	ú÷e 
�ús¡   ™4EÁCÁ CÁ2C7ÂDÂD-ÃCÃEÃCÃEÃC4Ã1EÃ3C4Ã4EÃ7DÄEÄDÄEÄD*Ä'EÄ)D*Ä*EÄ-EÅEÅEÅEÅE	c                óè   € T ;'       g    RP                  4       P                  4       p\        P                  ! RRV4      p\        P                  ! RRV4      P                  R4      pT;'       g    R# )zBLowercase, replace spaces with -, keep only alphanumeric and dash.r   z[^a-z0-9\-]Ú-z-+Úproject)r   ÚlowerÚreÚsub)Únamer'   s   & r   ÚslugifyrK   u   sZ   € à	�ˆ�×ÑÓ×"Ñ"Ó$€AÜ
�Šˆ~˜s AÓ&€AÜ
�Šˆu�c˜1Ó×#Ñ# CÓ(€AØ�>ˆ>�	Ðr5   c                óî   € \        V 4      p\        V4      ^8¼  d   V^ ,          V^,          V^,          3# \        V4      ^8X  d   V^ ,          V^,          ^ 3# \        V4      ^8X  d   V^ ,          ^ ^ 3# R# )z]Return (major, minor, patch) with at least 3 components. E.g. 1.0.1 -> (1,0,1), 1 -> (1,0,0).)r"   r"   r"   )r(   r,   )r'   Úts   & r   Ú_parse_version_partsrN   }   sp   € ä�aÓ€AÜ
ˆ1ƒv�„{Ø�!•�a˜•d˜A˜a�DÐ!Ð!Ü
ˆ1ƒv�„{Ø�!•�a˜•d˜AˆÐÜ
ˆ1ƒv�„{Ø�!•�a˜ˆ|ÐØÐr5   c                óø   € T ;'       g    RP                  4       '       g   R# \        V 4      w  rpV^,          pV^	8:  d   V RV RV 2# ^ pV^,          pV^	8:  d   V RV RV 2# ^ pV^,          pV RV RV 2# )z[Next semantic version: 1.0.1 -> 1.0.2; 1.0.9 -> 1.1.0; 1.9.9 -> 2.0.0. No version -> 1.0.0.r   z1.0.0r!   )r   rN   )Úversion_strÚmajorÚminorÚpatchs   &   r   Únext_version_fromrT   ‰   s£   € à×Ð˜2×$Ñ$×&Ò&ÙÜ.¨{Ó;Ñ€E�%Ø	ˆQ…J€EØ�„zØ�˜˜%˜  % Ð)Ð)Ø€EØ	ˆQ…J€EØ�„zØ�˜˜%˜  % Ð)Ð)Ø€EØ	ˆQ…J€EØˆW�A�e�W˜A˜e˜WÐ%Ð%r5   c                óþ  € V P                  4       ;_uu_ 4       pVP                  RV34       VP                  4       pRRR4       X Uu. uF\  qDP                  R4      ;'       g    RP	                  4       '       g   K3  VP                  R4      ;'       g    RP	                  4       NK^  	  ppV'       g   R# V^ ,          pVR,           F  p\        Wg4      '       g   K  TpK  	  V#   + '       g   i     L²; iu upi )zRReturn the latest (semantically greatest) version string for the project, or None.z6SELECT version FROM ota_firmware WHERE project_id = %sNÚversionr   ºé   NN)r=   r>   ÚfetchallÚgetr   r4   )rA   Ú
project_idrB   ÚrowsÚrÚversionsÚlatestÚvs   &&      r   Úget_latest_version_strra   š   sÆ   € à	�‰�Œ˜#Ø�‰ØDØˆMô	
ð �|‰|‹~ˆ÷ 
ñ ;?Óc¹$°QÇ5Á5ÈÓCS×CYÐCYÐWY×B`ÑB`×BbÔ0�—‘�yÓ!×'Ð' R×.Ñ.Ö0¹$€HÐcßÙØ�a�[€FØ�b�\ˆ\ˆÜ˜×"Ô"ØŠFñ ð €M÷ 
�üò ds#   ™$C'Á	C:Á%C:Á>C:ÂC:Ã'C7	c                ó.   € \        W4      p\        V4      # )zrReturn next version string for project: last version + 1 (semantic: 1.0.1->1.0.2, 1.0.9->1.1.0), or 1.0.0 if none.)ra   rT   )rA   r[   r_   s   && r   Únext_versionrc   ¬   s   € ä# DÓ5€FÜ˜VÓ$Ð$r5   c                  ó>   € \         P                  ! \        R R7       R# )T©Úexist_okN)ÚosÚmakedirsr   r7   r5   r   Ú_ensure_firmware_dirri   ²   s   € Ü‡K‚KÔ ¨4×0r5   Ú/c                 óØ  € \        4       p  \        V 4       V P                  4       ;_uu_ 4       pVP                  R4       VP	                  4       pV F6  pVP                  R4      '       g   K  VR,          P                  4       VR&   K8  	  RRR4       V P                  4       ;_uu_ 4       pVP                  R4       VP	                  4       pV F6  pVP                  R4      '       g   K  VR,          P                  4       VR&   K8  	  RRR4       \        RXXR7      V P                  4        #   + '       g   i     L¬; i  + '       g   i     LA; i  T P                  4        i ; i)zDashboard: list all projects.zASELECT id, name, slug, created_at FROM ota_projects ORDER BY nameÚ
created_atNa³  SELECT l.id, l.version, l.from_version, l.client_ip, l.node_id, l.node_name, l.created_at,
                          COALESCE(NULLIF(TRIM(l.project_name), ''), p.name) AS project_name,
                          COALESCE(NULLIF(TRIM(l.project_slug), ''), p.slug) AS project_slug
                   FROM ota_update_log l
                   JOIN ota_projects p ON l.project_id = p.id
                   ORDER BY l.created_at DESC LIMIT 50zota/index.html)ÚprojectsÚ
update_log)	r;   rC   r=   r>   rY   rZ   Ú	isoformatr   Úclose)rA   rB   rm   Úprn   Úes         r   Úindexrs   ¶   s
  € ô ‹8€DðÜ˜$ÔØ�[‰[�]Œ]˜cØ�K‰KØSôð —|‘|“~ˆHÛ�Ø—5‘5˜×&Ô&Ø&'¨¥o×&?Ñ&?Ó&A�A�l“Oñ ÷ ð �[‰[�]Œ]˜cØ�K‰Kð:ôð Ÿ™›ˆJÛ�Ø—5‘5˜×&Ô&Ø&'¨¥o×&?Ñ&?Ó&A�A�l“Oñ  ÷ ô Ð/¸(ÈzÔZà�
‰
�÷/ �]ú÷ �]ûð 	�
‰
�úsF   Œ#E ¯;D1Á/D1Â E Â-;EÃ-EÄE Ä1E	Ä<E ÅE	ÅE ÅE)z/project/newÚGETÚPOST)Úmethodsc                 ó  € \         P                  R8X  d   \        R4      # \         P                  P	                  R4      ;'       g    RP                  4       p V '       g   \        R4       \        R4      R3# \        V 4      p\        4       p \        V4       VP                  4       ;_uu_ 4       pVP                  RV34       VP                  4       '       d7   \        RV R	24       \        R4      R3uuR
R
R
4       VP                  4        # VP                  RW34       R
R
R
4       VP                  4        \        \!        RVR7      4      VP                  4        #   + '       g   i     LF; i  TP                  4        i ; i)z$Create a new project (name -> slug).rt   zota/project_new.htmlrJ   r   úProject name is required.é�  z+SELECT id FROM ota_projects WHERE slug = %szA project with slug 'z*' already exists. Choose a different name.Nz5INSERT INTO ota_projects (name, slug) VALUES (%s, %s)úota.project_detail©Úslug)r   Úmethodr   ÚformrZ   r   r   rK   r;   rC   r=   r>   Úfetchonerp   r@   r   r   )rJ   r|   rA   rB   s       r   Úproject_newr€   Ö   s*  € ô ‡~�~˜ÔÜÐ5Ó6Ð6Ü�L‰L×Ñ˜VÓ$×*Ð*¨×1Ñ1Ó3€DßÜÐ)Ô*ÜÐ5Ó6¸Ð;Ð;Ü�4‹=€DÜ‹8€DðÜ˜$ÔØ�[‰[�]Œ]˜cØ�K‰KÐEÈÀwÔOØ�|‰|�~Š~ÜÐ-¨d¨VÐ3]Ð^Ô_Ü&Ð'=Ó>ÀÐC÷	 ‰]ð 	�
‰
�ð �K‰KØGØ�ô÷ ð 	�‰ŒÜœÐ 4¸4Ô@ÓAà�
‰
�÷ �]ûð 	�
‰
�ús1   Â#E5 Â1AE"Ã6
E5 ÄE"Ä$-E5 Å"E2	Å-E5 Å5Fz/project/<slug>c           	     ó–  € T ;'       g    RP                  4       p \        4       p \        V4       VP                  4       ;_uu_ 4       pVP	                  RV 34       VP                  4       pRRR4       X'       g0   \        R4       \        \        R4      4      VP                  4        # VP                  4       ;_uu_ 4       pVP	                  RVR,          34       VP                  4       pV F6  pVP                  R4      '       g   K  VR,          P                  4       VR&   K8  	  RRR4       \        WR,          4      pVP                  4       ;_uu_ 4       pVP	                  R	VR
,          VR,          VR,          34       VP                  4       pV F6  pVP                  R4      '       g   K  VR,          P                  4       VR&   K8  	  RRR4       \        RVXVXR7      VP                  4        #   + '       g   i     EL˜; i  + '       g   i     Lí; i  + '       g   i     LW; i  TP                  4        i ; i)zBProject detail: latest firmware row only, upload form, update log.r   ú7SELECT id, name, slug FROM ota_projects WHERE slug = %sNúProject not found.ú	ota.indexz…SELECT id, version, original_filename, created_at
                   FROM ota_firmware WHERE project_id = %s ORDER BY id DESC LIMIT 1Úidrl   aZ  SELECT id, version, from_version, client_ip, node_id, node_name, created_at,
                          COALESCE(NULLIF(TRIM(project_name), ''), %s) AS project_name,
                          COALESCE(NULLIF(TRIM(project_slug), ''), %s) AS project_slug
                   FROM ota_update_log WHERE project_id = %s ORDER BY created_at DESC LIMIT 30rJ   r|   zota/project_detail.html)rF   Úfirmwarerc   Úproject_update_log)r   r;   rC   r=   r>   r   r   r   r   rp   rY   rZ   ro   rc   r   )	r|   rA   rB   rF   r†   ÚfÚnext_verr‡   rr   s	   &        r   Úproject_detailrŠ   ò   sÆ  € ð �JˆJ�B×ÑÓ€DÜ‹8€Dð*Ü˜$ÔØ�[‰[�]Œ]˜cØ�K‰KØIØ�ôð —l‘l“nˆG÷ ÷ ÜÐ&Ô'ÜœG KÓ0Ó1ð@ 	�
‰
�ð? �[‰[�]Œ]˜cØ�K‰KðWà˜•Ð ôð
 —|‘|“~ˆHÛ�Ø—5‘5˜×&Ô&Ø&'¨¥o×&?Ñ&?Ó&A�A�l“Oñ ÷ ô   ¨d¥mÓ4ˆØ�[‰[�]Œ]˜cØ�K‰Kðbð ˜• '¨&¥/°7¸4µ=ÐAôð "%§¡£ÐÛ'�Ø—5‘5˜×&Ô&Ø&'¨¥o×&?Ñ&?Ó&A�A�l“Oñ (÷ ô Ø%ØØØ!Ø1ô
ð 	�
‰
�÷Q �]ˆ]ú÷ �]ú÷ �]ûð( 	�
‰
�úsm   ¦#H6 Á	$G<Á-H6 Á=H6 Â,H6 ÃAHÄHÄ+2H6 ÅAH#Æ6H#ÇH6 Ç<H	È	H6 ÈH 	ÈH6 È#H3	È.H6 È6Iz/project/<slug>/editc           
     óH  € T ;'       g    RP                  4       p \        4       p \        V4       VP                  4       ;_uu_ 4       pVP	                  RV 34       VP                  4       pRRR4       X'       g0   \        R4       \        \        R4      4      VP                  4        # \        P                  R8X  d   \        RVR7      VP                  4        # \        P                  P                  R	4      ;'       g    RP                  4       p\        \        P                  P                  R
4      ;'       g
    VR
,          4      pV'       g+   \        R4       \        RVR7      R3VP                  4        # WP8w  Ed¾   VP                  4       ;_uu_ 4       pVP	                  RWSR,          34       VP                  4       '       d9   \        RV R24       \        RVR7      R3uuRRR4       VP                  4        #  RRR4       \         P"                  P%                  \&        V 4      p\         P"                  P%                  \&        V4      p\         P"                  P)                  V4      '       d   \         P*                  ! Wg4       VP                  4       ;_uu_ 4       pVP	                  RVR,          34       VP-                  4        Fa  p\         P"                  P%                  V\         P"                  P/                  VR,          4      4      p	VP	                  RW˜R,          34       Kc  	  RRR4       VP                  4       ;_uu_ 4       pVP	                  RWEVR,          34       RRR4       VP1                  4        \        R4       \        \        RVR7      4      VP                  4        #   + '       g   i     EL]; i  + '       g   i     ELÕ; i  + '       g   i     Lµ; i  + '       g   i     LŒ; i  TP                  4        i ; i)zEdit project name and slug.r   r‚   Nrƒ   r„   rt   zota/project_edit.html)rF   rJ   r|   rx   ry   z8SELECT id FROM ota_projects WHERE slug = %s AND id != %sr…   zSlug 'z%' is already used by another project.z>SELECT id, stored_path FROM ota_firmware WHERE project_id = %sÚstored_pathz6UPDATE ota_firmware SET stored_path = %s WHERE id = %sz:UPDATE ota_projects SET name = %s, slug = %s WHERE id = %szProject updated.rz   r{   )r   r;   rC   r=   r>   r   r   r   r   rp   r   r}   r   r~   rZ   rK   rg   ÚpathÚjoinr   ÚisdirÚrenamerY   Úbasenamer@   )
r|   rA   rB   rF   rJ   Únew_slugÚold_dirÚnew_dirÚrowÚnew_paths
   &         r   Úproject_editr—   $  sî  € ð �JˆJ�B×ÑÓ€DÜ‹8€Dð-Ü˜$ÔØ�[‰[�]Œ]˜cØ�K‰KØIØ�ôð —l‘l“nˆG÷ ÷ ÜÐ&Ô'ÜœG KÓ0Ó1ðF 	�
‰
�ôE �>‰>˜UÔ"Ü"Ð#:ÀGÔLðB 	�
‰
�ôA —‘× Ñ  Ó(×.Ð.¨B×5Ñ5Ó7ˆÜœ7Ÿ<™<×+Ñ+¨FÓ3×FÐF°w¸vµÓGˆßÜÐ-Ô.Ü"Ð#:ÀGÔLÈcÐQð8 	�
‰
�ð7 ÕØ—‘—” #Ø—‘ÐVÐYaÐkoÕcpÐXqÔrØ—<‘<—>’>Ü˜F 8 *Ð,QÐRÔSÜ*Ð+BÈGÔTÐVYÐY÷	 ‘ð4 	�
‰
�ð1 "÷ ô
 —g‘g—l‘lÔ#3°TÓ:ˆGÜ—g‘g—l‘lÔ#3°XÓ>ˆGÜ�w‰w�}‰}˜W×%Ò%Ü—	’	˜'Ô+Ø—‘—” #Ø—‘ØTØ˜T•]Ð$ôð Ÿ<™<ž>�CÜ!Ÿw™wŸ|™|¨H´b·g±g×6FÑ6FÀsÈ=ÕGYÓ6ZÓ[�HØ—K‘KÐ XÐ[cÐimÕenÐZoÖpñ *÷ ð �[‰[�]Œ]˜cØ�K‰KØLØ ¨¥Ð/ô÷ ð
 	�‰ŒÜÐ Ô!ÜœÐ 4¸8ÔDÓEà�
‰
�÷W �]ˆ]ú÷" —�ú÷ —ú÷ �]ûð 	�
‰
�ús¤   ¦#P Á	$OÁ-P Á=P Â,!P Ã%P Ä;P Å P ÅP Æ P ÆAOÇ-
P È	A4P É>.P Ê,BO)Ì< P ÍO<Í88P ÏO	Ï	P ÏO&	Ï 	P Ï)O9	Ï4P Ï<P	ÐP ÐP!c                óœ   € \         P                  P                  T ;'       g    R4      p \        P                  ! RRV 4      p T ;'       g    R# )z0Keep only safe characters for a stored filename.úfirmware.binú[^a-zA-Z0-9._\-]Ú_)rg   r�   r‘   rH   rI   )rJ   s   &r   Ú_sanitize_filenamerœ   Y  s?   € ä�7‰7×Ñ˜D×2Ð2 NÓ3€DÜ�6Š6Ð% s¨DÓ1€DØ×!Ð!�>Ð!r5   z/project/<slug>/uploadc           	     ó  € T ;'       g    RP                  4       p \        P                  P                  R4      p\        P                  P                  R4      ;'       g    RP                  4       p\        4       p \        V4       VP                  4       ;_uu_ 4       pVP                  RV 34       VP                  4       pRRR4       X'       g0   \        R4       \        \        R4      4      VP                  4        # V'       d   VP                  '       g2   \        R4       \        \        R	V R
7      4      VP                  4        # VP                  P                  4       P!                  R4      '       g2   \        R4       \        \        R	V R
7      4      VP                  4        # \        P"                  '       da   \        P"                  \$        R,          R,          8”  d:   \        R\$         R24       \        \        R	V R
7      4      VP                  4        # V'       d   TM\'        W5R,          4      p\)        4        \*        P,                  P/                  \0        V 4      p\*        P2                  ! VRR7       \4        P6                  ! RRV4      p\9        VP                  4      p	V RV	 2p
V
P                  4       P!                  R4      '       g
   V
R,          p
\*        P,                  P/                  Wz4      pVP;                  V4       \*        P,                  P/                  W
4      pVP                  4       ;_uu_ 4       pVP                  RVR,          WaP                  V34       RRR4       VP=                  4        \        RVP                   RV R24       \        \        R	V R
7      4      VP                  4        #   + '       g   i     EL; i  + '       g   i     Lv; i  TP                  4        i ; i)zEUpload a .bin file; optional version override. A file must be chosen.r   ÚfilerV   r‚   Nrƒ   r„   z$Please choose a .bin file to upload.rz   r{   z.binzOnly .bin files are allowed.i   zFile too large (max z MB).r…   Tre   rš   r›   zyINSERT INTO ota_firmware (project_id, version, original_filename, stored_path)
                   VALUES (%s, %s, %s, %s)z	Uploaded z as version r!   )r   r   ÚfilesrZ   r~   r;   rC   r=   r>   r   r   r   r   rp   ÚfilenamerG   ÚendswithÚcontent_lengthÚMAX_UPLOAD_MBrc   ri   rg   r�   rŽ   r   rh   rH   rI   rœ   Úsaver@   )r|   rž   Úversion_overriderA   rB   rF   rV   Úproject_dirÚsafe_versionÚoriginal_safeÚstored_namerŒ   Úrel_paths   &            r   Úuploadr«   `  sß  € ð �JˆJ�B×ÑÓ€DÜ�=‰=×Ñ˜VÓ$€DÜŸ™×(Ñ(¨Ó3×9Ð9°r×@Ñ@ÓBÐÜ‹8€Dð*Ü˜$ÔØ�[‰[�]Œ]˜cØ�K‰KØIØ�ôð —l‘l“nˆG÷ ÷ ÜÐ&Ô'ÜœG KÓ0Ó1ð@ 	�
‰
�÷? ˜4Ÿ=Ÿ=˜=ÜÐ8Ô9ÜœGÐ$8¸tÔDÓEð: 	�
‰
�ð9 �}‰}×"Ñ"Ó$×-Ñ-¨f×5Ò5ÜÐ0Ô1ÜœGÐ$8¸tÔDÓEð4 	�
‰
�ô3 ×!×!Ð!¤g×&<Ñ&<¼}ÈtÕ?SÐVZÕ?ZÔ&ZÜÐ(¬¨°uÐ=Ô>ÜœGÐ$8¸tÔDÓEð. 	�
‰
�÷- '7Ñ"¼LÈÐW[Í}Ó<]ˆÜÔÜ—g‘g—l‘lÔ#3°TÓ:ˆÜ
�Š�K¨$Õ/Ü—v’vÐ1°3¸Ó@ˆÜ*¨4¯=©=Ó9ˆØ%˜ a¨ Ð7ˆØ× Ñ Ó"×+Ñ+¨F×3Ò3Ø˜6Õ!ˆKÜ—g‘g—l‘l ;Ó<ˆØ�	‰	�+ÔÜ—7‘7—<‘< Ó2ˆØ�[‰[�]Œ]˜cØ�K‰Kð.à˜• ¯©¸ÐAô÷ ð 	�‰ŒÜ�	˜$Ÿ-™-˜¨°W°I¸QÐ?Ô@ÜœÐ 4¸4Ô@ÓAà�
‰
�÷Q �]ˆ]ú÷< �]ûð 	�
‰
�ús{   Á<#O6 Â$OÃO6 ÃO6 ÄO6 Ä
O6 Ä O6 Å.O6 Å< O6 Æ-O6 ÇAO6 È#O6 È+D#O6 Í'O#Í5A	O6 ÏO 	Ï	O6 Ï#O3	Ï.O6 Ï6Pz/project/<slug>/updatec                óN  € T ;'       g    RP                  4       p \        P                  P                  R4      ;'       g    RP                  4       pV'       g   \	        RR7      # \        4       p VP                  4       ;_uu_ 4       pVP                  RV 34       VP                  4       pRRR4       X'       g   \	        RRR7      VP                  4        # VP                  4       ;_uu_ 4       pVP                  R	VR
,          34       VP                  4       pRRR4       X'       g   \	        RRR7      VP                  4        # V^ ,          pVR,           F'  p\        VR,          VR,          4      '       g   K%  TpK)  	  VP                  R4      ;'       g    RP                  4       p\        P                  P                  \        VR,          4      p	\        P                  P!                  V	4      '       g   \	        RRR7      VP                  4        # \        W4      '       Ed�   \        P"                  ;'       g    RP                  4       ;'       g    Rp
\        P                  P                  R4      ;'       g    RP                  4       ;'       g    Rp\        P                  P                  R4      ;'       g    RP                  4       ;'       g    RpVP                  4       ;_uu_ 4       pVP                  RVR
,          WŠ34       VP                  4       f\   TP                  RVR
,          VP                  R4      ;'       g    RVP                  R4      ;'       g    RY�;'       g    RW«V34       RRR4       VP%                  4        \'        V	RRRR7      VP                  4        # \	        RR7      VP                  4        #   + '       g   i     EL3; i  + '       g   i     EL×; i  + '       g   i     Lƒ; i  TP                  4        i ; i)zaESP32 OTA: GET with header x-esp32-version. Return 200+bin if server has newer, else 304, or 404.r   zx-esp32-versioni0  )Ústatusr‚   NzNo project or firmwarei”  zkSELECT id, version, stored_path FROM ota_firmware
                   WHERE project_id = %s ORDER BY id DESCr…   rW   rV   rŒ   zx-esp32-node-idzx-esp32-node-namezÒSELECT 1 FROM ota_update_log
                       WHERE project_id = %s AND version = %s AND (client_ip <=> %s)
                       AND created_at > NOW() - INTERVAL 2 MINUTE
                       LIMIT 1z¼INSERT INTO ota_update_log (project_id, project_name, project_slug, version, from_version, client_ip, node_id, node_name)
                           VALUES (%s, %s, %s, %s, %s, %s, %s, %s)rJ   r|   úapplication/octet-streamTr™   )ÚmimetypeÚas_attachmentÚdownload_name)r   r   ÚheadersrZ   r	   r;   r=   r>   r   rp   rY   r4   rg   r�   rŽ   r   ÚisfileÚremote_addrr@   r   )r|   Údevice_versionrA   rB   rF   Úfirmware_rowsr_   r•   Úserver_versionÚ	full_pathÚ	client_ipÚnode_idÚ	node_names   &            r   Úproject_updater¼   ”  s>  € ð �JˆJ�B×ÑÓ€DÜ—o‘o×)Ñ)Ð*;Ó<×BÐBÀ×IÑIÓK€NçÜ˜sÔ#Ð#Ü‹8€Dð7Ø�[‰[�]Œ]˜cØ�K‰KØIØ�ôð —l‘l“nˆG÷ ÷ ÜÐ4¸SÔAð^ 	�
‰
�ð] �[‰[�]Œ]˜cØ�K‰Kð=à˜•Ð ôð
  ŸL™L›NˆM÷ ÷ ÜÐ4¸SÔAðL 	�
‰
�ðI ˜qÕ!ˆØ  ×$Ð$ˆCÜ˜F 9Õ-¨s°9­~×>Ô>Ø’ñ %ð !Ÿ*™* YÓ/×5Ð5°2×<Ñ<Ó>ˆÜ—G‘G—L‘LÔ!1°6¸-Õ3HÓIˆ	Ü�w‰w�~‰~˜i×(Ò(ÜÐ4¸SÔAð: 	�
‰
�ô9 ˜×7Ó7Ü ×,Ñ,×2Ð2°×9Ñ9Ó;×CÐC¸tˆIÜ—‘×*Ñ*Ð+<Ó=×CÐCÀ×JÑJÓL×TÐTÐPTˆGÜ Ÿ™×,Ñ,Ð-@ÓA×GÐGÀR×NÑNÓP×XÐXÐTXˆIà—‘—” #Ø—‘ð"ð ˜T•] NÐ>ôð —<‘<“>Ò)Ø—K‘KðFà  �¨¯©°FÓ(;×(AÐ(A¸rÀ7Ç;Á;ÈvÓCV×C\ÐC\ÐZ\Ð^l÷  oEð  oEð  AEð  GPð  [dð  eô÷ ð �K‰KŒMÜØØ3Ø"Ø,ô	ð 	�
‰
�ô ˜sÔ#à�
‰
�÷m �]ˆ]ú÷ �]ˆ]ú÷. —ûð. 	�
‰
�úsÂ   Á1P Â	$OÂ-P Â=P ÃP Ã2+O+ÄP Ä-P Å
2P ÆP ÆA P È P È(P ÉP É(P ÊP Ê(P ËP Ë P Ë;AO?ÍO?Í/
O?Í:
O?Î&P Î;P ÏO(	Ï"	P Ï+O<	Ï6	P Ï?P	Ð
P ÐP$z*/project/<slug>/firmware/<int:firmware_id>c                ó  € T ;'       g    RP                  4       p \        4       p VP                  4       ;_uu_ 4       pVP                  RW34       VP	                  4       pRRR4       X'       g0   \        R4       \        \        R4      4      VP                  4        # \        P                  P                  \        VR,          4      p\        P                  P                  V4      '       g2   \        R4       \        \        RV R	7      4      VP                  4        # \        VR
\        P                  P                  VR,          4      RR7      VP                  4        #   + '       g   i     EL; i  TP                  4        i ; i)z+Download the .bin file for Arduino clients.r   úºSELECT f.id, f.stored_path, p.slug
                   FROM ota_firmware f
                   JOIN ota_projects p ON f.project_id = p.id
                   WHERE p.slug = %s AND f.id = %sNúFirmware not found.r„   rŒ   zFile no longer exists on disk.rz   r{   Tr®   )r°   r±   r¯   )r   r;   r=   r>   r   r   r   r   rp   rg   r�   rŽ   r   r³   r   r‘   ©r|   Úfirmware_idrA   rB   r•   r¸   s   &&    r   Údownload_firmwarerÂ   ×  s   € ð �JˆJ�B×ÑÓ€DÜ‹8€DðØ�[‰[�]Œ]˜cØ�K‰Kð6ð Ð#ôð —,‘,“.ˆC÷ ÷ ÜÐ'Ô(ÜœG KÓ0Ó1ð 	�
‰
�ô —G‘G—L‘LÔ!1°3°}Õ3EÓFˆ	Ü�w‰w�~‰~˜i×(Ò(ÜÐ2Ô3ÜœGÐ$8¸tÔDÓEð 	�
‰
�ô ØØÜŸ'™'×*Ñ*¨3¨}Õ+=Ó>Ø/ô	
ð 	�
‰
�÷/ �]ˆ]ûð. 	�
‰
�ús;   ¦E9 ¾$E%Á"E9 Á2E9 Â!AE9 Ã1 E9 Ä"2E9 Å%E6	Å0	E9 Å9Fz1/project/<slug>/firmware/<int:firmware_id>/deletec                ó˜  € T ;'       g    RP                  4       p \        4       p VP                  4       ;_uu_ 4       pVP                  RW34       VP	                  4       pRRR4       X'       g2   \        R4       \        \        RV R7      4      VP                  4        # \        P                  P                  \        VR,          4      pVP                  4       ;_uu_ 4       pVP                  RV34       RRR4       VP                  4        \        P                  P                  V4      '       d    \        P                  ! V4       \        R	4       \        \        RV R7      4      VP                  4        #   + '       g   i     EL&; i  + '       g   i     L¡; i  \          d     Lei ; i  TP                  4        i ; i)
z0Delete a firmware record and its file from disk.r   r¾   Nr¿   rz   r{   rŒ   z&DELETE FROM ota_firmware WHERE id = %szFirmware deleted.)r   r;   r=   r>   r   r   r   r   rp   rg   r�   rŽ   r   r@   r³   ÚremoveÚOSErrorrÀ   s   &&    r   Údelete_firmwarerÆ   ÷  sF  € ð �JˆJ�B×ÑÓ€DÜ‹8€DðØ�[‰[�]Œ]˜cØ�K‰Kð6ð Ð#ôð —,‘,“.ˆC÷ ÷ ÜÐ'Ô(ÜœGÐ$8¸tÔDÓEð 	�
‰
�ô —G‘G—L‘LÔ!1°3°}Õ3EÓFˆ	Ø�[‰[�]Œ]˜cØ�K‰KÐ@À;À.ÔQ÷ à�‰ŒÜ�7‰7�>‰>˜)×$Ò$ðÜ—	’	˜)Ô$ô 	Ð!Ô"ÜœÐ 4¸4Ô@ÓAà�
‰
�÷1 �]ˆ]ú÷ �]ûô ô Ùðûð
 	�
‰
�úsk   ¦F7 ¾$E?Á"F7 Á2 F7 Â#AF7 Ã&FÃ:<F7 Ä8F& Å F7 Å?F	Æ
	F7 ÆF#	ÆF7 Æ&F4Æ1F7 Æ3F4Æ4F7 Æ7G	)/Ú__doc__rg   rH   Úshutilr8   Úflaskr   r   r   r   r   r   r   r	   Úconfigr
   r   r   r   r   r   Ú__name__ÚbpÚcursorsÚ
DictCursorr:   r£   r(   r4   r;   rC   rK   rN   rT   ra   rc   ri   Úrouters   r€   rŠ   r—   rœ   r«   r¼   rÂ   rÆ   r7   r5   r   Ú<module>rÐ      s·  ðñó 
Û 	Û Û ß d× dÓ d÷÷ ñ ˆu�hÓ€ð ˆJØ
ˆJØ
ˆJØ�Ø�Ø�7—?‘?×-Ñ-ð€ð €òòò,ò5òpò	ò&ò"ò$%ò1ð ‡�ˆ#ƒñó ðð> ‡�ˆ. 5¨& /€Ó2ñó 3ðð6 ‡�Ð
Óñ.ó ð.ðb ‡�Ð
 ¨5°&¨/€Ó:ñ1ó ;ð1òh"ð ‡�Ð
"¨V¨H€Ó5ñ0ó 6ð0ðf ‡�Ð
"Ó#ñ?ó $ð?ðD ‡�Ð
6Ó7ñó 8ðð> ‡�Ð
=ÈÀx€ÓPñó Qòr5   