Ë
    {Ìj€*  ã            
       ó6  — U d Z ddlZddlZddlZddlmZ ddlmZmZm	Z	m
Z
 ddlmZ ddlmZmZ ddlmZ  ej$                  d«      Zdae	e   ed	<    ej,                  «       Zd
Zdefd„Zdefd„Zd-d„Z ej8                  d«      Z ej8                  dej<                  «      Z ej8                  dej<                  «      Z  ej8                  dej<                  «      Z! ej8                  dej<                  «      Z" ej8                  dej<                  «      Z#i Z$e%e&e&f   ed<   de&de&fd„Z' G d„ de%«      Z( G d„ d«      Z) G d„ d«      Z*de*fd„Z+ed.de&d e,fd!„«       Z-d"e*d#e&de.e&   fd$„Z/d"e*d#e&de0fd%„Z1d"e*d#e&d&e&d'e&ddf
d(„Z2d"e*d#e&de&d)e&ddf
d*„Z3d-d+„Z4d-d,„Z5y)/uÉ  
AccÃ¨s MySQL partagÃ© par tous les modules (mÃªme pile que hec-photo-app : SQLAlchemy 2.0 + PyMySQL,
utf8mb4 / InnoDB, variables MYSQL_*).

Pourquoi un moteur SYNCHRONE derriÃ¨re `get_db()` : le nettoyage RGPD, l'organizer, l'Ã©lection du leader et
le rate limiting tournent dans des threads (`asyncio.to_thread`) et appelaient dÃ©jÃ  `get_db()`. Le pool de
SQLAlchemy fournit les connexions rÃ©utilisables ; l'interface (`conn.execute(sql, params)`, lignes accessibles
par nom, `with get_db() as conn`) ne change pas pour les ~220 requÃªtes existantes.

Dialecte : le code garde ses placeholders `?` ; ils sont traduits ici (avec `%` Ã©chappÃ©). `INSERT OR REPLACE`
â†’ `REPLACE`, `INSERT OR IGNORE` â†’ `INSERT IGNORE`, `BEGIN IMMEDIATE` â†’ `START TRANSACTION`, la colonne `key`
(mot rÃ©servÃ© MySQL) est citÃ©e automatiquement et `COLLATE NOCASE` est ignorÃ© (la collation par dÃ©faut
utf8mb4_unicode_ci est dÃ©jÃ  insensible Ã  la casse, comme LIKE sous SQLite).
é    N)Úcontextmanager)ÚAnyÚIteratorÚOptionalÚSequence)Úcreate_engine)ÚURLÚEngine)Úconfigzmeeconic.dbÚ_enginez@ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ciÚreturnc            
      ó,  — t        j                  dt        j                  t        j                  xs d t        j
                  t        j                  t        j                  ddi¬«      } t        | t        j                  t        j                  ddd¬«      S )	Nzmysql+pymysqlÚcharsetÚutf8mb4)ÚusernameÚpasswordÚhostÚportÚdatabaseÚqueryi  TzREAD COMMITTED)Ú	pool_sizeÚmax_overflowÚpool_recycleÚpool_pre_pingÚisolation_level)
