|
16 | 16 | from radioshaq.auth.jwt import TokenPayload |
17 | 17 | from radioshaq.config.schema import Config |
18 | 18 | from radioshaq.messaging_compliance import emergency_messaging_allowed |
| 19 | +from radioshaq.utils.phone import normalize_e164 |
19 | 20 |
|
20 | 21 | router = APIRouter() |
21 | 22 |
|
22 | 23 | E164_PATTERN = re.compile(r"^\+?[0-9]{10,15}$") |
23 | 24 |
|
24 | 25 |
|
25 | | -def _normalize_e164(phone: str) -> str: |
26 | | - digits = re.sub(r"\D", "", (phone or "").strip()) |
27 | | - return "+" + digits if digits else "" |
28 | | - |
29 | | - |
30 | 26 | class EmergencyRequestBody(BaseModel): |
31 | 27 | """Body for POST /emergency/request.""" |
32 | 28 |
|
@@ -67,7 +63,7 @@ async def create_emergency_request( |
67 | 63 | ) |
68 | 64 | if body.contact_channel not in ("sms", "whatsapp"): |
69 | 65 | raise HTTPException(status_code=400, detail="contact_channel must be sms or whatsapp") |
70 | | - phone = _normalize_e164(body.contact_phone) |
| 66 | + phone = normalize_e164(body.contact_phone) |
71 | 67 | if not E164_PATTERN.match(phone): |
72 | 68 | raise HTTPException(status_code=400, detail="contact_phone must be E.164 (10–15 digits)") |
73 | 69 | initiator = getattr(_user, "callsign", None) or getattr(_user, "sub", "api") |
@@ -151,13 +147,14 @@ async def list_emergency_events( |
151 | 147 | status: str | None = None, |
152 | 148 | _user: TokenPayload = Depends(get_current_user), |
153 | 149 | ) -> dict[str, Any]: |
154 | | - """List coordination events with event_type=emergency. Optional filter by status.""" |
| 150 | + """List coordination events with event_type=emergency. Optional filter by status (e.g. pending, approved, rejected).""" |
155 | 151 | db = getattr(request.app.state, "db", None) |
156 | 152 | if db is None or not hasattr(db, "get_pending_coordination_events"): |
157 | 153 | return {"events": [], "count": 0} |
158 | | - events = await db.get_pending_coordination_events(max_results=1000, event_type="emergency") |
159 | | - if status: |
160 | | - events = [e for e in events if e.get("status") == status] |
| 154 | + status_filter = status if status else "pending" |
| 155 | + events = await db.get_pending_coordination_events( |
| 156 | + max_results=1000, event_type="emergency", status=status_filter |
| 157 | + ) |
161 | 158 | return {"events": events, "count": len(events)} |
162 | 159 |
|
163 | 160 |
|
@@ -234,15 +231,15 @@ async def reject_emergency_event( |
234 | 231 | ) -> dict[str, Any]: |
235 | 232 | """Reject an emergency event (do not send). Sets status=rejected and records rejected_at, rejected_by.""" |
236 | 233 | db = getattr(request.app.state, "db", None) |
237 | | - if db is None or not hasattr(db, "get_coordination_event_by_id") or not hasattr(db, "update_coordination_event"): |
| 234 | + if db is None or not hasattr(db, "claim_emergency_event_pending") or not hasattr(db, "get_coordination_event_by_id") or not hasattr(db, "update_coordination_event"): |
238 | 235 | raise HTTPException(status_code=503, detail="Database not available") |
| 236 | + claimed = await db.claim_emergency_event_pending(event_id) |
| 237 | + if claimed is None: |
| 238 | + raise HTTPException(status_code=400, detail="Event already processed") |
239 | 239 | event = await db.get_coordination_event_by_id(event_id) |
240 | | - if not event: |
241 | | - raise HTTPException(status_code=404, detail="Event not found") |
242 | | - if event.get("event_type") != "emergency": |
| 240 | + if not event or event.get("event_type") != "emergency": |
| 241 | + await db.update_coordination_event(event_id, status="pending") |
243 | 242 | raise HTTPException(status_code=400, detail="Not an emergency event") |
244 | | - if event.get("status") != "pending": |
245 | | - raise HTTPException(status_code=400, detail="Event already processed") |
246 | 243 | rejector = getattr(_user, "sub", None) or getattr(_user, "callsign", "api") |
247 | 244 | now = datetime.now(timezone.utc).isoformat() |
248 | 245 | extra = event.get("extra_data") or {} |
|
0 commit comments