fix: Recreate httpx client when closed in telegram bot API client

The singleton BackendAPIClient was failing with "Cannot send a request,
as the client has been closed" error when used after async context
manager closed it in linking.py. Changed all client checks from
`if not self.client` to `if not self.client or self.client.is_closed`
to properly recreate the AsyncClient when needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-22 19:09:50 +02:00
parent f52aa27bdc
commit eb3dc195ed

View File

@@ -131,7 +131,7 @@ class BackendAPIClient:
jwt_token = result['access_token'] jwt_token = result['access_token']
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Flow A: Auto-linking (no password required) # Flow A: Auto-linking (no password required)
@@ -170,7 +170,7 @@ class BackendAPIClient:
str: New JWT access token, None if failed str: New JWT access token, None if failed
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.post( response = await self.client.post(
@@ -199,7 +199,7 @@ class BackendAPIClient:
httpx.HTTPError: On network or HTTP errors httpx.HTTPError: On network or HTTP errors
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.post( response = await self.client.post(
@@ -247,7 +247,7 @@ class BackendAPIClient:
httpx.HTTPError: On network or HTTP errors httpx.HTTPError: On network or HTTP errors
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.post( response = await self.client.post(
@@ -309,7 +309,7 @@ class BackendAPIClient:
List of company dicts with id, nume_firma, cui, etc. List of company dicts with id, nume_firma, cui, etc.
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.get( response = await self.client.get(
@@ -350,7 +350,7 @@ class BackendAPIClient:
Includes _cache_hit and _response_time_ms metadata Includes _cache_hit and _response_time_ms metadata
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -385,7 +385,7 @@ class BackendAPIClient:
Dict with treasury breakdown data (accounts by type) Dict with treasury breakdown data (accounts by type)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -421,7 +421,7 @@ class BackendAPIClient:
Dict with detailed data (list of clients/suppliers with balances) Dict with detailed data (list of clients/suppliers with balances)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -457,7 +457,7 @@ class BackendAPIClient:
Dict with maturity data (in_term, overdue, total) Dict with maturity data (in_term, overdue, total)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -491,7 +491,7 @@ class BackendAPIClient:
Dict with performance data (incasari_total, plati_total, net) Dict with performance data (incasari_total, plati_total, net)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -527,7 +527,7 @@ class BackendAPIClient:
Dict with monthly flows (months, incasari, plati arrays) Dict with monthly flows (months, incasari, plati arrays)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -563,7 +563,7 @@ class BackendAPIClient:
Dict with trends data including periods, clienti_incasat, furnizori_achitat arrays Dict with trends data including periods, clienti_incasat, furnizori_achitat arrays
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
# Add cache metadata header for Telegram Bot # Add cache metadata header for Telegram Bot
@@ -611,7 +611,7 @@ class BackendAPIClient:
List of invoice dicts List of invoice dicts
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
params = {"company": company_id} params = {"company": company_id}
@@ -656,7 +656,7 @@ class BackendAPIClient:
Dict with summary (total_count, total_amount, paid, unpaid, etc.) Dict with summary (total_count, total_amount, paid, unpaid, etc.)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.get( response = await self.client.get(
@@ -694,7 +694,7 @@ class BackendAPIClient:
Dict with treasury data (cash_balance, incoming, outgoing, etc.) Dict with treasury data (cash_balance, incoming, outgoing, etc.)
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.get( response = await self.client.get(
@@ -739,7 +739,7 @@ class BackendAPIClient:
bytes: File content, None if failed bytes: File content, None if failed
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
request_data = { request_data = {
@@ -784,7 +784,7 @@ class BackendAPIClient:
bool: True if successful bool: True if successful
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
request_data = {} request_data = {}
@@ -823,7 +823,7 @@ class BackendAPIClient:
bool: True if successful bool: True if successful
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.post( response = await self.client.post(
@@ -854,7 +854,7 @@ class BackendAPIClient:
Dict with cache stats including 'user_enabled' field Dict with cache stats including 'user_enabled' field
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.get( response = await self.client.get(
@@ -881,7 +881,7 @@ class BackendAPIClient:
bool: True if backend is healthy bool: True if backend is healthy
""" """
try: try:
if not self.client: if not self.client or self.client.is_closed:
self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT) self.client = AsyncClient(base_url=self.base_url, timeout=REQUEST_TIMEOUT)
response = await self.client.get("/api/telegram/health") response = await self.client.get("/api/telegram/health")