r	   Úcreater   Ú
MYSQL_USERÚMYSQL_PASSWORDÚ
MYSQL_HOSTÚ
MYSQL_PORTÚMYSQL_DBr   ÚMYSQL_POOL_SIZE)Úurls    ú"/var/www/photos/backend/core/db.pyÚ_build_enginer%   "   ss   € Ü
�*‰*Ø¤&×"3Ñ"3¼f×>SÑ>SÒ>[ÐW[Ü×Ñ¤V×%6Ñ%6ÄÇÁØ˜)Ð$ô&€Cô Ø”v×-Ñ-¼F×<RÑ<RÐaeØØ(ô*ð *ó    c                  ó†   — t         €%t        5  t         €
t        «       a d d d «       t         S t         S # 1 sw Y   t         S xY w©N)r   Ú_engine_lockr%   © r&   r$   Úenginer+   -   s8   € ä€Üñ 	*ÜˆÜ'›/�÷	*ô €NŒ7€N÷	*ô €Nús	   �2²A c                  ór   — t         5  t        �t        j                  «        daddd«       y# 1 sw Y   yxY w)zICloses the pool (tests switch database; the service does it on shutdown).N)r)   r   Údisposer*   r&   r$   Údispose_enginer.   6   s1   € ô 
ñ ÜÐÜ�O‰OÔØˆG÷÷ ñ ús   ‡-­6z('(?:[^']|'')*')z(?<![\w`.])key(?![\w`])z \bINSERT\s+OR\s+REPLACE\s+INTO\bz\bINSERT\s+OR\s+IGNORE\s+INTO\bz\s+COLLATE\s+NOCASE\bz+^\s*BEGIN(\s+IMMEDIATE|\s+TRANSACTION)?\s*$Ú_cacheÚsqlc                 óŽ  — t         j                  | «      }|�|S t        j                  | «      rd}nø| j	                  «       dd j                  «       dv }t        j                  | «      }t        |«      D ]¡  \  }}|dz  r|j                  dd«      ||<   Œ!t        j                  d|«      }t        j                  d	|«      }t        j                  d
|«      }|st        j                  d|«      }|j                  dd«      j                  dd«      }|||<   Œ£ d
j                  |«      }t!        t         «      dk  r	|t         | <   |S )uM   SQL Ã©crit pour sqlite3 (placeholders `?`) â†’ SQL MySQL pour PyMySQL (`%s`).NzSTART TRANSACTIONé   )ÚCREATEzALTER zDROP Té   ú%z%%zREPLACE INTOzINSERT IGNORE INTOÚ z`key`ú?z%siÐ  )r/   ÚgetÚ	_BEGIN_REÚmatchÚlstripÚupperÚ
_STRING_REÚsplitÚ	enumerateÚreplaceÚ_OR_REPLACE_REÚsubÚ_OR_IGNORE_REÚ
_NOCASE_REÚ_KEY_REÚjoinÚlen)r0   ÚhitÚoutÚis_ddlÚpartsÚiÚparts          r$   Ú	translaterN   J   s*  € ä
�*‰*�S‹/€CØ
€Øˆ
Ü‡��sÔØ!‰ð —‘“˜b˜qÐ!×'Ñ'Ó)Ð-KÐKˆä× Ñ  Ó%ˆÜ  Ó'ò 
	‰GˆAˆtØ�1ŠuØŸ<™<¨¨TÓ2��a‘ØÜ!×%Ñ% n°dÓ;ˆDÜ ×$Ñ$Ð%9¸4Ó@ˆDÜ—>‘> " dÓ+ˆDÙÜ—{‘{ 7¨DÓ1�Ø—<‘<  TÓ*×2Ñ2°3¸Ó=ˆDØˆE�!ŠHð
	ð �g‰g�e‹nˆÜ
