Checkpoint: первая рабочая версия звонков, сигналинг будет переделан

This commit is contained in:
AidarKC
2026-05-02 18:13:22 +03:00
parent b05da86197
commit 310863faec
20 changed files with 572 additions and 37 deletions
@@ -142,6 +142,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
case "prev_line_block_not_found" -> "Не найден блок prevLineNumber для проверки линии";
case "bad_prev_line_hash" -> "Некорректный prevLineHash";
case "db_error_prev_line_check" -> "Ошибка БД при проверке prevLine";
case "channel_zero_writes_disabled" -> "Запись в канал 0 временно отключена";
case "channel_name_already_exists" -> "Такое название канала уже занято";
case "internal_error" -> "Внутренняя ошибка сервера при записи блока";
default -> "Ошибка: " + code;
@@ -337,6 +338,17 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
prevLineHash32 = bl.prevLineBlockHash32();
thisLineNumber = bl.lineSeq();
// Канал 0 сохраняем как технический root, но публикации в него пока не принимаем.
// Это правило защищает от "случайных" постов в дефолтный канал.
int msgType = block.type & 0xFFFF;
int msgSubType = block.subType & 0xFFFF;
if (msgType == 1
&& msgSubType == (MsgSubType.TEXT_POST & 0xFFFF)
&& lineCode != null
&& lineCode == 0) {
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "channel_zero_writes_disabled", serverLastNum, serverLastHashHex);
}
// Нормализация: -1 не пишем в БД (для совместимости со старым TextBody)
if (prevLineNumber != null && prevLineNumber == -1) {
lineCode = null;
@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
public class Net_GetUserConnectionsGraph_Handler implements JsonMessageHandler {
private static final Pattern AR_TX_ID_PATTERN = Pattern.compile("^[A-Za-z0-9_-]{43}$");
private static final Pattern AVATAR_AR_TOKEN_PATTERN = Pattern.compile("(?:^|,)\\s*AR:([A-Za-z0-9_-]{43})\\s*(?:,|$)");
@Override
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) throws Exception {
@@ -201,8 +202,18 @@ public class Net_GetUserConnectionsGraph_Handler implements JsonMessageHandler {
private String extractArAvatarTxId(String rawValue) {
String value = String.valueOf(rawValue == null ? "" : rawValue).trim();
if (!value.startsWith("AR:")) return null;
String txId = value.substring(3).trim();
if (value.isEmpty()) return null;
var tokenMatch = AVATAR_AR_TOKEN_PATTERN.matcher(value);
String txId = tokenMatch.find() ? String.valueOf(tokenMatch.group(1)).trim() : "";
// fallback для старых/кривых значений без запятых: "SHA256:...AR:<txid>"
if (txId.isEmpty()) {
int idx = value.indexOf("AR:");
if (idx >= 0) {
int start = idx + 3;
int end = value.indexOf(',', start);
txId = (end >= 0 ? value.substring(start, end) : value.substring(start)).trim();
}
}
if (!AR_TX_ID_PATTERN.matcher(txId).matches()) return null;
return txId;
}
@@ -51,12 +51,18 @@ public class Net_GetCallIceConfig_Handler implements JsonMessageHandler {
String turnUsername = "";
String turnPassword = "";
List<Net_GetCallIceConfig_Response.TurnServerConfig> turnServers = buildTurnServers(ctx, nowMs, ttlSec);
String sharedSecret = readStr("call.ice.turn.sharedSecret", "");
String staticUsername = readStr("call.ice.turn.username", "");
String staticPassword = readStr("call.ice.turn.password", "");
if (!turnUrls.isEmpty()) {
if (!turnServers.isEmpty()) {
Net_GetCallIceConfig_Response.TurnServerConfig primary = turnServers.get(0);
turnUrls = primary.getUrls();
turnUsername = primary.getUsername();
turnPassword = primary.getPassword();
} else if (!turnUrls.isEmpty()) {
if (!sharedSecret.isBlank()) {
long expiresEpochSec = nowMs / 1000L + ttlSec;
expiresAtMs = expiresEpochSec * 1000L;
@@ -78,6 +84,7 @@ public class Net_GetCallIceConfig_Handler implements JsonMessageHandler {
resp.setTurnUrls(turnUrls);
resp.setTurnUsername(turnUsername);
resp.setTurnPassword(turnPassword);
resp.setTurnServers(turnServers);
resp.setTurnEnabled(!turnUrls.isEmpty() && !turnUsername.isBlank() && !turnPassword.isBlank());
resp.setGeneratedAtMs(nowMs);
resp.setExpiresAtMs(expiresAtMs);
@@ -85,6 +92,40 @@ public class Net_GetCallIceConfig_Handler implements JsonMessageHandler {
return resp;
}
private static List<Net_GetCallIceConfig_Response.TurnServerConfig> buildTurnServers(ConnectionContext ctx, long nowMs, int ttlSec) {
List<Net_GetCallIceConfig_Response.TurnServerConfig> out = new ArrayList<>();
for (int i = 1; i <= 16; i++) {
String base = "call.ice.turn.servers." + i + ".";
List<String> urls = parseUrls(readStr(base + "urls", ""));
if (urls.isEmpty()) continue;
String id = readStr(base + "id", "turn-" + i);
String sharedSecret = readStr(base + "sharedSecret", "");
String staticUsername = readStr(base + "username", "");
String staticPassword = readStr(base + "password", "");
String username = "";
String password = "";
if (!sharedSecret.isBlank()) {
long expiresEpochSec = nowMs / 1000L + ttlSec;
String prefix = readStr("call.ice.turn.userPrefix", "shine");
String safeLogin = sanitizeLogin(ctx.getLogin());
username = expiresEpochSec + ":" + prefix + "_" + safeLogin;
password = makeTurnRestPassword(sharedSecret, username);
} else if (!staticUsername.isBlank() && !staticPassword.isBlank()) {
username = staticUsername;
password = staticPassword;
}
if (username.isBlank() || password.isBlank()) continue;
Net_GetCallIceConfig_Response.TurnServerConfig item = new Net_GetCallIceConfig_Response.TurnServerConfig();
item.setId(id);
item.setUrls(urls);
item.setUsername(username);
item.setPassword(password);
out.add(item);
}
return out;
}
private static int readInt(String key, int fallback) {
String value = CONFIG.getParam(key);
if (value == null || value.isBlank()) return fallback;
@@ -146,4 +187,3 @@ public class Net_GetCallIceConfig_Handler implements JsonMessageHandler {
}
}
}
@@ -6,10 +6,27 @@ import java.util.ArrayList;
import java.util.List;
public class Net_GetCallIceConfig_Response extends Net_Response {
public static class TurnServerConfig {
private String id = "";
private List<String> urls = new ArrayList<>();
private String username = "";
private String password = "";
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public List<String> getUrls() { return urls; }
public void setUrls(List<String> urls) { this.urls = urls; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
private List<String> stunUrls = new ArrayList<>();
private List<String> turnUrls = new ArrayList<>();
private String turnUsername = "";
private String turnPassword = "";
private List<TurnServerConfig> turnServers = new ArrayList<>();
private boolean turnEnabled;
private long generatedAtMs;
private long expiresAtMs;
@@ -27,6 +44,9 @@ public class Net_GetCallIceConfig_Response extends Net_Response {
public String getTurnPassword() { return turnPassword; }
public void setTurnPassword(String turnPassword) { this.turnPassword = turnPassword; }
public List<TurnServerConfig> getTurnServers() { return turnServers; }
public void setTurnServers(List<TurnServerConfig> turnServers) { this.turnServers = turnServers; }
public boolean isTurnEnabled() { return turnEnabled; }
public void setTurnEnabled(boolean turnEnabled) { this.turnEnabled = turnEnabled; }
@@ -39,4 +59,3 @@ public class Net_GetCallIceConfig_Response extends Net_Response {
public int getTtlSec() { return ttlSec; }
public void setTtlSec(int ttlSec) { this.ttlSec = ttlSec; }
}