Fix SSL/TLS issue and remove hina_invitation from built-in templates (user-managed only)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
dvirlabs 2026-05-14 10:02:12 +03:00
parent 15890cc138
commit 0cc965aff6
3 changed files with 21 additions and 90 deletions

View File

@ -74,21 +74,11 @@ def _fix_template_parameters():
import json import json
db = SessionLocal() db = SessionLocal()
try: try:
# First, delete any old custom hina_invitation records (it's now built-in only) # No built-in template fixes needed currently
old_templates = db.query(models.WhatsAppTemplate).filter( # hina_invitation is now user-managed only (not built-in)
(models.WhatsAppTemplate.template_key == 'hina_invitation') | pass
(models.WhatsAppTemplate.meta_name == 'hina_invitation')
).all()
if old_templates:
print(f"[startup] Removing old custom hina_invitation templates from database...")
for old_tpl in old_templates:
print(f" Deleting: {old_tpl.template_key} (had {old_tpl.body_params} params)")
db.delete(old_tpl)
db.commit()
print(f" ✓ Old templates removed - hina_invitation is now built-in only!")
except Exception as e: except Exception as e:
print(f"[startup] Template cleanup warning: {e}") print(f"[startup] Template fix warning: {e}")
finally: finally:
db.close() db.close()
@ -1988,16 +1978,19 @@ async def test_whatsapp_send(
service = get_whatsapp_service(db) service = get_whatsapp_service(db)
# hina_invitation template needs both parameters: # Test with wedding_invitation template (built-in template with basic params)
# - contact_name: for body {{1}} (guest's name)
# - event_id: for button {{1}} (dynamic URL)
params = { params = {
"contact_name": "Test", "contact_name": "Test User",
"event_id": "test-event-123" "groom_name": "Groom",
"bride_name": "Bride",
"venue": "Test Venue",
"event_date": "01/06",
"event_time": "18:00",
"guest_link": "https://invy.dvirlabs.com/guest/test-event-123"
} }
# Use hina_invitation template # Use wedding_invitation template
template_key = "hina_invitation" template_key = "wedding_invitation"
result = await service.send_by_template_key( result = await service.send_by_template_key(
template_key=template_key, template_key=template_key,

View File

@ -17,51 +17,14 @@ logger = logging.getLogger(__name__)
async def create_http_client() -> httpx.AsyncClient: async def create_http_client() -> httpx.AsyncClient:
""" """
Create an httpx client with proper certificate verification. Create an httpx client with proper certificate verification.
Uses relaxed SSL settings for compatibility with Meta's WhatsApp Cloud API. Uses certifi for CA bundle - simple and reliable.
""" """
import ssl return httpx.AsyncClient(
verify=certifi.where(),
try: timeout=httpx.Timeout(30.0, connect=10.0),
# Create a more permissive SSL context for compatibility http2=False,
ssl_context = ssl.create_default_context(cafile=certifi.where()) limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
)
# Allow TLS 1.0+ for maximum compatibility
ssl_context.minimum_version = ssl.TLSVersion.TLSv1
# Relax cipher restrictions
ssl_context.set_ciphers('DEFAULT:@SECLEVEL=1')
# Keep hostname verification enabled for security
ssl_context.check_hostname = True
ssl_context.verify_mode = ssl.CERT_REQUIRED
return httpx.AsyncClient(
verify=ssl_context,
timeout=httpx.Timeout(30.0, connect=10.0),
http2=False, # Disable HTTP/2 for better compatibility
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
)
except Exception as e:
logger.warning(f"[WhatsApp] SSL context creation failed: {e}, trying simple verify")
# Final fallback: just use certifi directly
try:
return httpx.AsyncClient(
verify=certifi.where(),
timeout=httpx.Timeout(30.0, connect=10.0),
http2=False,
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
)
except Exception as e2:
logger.error(f"[WhatsApp] All SSL methods failed: {e2}")
# Last resort: no verification (INSECURE - log warning)
logger.warning("⚠️ Using INSECURE SSL (no verification) - FIX YOUR SSL SETUP!")
return httpx.AsyncClient(
verify=False,
timeout=httpx.Timeout(30.0, connect=10.0),
http2=False,
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
)
class WhatsAppError(Exception): class WhatsAppError(Exception):

View File

@ -240,31 +240,6 @@ TEMPLATES: Dict[str, Dict[str, Any]] = {
"guest_link": "https://invy.dvirlabs.com/guest", "guest_link": "https://invy.dvirlabs.com/guest",
}, },
}, },
# ── hina_invitation ────────────────────────────────────────────────────────
# Special event template with dynamic IMAGE header, body params, and button
# Header: IMAGE (dynamic - sent from event.invitation_image_url)
# Body {{1}} = contact_name (guest's name in greeting)
# Button {{1}} = event_id (dynamic URL parameter)
"hina_invitation": {
"meta_name": "hina_invitation",
"language_code": "he",
"friendly_name": "הזמנה לחינה",
"description": "הזמנה לאירוע חינה עם תמונה וקישור דינמי",
"header_type": "IMAGE",
"header_params": [], # IMAGE headers use header_handle, not header_params
"header_handle_key": "invitation_image_url", # Dynamic - from params dict
"body_params": ["contact_name"],
"button_type": "URL",
"button_text": "הצבע על הזמנה",
"button_url": "https://invy.dvirlabs.com/guest/{{1}}",
"button_param_key": "event_id",
"fallbacks": {
"contact_name": "חבר",
"event_id": "event-id",
"invitation_image_url": "https://api-invy.dvirlabs.com/uploads/default.jpg",
},
},
} }