Œ6ƒ{�TÒØŒˆs‰Ø€Jr&   c                   ó&   ‡ — e Zd ZdZdZˆ fd„Zˆ xZS )ÚRowz<Ligne accessible par nom ET par position, comme sqlite3.Row.r*   c                 óz   •— t        |t        «      rt        | j                  «       «      |   S t        ‰| �  |«      S r(   )Ú
isinstanceÚintÚlistÚvaluesÚsuperÚ__getitem__)ÚselfÚkÚ	__class__s     €r$   rW   zRow.__getitem__m   s3   ø€ Ü�aœÔÜ˜Ÿ™›Ó& qÑ)Ð)Ü‰wÑ" 1Ó%Ð%r&   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__rW   Ú__classcell__)rZ   s   @r$   rP   rP   i   s   ø„ ÙFØ€I÷&ð &r&   rP   c                   óz   — e Zd Zd„ Zedefd„«       Zedee   fd„«       Zdee	   fd„Z
dee	   fd„Zdee	   fd„Zy)	ÚCursorc                 ó   — || _         y r(   )Ú_cur)rX   Úcurs     r$   Ú__init__zCursor.__init__t   s	   € Øˆ�	r&   r   c                 ó.   — | j                   j                  S r(   )rd   Úrowcount©rX   s    r$   rh   zCursor.rowcountw   s   € à�y‰y×!Ñ!Ð!r&   c                 ó.   — | j                   j                  S r(   )rd   Ú	lastrowidri   s    r$   rk   zCursor.lastrowid{   s   € à�y‰y×"Ñ"Ð"r&   c                 óT   — | j                   j                  «       }|�t        |«      S d S r(   )rd   ÚfetchonerP   ©rX   Úrs     r$   rm   zCursor.fetchone   s'   € Ø�I‰I×ÑÓ ˆØ˜Œs�1‹vÐ0¨DÐ0r&   c                 ón   — | j                   j                  «       D �cg c]  }t        |«      ‘Œ c}S c c}w r(   ©rd   ÚfetchallrP   rn   s     r$   rr   zCursor.fetchallƒ   s'   € Ø $§	¡	× 2Ñ 2Ó 4Ö5˜1”�A•Ò5Ð5ùÒ5s   �2c              #   ód   K  — | j                   j                  «       D ]  }t        |«      –— Œ y ­wr(   rq   rn   s     r$   Ú__iter__zCursor.__iter__†   s+   è ø€ Ø—‘×#Ñ#Ó%ò 	ˆAÜ�a“&‹Lñ	ùs   ‚.0N)r[   r\   r]   rf   ÚpropertyrS   rh   r   rk   rP   rm   rT   rr   r   rt   r*   r&   r$   rb   rb   s   sq   „ òð ð"˜#ò "ó ð"ð ð#˜8 C™=ò #ó ð#ð1˜( 3™-ó 1ð6˜$˜s™)ó 6ð˜( 3™-ô r&   rb   c                   óŽ   — e Zd ZdZd„ Zddedee   defd„Z	dedeee      ddfd	„Z
d
eddfd„Zdd„Zdd„Zdd„Zdd„Zdefd„Zy)Ú
ConnectionzOConnexion du pool avec l'interface de sqlite3.Connection que le code utilisait.c                 ó^   — dd l }|| _        |j                  j                  | _        d| _        y )Nr   r6   )Úpymysql.cursorsÚ_rawÚcursorsÚ
DictCursorÚ_dict_cursorr   )rX   ÚrawÚpymysqls      r$   rf   zConnection.__init__Ž   s&   € ÛØˆŒ	Ø#ŸO™O×6Ñ6ˆÔØ!ˆÕr&   r0   Úparamsr   c                 óª   — | j                   j                  | j                  «      }|j                  t	        |«      t        |«      «       t        |«      S r(   )rz   Úcursorr}   ÚexecuterN   Útuplerb   )rX   r0   r€   re   s       r$   rƒ   zConnection.execute”   s<   € Ø�i‰i×Ñ˜t×0Ñ0Ó1ˆØ�‰”I˜c“N¤E¨&£MÔ2Ü�c‹{Ðr&   ÚseqNc           	      ó¼   — | j                   j                  | j                  «      }|j                  t	        |«      |D �cg c]  }t        |«      ‘Œ c}«       y c c}w r(   )rz   r‚   r}   ÚexecutemanyrN   r„   )rX   r0   r…   re   Úps        r$   r‡   zConnection.executemany™   s@   € Ø�i‰i×Ñ˜t×0Ñ0Ó1ˆØ�‰œ	 #›¸3Ö(?°a¬¨q­Ò(?Õ@ùÒ(?s   ¿A
Úscriptc                 óf   — d„ |j                  d«      D «       D ]  }|sŒ| j                  |«       Œ y )Nc              3   ó<   K  — | ]  }|j                  «       –— Œ y ­wr(   )Ústrip)Ú.0Úss     r$   ú	<genexpr>z+Connection.executescript.<locals>.<genexpr>ž   s   è ø€ Ò: 1�Q—W‘W—YÑ:ùs   ‚ú;)r>   rƒ   )rX   r‰   Ústmts      r$   ÚexecutescriptzConnection.executescript�   s.   € Ù:¨¯©°SÓ(9Ô:ò 	#ˆDÚØ—‘˜TÕ"ñ	#r&   c                 ó8   — | j                   j                  «        y r(   )rz   Úcommitri   s    r$   r”   zConnection.commit¢   s   € Ø�	‰	×ÑÕr&   c                 ó8   — | j                   j                  «        y r(   )rz   Úrollbackri   s    r$   r–   zConnection.rollback¥   s   € Ø�	‰	×ÑÕr&   c                 óX   — 	 | j                   j                  «        y # t        $ r Y y w xY wr(   )rz   ÚcloseÚ	Exceptionri   s    r$   r˜   zConnection.close¨   s'   € ð	Ø�I‰I�O‰OÕøÜò 	Ùð	ús   ‚ �	)¨)c                 ó   — | S r(   r*   ri   s    r$   Ú	__enter__zConnection.__enter__®   s   € Øˆr&   c                 ó¾   — 	 |€| j                   j                  «        n| j                   j                  «        | j                  «        y# | j                  «        w xY w)NF)rz   r”   r–   r˜   )rX   Úexc_typeÚexcÚtbs       r$   Ú__exit__zConnection.__exit__±   sF   € ð	ØÐØ—	‘	× Ñ Õ"à—	‘	×"Ñ"Ô$à�J‰JŒLØøð �J‰J�Lús   ‚7A
 Á
A)r*   ©r   N)r   rw   )r[   r\   r]   r^   rf   Ústrr   r   rb   rƒ   r‡   r’   r”   r–   r˜   r›   Úboolr    r*   r&   r$   rw   rw   ‹   s‚   „ ÙYò"ñ˜3ð ¨°©ð ¸vó ð
A˜sð A¨°(¸3±-Ñ)@ð AÀTó Að# Cð #¨Dó #ó
óóóð¨Tô r&   rw   c                  óD   — t        t        «       j                  «       «      S r(   )rw   r+   Úraw_connectionr*   r&   r$   Úget_dbr¦   ¼   s   € Ü”f“h×-Ñ-Ó/Ó0Ð0r&   ÚnameÚtimeoutc              #   óÔ  K  — d| › �}t        |«      dkD  r4ddl}d|j                  | j                  «       «      j	                  «       z   }t        «       }	 |j                  d||f«      j                  «       d   }|dk7  rt        d| ›d	|› d
�«      ‚	 d–— |j                  d|f«       	 |j                  «        y# |j                  d|f«       w xY w# |j                  «        w xY w­w)u·   Verrou applicatif inter-processus (GET_LOCK). Remplace `BEGIN IMMEDIATE` de SQLite pour les
    lecture-puis-Ã©criture qui doivent Ãªtre atomiques (rate limiting, cooldown d'alertes).z	meeconic:é@   r   NzSELECT GET_LOCK(?, ?) AS okÚoké   zlock z not acquired in rŽ   zSELECT RELEASE_LOCK(?))
rG   ÚhashlibÚsha1ÚencodeÚ	hexdigestr¦   rƒ   rm   ÚTimeoutErrorr˜   )r§   r¨   Úlockr­   ÚconnÚgots         r$   Ú
named_lockrµ   À   sÖ   è ø€ ð �t�fÐ€DÜ
ˆ4ƒy�2‚~ÛØ˜WŸ\™\¨$¯+©+«-Ó8×BÑBÓDÑDˆÜ‹8€Dð	Ø�l‰lÐ8¸4À¸/ÓJ×SÑSÓUÐVZÑ[ˆØ�!Š8Ü  t hÐ.?À¸yÈÐJÓKÐKð	<Ûà�L‰LÐ1°D°7Õ;à�
‰
�øð �L‰LÐ1°D°7Õ;ûà�
‰
�üs6   ‚AC(Á<C ÂB; ÂC Â*C(Â;CÃC ÃC%Ã%C(r³   Útablec                 ót   — | j                  d|f«      j                  «       }|D �cg c]  }|d   ‘Œ	 c}S c c}w )NziSELECT COLUMN_NAME AS name FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?r§   )rƒ   rr   )r³   r¶   Úrowsro   s       r$   Útable_columnsr¹   ×   s?   € Ø�<‰<ð	9Ø;@¸(óDçDLÁHÃJð 	ð  $Ö$˜!ˆAˆf‹IÒ$Ð$ùÒ$s   ¦5c                 óV   — t        | j                  d|f«      j                  «       «      S )NzVSELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?)r£   rƒ   rm   )r³   r¶   s     r$   Útable_existsr»   Þ   s)   € Ü�—‘Ø`Ø	ˆóç‘(“*óð r&   ÚcolumnÚddlc                 óV   — |t        | |«      vr| j                  d|› d|› d|› �«       y y )NzALTER TABLE z ADD COLUMN ú )r¹   rƒ   )r³   r¶   r¼   r½   s       r$   Úensure_columnrÀ   ä   s5   € Ø”] 4¨Ó/Ñ/Ø�‰�| E 7¨,°v°h¸aÀ¸uÐEÕFð 0r&   Úcolumnsc           	      ó„   — | j                  d||f«      j                  «       }|s| j                  d|› d|› d|› d�«       yy)z1CREATE INDEX IF NOT EXISTS n'existe pas en MySQL.zkSELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? AND INDEX_NAME=?zCREATE INDEX z ON z (ú)N)rƒ   rm   )r³   r¶   r§   rÁ   Úexistss        r$   Úensure_indexrÅ   é   sO   € à�\‰\ØuØ	�ˆóç‘x“zð ñ Ø�‰�} T F¨$¨u¨g°R¸°yÀÐBÕCð r&   c                  ó  — ddl } 	 | j                  t        j                  t        j                  t        j
                  t        j                  d¬«      }	 |j                  «       5 }|j                  dt        j                  › d�«       ddd«       |j                  «        |j                  «        y# 1 sw Y   Œ*xY w# |j                  «        w xY w# t        $ r/}t        j                  dt        j                  |«       Y d}~yd}~ww xY w)uY   CrÃ©e la base (utf8mb4) si l'utilisateur MySQL en a le droit â€” pratique en dev (XAMPP).r   Nr   )r   r   Úuserr   r   zCREATE DATABASE IF NOT EXISTS `z:` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciuO   Could not ensure database %s exists (%s) â€” assuming it was created beforehand)r   Úconnectr   r   r    r   r   r‚   rƒ   r!   r”   r˜   r™   ÚlogÚwarning)r   r³   re   Úes       r$   Úcreate_database_if_missingrÌ   ò   sá   € ãð{Ø�‰¤F×$5Ñ$5¼F×<MÑ<MÔTZ×TeÑTeÜ(.×(=Ñ(=Àyð ó Rˆð	Ø—‘“ð Y #Ø—‘Ð=¼f¿o¹oÐ=Nð OWð Xô Y÷Yð �K‰KŒMà�J‰J�L÷Yð Yûð
 �J‰J�LûÜò {ô 	�‰ÐeÔgm×gvÑgvÐxy×zÑzûð{úsH   †AC ÁB> Á%$B2Â	B> Â!C Â2B;Â7B> Â>CÃC Ã	DÃ%DÄDc            
      óÄ   — t        «        t        «       5 } | j                  dt        › dt        › dt        › d�«       | j	                  «        d d d «       y # 1 sw Y   y xY w)Nz�
        CREATE TABLE IF NOT EXISTS app_kv (
            `key`   VARCHAR(191) NOT NULL PRIMARY KEY,
            value   MEDIUMTEXT
        ) zµ;
        CREATE TABLE IF NOT EXISTS rate_hits (
            `key` VARCHAR(191) NOT NULL,
            ts    DOUBLE NOT NULL,
            KEY idx_rate_hits_key (`key`, ts)
        ) zË;
        CREATE TABLE IF NOT EXISTS leader_locks (
            name       VARCHAR(64) NOT NULL PRIMARY KEY,
            owner      VARCHAR(64) NOT NULL,
            expires_at DOUBLE NOT NULL
        ) z	
        )rÌ   r¦   r’   ÚTABLE_OPTIONSr”   )r³   s    r$   Úinit_core_tablesrÏ     sk   € ÜÔ Ü	‹ð �TØ×Ñð  ô ˆ/ð ô
 ˆ/ð ô
 ˆ/ð 	ðô 	ð  	�‰Œ÷#÷ ñ ús   •8AÁAr¡   )é   )6r^   ÚloggingÚreÚ	threadingÚ
contextlibr   Útypingr   r   r   r   Ú
sqlalchemyr   Úsqlalchemy.enginer	   r
   Úcorer   Ú	getLoggerrÉ   r   Ú__annotations__ÚLockr)   rÎ   r%   r+   r.   Úcompiler=   Ú
IGNORECASErE   rA   rC   rD   r9   r/   Údictr¢   rN   rP   rb   rw   r¦   rS   rµ   rT   r¹   r£   r»   rÀ   rÅ   rÌ   rÏ   r*   r&   r$   ú<module>rß      s  ðòó Û 	Û Ý %ß 4Ó 4å $ß )å à€g×Ñ˜Ó&€à €ˆ�&Ñ	Ó  Øˆy�~‰~Ó€àR€ð*�vó *ð�ó óð ˆR�Z‰ZÐ+Ó,€
Ø
ˆ"�*‰*Ð/°·±Ó
?€Ø�—‘Ð?ÀÇÁÓO€Ø�—
‘
Ð=¸r¿}¹}ÓM€ØˆR�Z‰ZÐ0°"·-±-Ó@€
ØˆB�J‰JÐEÀrÇ}Á}ÓU€	Ø€ˆˆS�#ˆX‰Ó ð�3ð ˜3ó ô>&ˆ$ô &÷ñ ÷0.ñ .ðb1�
ó 1ð ñ�Sð  3ò ó ðð,%˜
ð %¨3ð %°4¸±9ó %ð�zð ¨#ð °$ó ðG˜
ð G¨3ð G¸ð GÀ#ð GÈ$ó Gð
D�zð D¨#ð D°Sð DÀ3ð DÈ4ó Dó{ô&r&   