SHA256
Add MVP call signaling API and browser call flow
This commit is contained in:
+8
@@ -58,9 +58,13 @@ import server.logic.ws_protocol.JSON.handlers.connections.entyties.Net_GetUserCo
|
||||
import server.logic.ws_protocol.JSON.handlers.connections.entyties.Net_AddCloseFriend_Request;
|
||||
import server.logic.ws_protocol.JSON.handlers.connections.entyties.Net_ListContacts_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.Net_AckIncomingMessage_Handler;
|
||||
import server.logic.ws_protocol.JSON.messages.Net_CallInviteBroadcast_Handler;
|
||||
import server.logic.ws_protocol.JSON.messages.Net_CallSignalToSession_Handler;
|
||||
import server.logic.ws_protocol.JSON.messages.Net_SendDirectMessage_Handler;
|
||||
import server.logic.ws_protocol.JSON.messages.Net_UpsertPushToken_Handler;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_AckIncomingMessage_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_CallInviteBroadcast_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_CallSignalToSession_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_SendDirectMessage_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_UpsertPushToken_Request;
|
||||
|
||||
@@ -116,6 +120,8 @@ public final class JsonHandlerRegistry {
|
||||
Map.entry("UpsertPushToken", new Net_UpsertPushToken_Handler()),
|
||||
Map.entry("SendDirectMessage", new Net_SendDirectMessage_Handler()),
|
||||
Map.entry("AckIncomingMessage", new Net_AckIncomingMessage_Handler()),
|
||||
Map.entry("CallInviteBroadcast", new Net_CallInviteBroadcast_Handler()),
|
||||
Map.entry("CallSignalToSession", new Net_CallSignalToSession_Handler()),
|
||||
|
||||
// --- system ---
|
||||
Map.entry("Ping", new Net_Ping_Handler()),
|
||||
@@ -162,6 +168,8 @@ public final class JsonHandlerRegistry {
|
||||
Map.entry("UpsertPushToken", Net_UpsertPushToken_Request.class),
|
||||
Map.entry("SendDirectMessage", Net_SendDirectMessage_Request.class),
|
||||
Map.entry("AckIncomingMessage", Net_AckIncomingMessage_Request.class),
|
||||
Map.entry("CallInviteBroadcast", Net_CallInviteBroadcast_Request.class),
|
||||
Map.entry("CallSignalToSession", Net_CallSignalToSession_Request.class),
|
||||
|
||||
// --- system ---
|
||||
Map.entry("Ping", Net_Ping_Request.class),
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package server.logic.ws_protocol.JSON.messages;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_CallInviteBroadcast_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_CallInviteBroadcast_Response;
|
||||
import server.logic.ws_protocol.JSON.push.FcmPushSender;
|
||||
import server.logic.ws_protocol.JSON.push.WsEventSender;
|
||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.JSON.utils.NetIdGenerator;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.PushTokensDAO;
|
||||
import shine.db.dao.SolanaUsersDAO;
|
||||
import shine.db.entities.PushTokenEntry;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class Net_CallInviteBroadcast_Handler implements JsonMessageHandler {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
private static final int TYPE_INVITE = 100;
|
||||
|
||||
@Override
|
||||
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) throws Exception {
|
||||
Net_CallInviteBroadcast_Request req = (Net_CallInviteBroadcast_Request) baseRequest;
|
||||
if (ctx == null || !ctx.isAuthenticatedUser()) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.UNVERIFIED, "NOT_AUTHENTICATED", "Требуется авторизация");
|
||||
}
|
||||
|
||||
String toRequest = req.getToLogin() == null ? "" : req.getToLogin().trim();
|
||||
String callId = req.getCallId() == null ? "" : req.getCallId().trim();
|
||||
int type = req.getType() == null ? TYPE_INVITE : req.getType();
|
||||
if (toRequest.isBlank() || callId.isBlank() || type != TYPE_INVITE) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_FIELDS", "toLogin/callId/type=100 обязательны");
|
||||
}
|
||||
|
||||
SolanaUserEntry targetUser = SolanaUsersDAO.getInstance().getByLogin(toRequest);
|
||||
if (targetUser == null) {
|
||||
return NetExceptionResponseFactory.error(req, 404, "USER_NOT_FOUND", "Пользователь не найден");
|
||||
}
|
||||
|
||||
String from = ctx.getLogin();
|
||||
String to = targetUser.getLogin();
|
||||
long timeMs = System.currentTimeMillis();
|
||||
|
||||
Set<ConnectionContext> activeSessions = ActiveConnectionsRegistry.getInstance().getByLogin(to);
|
||||
List<PushTokenEntry> tokens = PushTokensDAO.getInstance().listByLogin(to);
|
||||
|
||||
int wsDelivered = 0;
|
||||
int fcmDelivered = 0;
|
||||
Set<String> activeSessionIds = new HashSet<>();
|
||||
|
||||
for (ConnectionContext targetCtx : activeSessions) {
|
||||
activeSessionIds.add(targetCtx.getSessionId());
|
||||
|
||||
String eventId = NetIdGenerator.eventId("evt");
|
||||
ObjectNode payload = MAPPER.createObjectNode();
|
||||
payload.put("eventId", eventId);
|
||||
payload.put("fromLogin", from);
|
||||
payload.put("fromSessionId", ctx.getSessionId());
|
||||
payload.put("toLogin", to);
|
||||
payload.put("callId", callId);
|
||||
payload.put("type", TYPE_INVITE);
|
||||
payload.put("timeMs", timeMs);
|
||||
|
||||
boolean sent = WsEventSender.sendEvent(targetCtx, "IncomingCallInvite", eventId, payload);
|
||||
if (sent) wsDelivered++;
|
||||
}
|
||||
|
||||
for (PushTokenEntry token : tokens) {
|
||||
boolean pushed = FcmPushSender.sendNotification(
|
||||
token.getToken(),
|
||||
"Входящий звонок",
|
||||
from + " пытается дозвониться",
|
||||
callId
|
||||
);
|
||||
if (pushed) fcmDelivered++;
|
||||
}
|
||||
|
||||
Net_CallInviteBroadcast_Response resp = new Net_CallInviteBroadcast_Response();
|
||||
resp.setOp(req.getOp());
|
||||
resp.setRequestId(req.getRequestId());
|
||||
resp.setStatus(WireCodes.Status.OK);
|
||||
resp.setCallId(callId);
|
||||
resp.setDeliveredWsSessions(wsDelivered);
|
||||
resp.setDeliveredFcmSessions(fcmDelivered);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package server.logic.ws_protocol.JSON.messages;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_CallSignalToSession_Request;
|
||||
import server.logic.ws_protocol.JSON.messages.entyties.Net_CallSignalToSession_Response;
|
||||
import server.logic.ws_protocol.JSON.push.WsEventSender;
|
||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.JSON.utils.NetIdGenerator;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.SolanaUsersDAO;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
|
||||
public class Net_CallSignalToSession_Handler implements JsonMessageHandler {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) throws Exception {
|
||||
Net_CallSignalToSession_Request req = (Net_CallSignalToSession_Request) baseRequest;
|
||||
if (ctx == null || !ctx.isAuthenticatedUser()) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.UNVERIFIED, "NOT_AUTHENTICATED", "Требуется авторизация");
|
||||
}
|
||||
|
||||
String toRequest = req.getToLogin() == null ? "" : req.getToLogin().trim();
|
||||
String targetSessionId = req.getTargetSessionId() == null ? "" : req.getTargetSessionId().trim();
|
||||
String callId = req.getCallId() == null ? "" : req.getCallId().trim();
|
||||
Integer type = req.getType();
|
||||
String data = req.getData() == null ? "" : req.getData();
|
||||
|
||||
if (toRequest.isBlank() || targetSessionId.isBlank() || callId.isBlank() || type == null) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_FIELDS", "toLogin/targetSessionId/callId/type обязательны");
|
||||
}
|
||||
|
||||
SolanaUserEntry targetUser = SolanaUsersDAO.getInstance().getByLogin(toRequest);
|
||||
if (targetUser == null) {
|
||||
return NetExceptionResponseFactory.error(req, 404, "USER_NOT_FOUND", "Пользователь не найден");
|
||||
}
|
||||
String to = targetUser.getLogin();
|
||||
|
||||
ConnectionContext targetCtx = ActiveConnectionsRegistry.getInstance().getBySessionId(targetSessionId);
|
||||
if (targetCtx == null || !to.equalsIgnoreCase(targetCtx.getLogin())) {
|
||||
return NetExceptionResponseFactory.error(req, 404, "SESSION_NOT_FOUND", "Целевая сессия не найдена");
|
||||
}
|
||||
|
||||
String eventId = NetIdGenerator.eventId("evt");
|
||||
ObjectNode payload = MAPPER.createObjectNode();
|
||||
payload.put("eventId", eventId);
|
||||
payload.put("fromLogin", ctx.getLogin());
|
||||
payload.put("fromSessionId", ctx.getSessionId());
|
||||
payload.put("toLogin", to);
|
||||
payload.put("callId", callId);
|
||||
payload.put("type", type);
|
||||
payload.put("data", data);
|
||||
payload.put("timeMs", System.currentTimeMillis());
|
||||
|
||||
boolean delivered = WsEventSender.sendEvent(targetCtx, "IncomingCallSignal", eventId, payload);
|
||||
|
||||
Net_CallSignalToSession_Response resp = new Net_CallSignalToSession_Response();
|
||||
resp.setOp(req.getOp());
|
||||
resp.setRequestId(req.getRequestId());
|
||||
resp.setStatus(delivered ? WireCodes.Status.OK : 404);
|
||||
resp.setDelivered(delivered);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package server.logic.ws_protocol.JSON.messages.entyties;
|
||||
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||
|
||||
public class Net_CallInviteBroadcast_Request extends Net_Request {
|
||||
private String toLogin;
|
||||
private String callId;
|
||||
private Integer type;
|
||||
|
||||
public String getToLogin() { return toLogin; }
|
||||
public void setToLogin(String toLogin) { this.toLogin = toLogin; }
|
||||
|
||||
public String getCallId() { return callId; }
|
||||
public void setCallId(String callId) { this.callId = callId; }
|
||||
|
||||
public Integer getType() { return type; }
|
||||
public void setType(Integer type) { this.type = type; }
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package server.logic.ws_protocol.JSON.messages.entyties;
|
||||
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||
|
||||
public class Net_CallInviteBroadcast_Response extends Net_Response {
|
||||
private String callId;
|
||||
private int deliveredWsSessions;
|
||||
private int deliveredFcmSessions;
|
||||
|
||||
public String getCallId() { return callId; }
|
||||
public void setCallId(String callId) { this.callId = callId; }
|
||||
|
||||
public int getDeliveredWsSessions() { return deliveredWsSessions; }
|
||||
public void setDeliveredWsSessions(int deliveredWsSessions) { this.deliveredWsSessions = deliveredWsSessions; }
|
||||
|
||||
public int getDeliveredFcmSessions() { return deliveredFcmSessions; }
|
||||
public void setDeliveredFcmSessions(int deliveredFcmSessions) { this.deliveredFcmSessions = deliveredFcmSessions; }
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package server.logic.ws_protocol.JSON.messages.entyties;
|
||||
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||
|
||||
public class Net_CallSignalToSession_Request extends Net_Request {
|
||||
private String toLogin;
|
||||
private String targetSessionId;
|
||||
private String callId;
|
||||
private Integer type;
|
||||
private String data;
|
||||
|
||||
public String getToLogin() { return toLogin; }
|
||||
public void setToLogin(String toLogin) { this.toLogin = toLogin; }
|
||||
|
||||
public String getTargetSessionId() { return targetSessionId; }
|
||||
public void setTargetSessionId(String targetSessionId) { this.targetSessionId = targetSessionId; }
|
||||
|
||||
public String getCallId() { return callId; }
|
||||
public void setCallId(String callId) { this.callId = callId; }
|
||||
|
||||
public Integer getType() { return type; }
|
||||
public void setType(Integer type) { this.type = type; }
|
||||
|
||||
public String getData() { return data; }
|
||||
public void setData(String data) { this.data = data; }
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package server.logic.ws_protocol.JSON.messages.entyties;
|
||||
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||
|
||||
public class Net_CallSignalToSession_Response extends Net_Response {
|
||||
private boolean delivered;
|
||||
|
||||
public boolean isDelivered() { return delivered; }
|
||||
public void setDelivered(boolean delivered) { this.delivered = delivered; }
|
||||
}
|
||||
Reference in New Issue
Block a user