import { Injectable } from "@nestjs/common"; import type { PushProvider, PushMessage, PushSendResult } from "./push-provider.js"; const EXPO_PUSH_ENDPOINT = "https://exp.host/--/api/v2/push/send"; interface ExpoPushResponse { data?: { status?: string; message?: string }; } // Q-3 真實推播供應商:Expo 的推播 API 是「React Native + Expo」這個技術選型本身自帶的服務 // (不像 LLM/TTS 需要另外挑一家外部供應商待人工確認),基本匿名發送不需要金鑰,所以這裡直接 // 實作真正會打出去的 HTTP 呼叫——沒有真實裝置 token 時,Expo 端會回報 token 無效,屬於預期行為, // 不是這個 Provider 本身沒做完。 @Injectable() export class ExpoPushProvider implements PushProvider { async send(message: PushMessage): Promise { try { const res = await fetch(EXPO_PUSH_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify({ to: message.token, title: message.title, body: message.body, data: message.data ?? {} }), }); const payload = (await res.json().catch(() => null)) as ExpoPushResponse | null; const ticket = payload?.data; if (!res.ok || ticket?.status === "error") { return { success: false, errorText: ticket?.message ?? `HTTP ${res.status}` }; } return { success: true }; } catch (err) { return { success: false, errorText: err instanceof Error ? err.message : String(err) }; } } }