Phase 3.1 WS2: Backend FCM fallback, ping config, unread API
- Add require_mitra_ping + mitra_ping_interval_seconds config keys (migration) - Add getMitraPingConfig/setMitraPingConfig to config service - Add GET/PATCH /internal/config/mitra-ping routes for control center - Update mitra status service: honor ping config in auto-offline sweep, include ping config in GET /api/mitra/status response - Enhance pairing FCM payload with action: 'open_accept' for deep-link - Add FCM fallback to closure.service (initiateEarlyEnd, completeSession) - Add FCM fallback to session-timer.service (onSessionExpired) - Add unread count queries (getActiveSessionByCustomerWithUnread, getActiveSessionsByMitraWithUnread) - Add GET /api/client/chat/session/active-with-unread route - Add GET /api/mitra/chat-requests/sessions/active-with-unread route Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { getDb } from '../db/client.js'
|
||||
import { publish } from '../plugins/valkey.js'
|
||||
import { clearSessionTimer, clearClosureGraceTimer } from './session-timer.service.js'
|
||||
import { sendToSessionParticipant } from '../plugins/websocket.js'
|
||||
import { sendPushNotification } from './notification.service.js'
|
||||
import { UserType, SessionStatus, EndedBy, WsMessage } from '../constants.js'
|
||||
|
||||
const sql = getDb()
|
||||
@@ -53,10 +54,25 @@ export const completeSession = async (sessionId) => {
|
||||
`
|
||||
if (!session) return null
|
||||
|
||||
// Notify both parties
|
||||
// Notify both parties, FCM fallback if WebSocket is down
|
||||
const data = { type: WsMessage.SESSION_COMPLETED, session_id: sessionId }
|
||||
sendToSessionParticipant(sessionId, UserType.CUSTOMER, data)
|
||||
sendToSessionParticipant(sessionId, UserType.MITRA, data)
|
||||
const customerSent = sendToSessionParticipant(sessionId, UserType.CUSTOMER, data)
|
||||
const mitraSent = sendToSessionParticipant(sessionId, UserType.MITRA, data)
|
||||
|
||||
if (!customerSent) {
|
||||
await sendPushNotification(UserType.CUSTOMER, session.customer_id, {
|
||||
title: 'Sesi Selesai',
|
||||
body: 'Sesi curhat kamu telah selesai.',
|
||||
data: { type: WsMessage.SESSION_COMPLETED, session_id: sessionId },
|
||||
})
|
||||
}
|
||||
if (!mitraSent) {
|
||||
await sendPushNotification(UserType.MITRA, session.mitra_id, {
|
||||
title: 'Sesi Selesai',
|
||||
body: 'Sesi curhat telah selesai.',
|
||||
data: { type: WsMessage.SESSION_COMPLETED, session_id: sessionId },
|
||||
})
|
||||
}
|
||||
|
||||
await publish(`session:${sessionId}:status`, { type: WsMessage.SESSION_ENDED, session_id: sessionId })
|
||||
|
||||
@@ -90,10 +106,25 @@ export const initiateEarlyEnd = async (sessionId, userType) => {
|
||||
|
||||
clearSessionTimer(sessionId)
|
||||
|
||||
// Notify both parties to enter closure flow
|
||||
// Notify both parties to enter closure flow, FCM fallback if WebSocket is down
|
||||
const data = { type: WsMessage.SESSION_CLOSING, session_id: sessionId, ended_by: userType }
|
||||
sendToSessionParticipant(sessionId, UserType.CUSTOMER, data)
|
||||
sendToSessionParticipant(sessionId, UserType.MITRA, data)
|
||||
const customerSent = sendToSessionParticipant(sessionId, UserType.CUSTOMER, data)
|
||||
const mitraSent = sendToSessionParticipant(sessionId, UserType.MITRA, data)
|
||||
|
||||
if (!customerSent) {
|
||||
await sendPushNotification(UserType.CUSTOMER, session.customer_id, {
|
||||
title: 'Sesi Berakhir',
|
||||
body: 'Sesi curhat kamu telah berakhir. Ketuk untuk menulis pesan penutup.',
|
||||
data: { type: WsMessage.SESSION_CLOSING, session_id: sessionId },
|
||||
})
|
||||
}
|
||||
if (!mitraSent) {
|
||||
await sendPushNotification(UserType.MITRA, session.mitra_id, {
|
||||
title: 'Sesi Berakhir',
|
||||
body: 'Sesi curhat telah berakhir. Ketuk untuk menulis pesan penutup.',
|
||||
data: { type: WsMessage.SESSION_CLOSING, session_id: sessionId },
|
||||
})
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user