Промежуточная версия

в которой надо дорабоать

1. Исправить ошибки и сделать что бы работала вторая слева вкладка. ТОесть АПИ для сервера я сделал (пока они возвращают весь список сообщений целиком - всем большим списком сообщений в канал - для мвп это устраивает,и по этому только три АПИ функции добавилось)

  Там какието ошибки на клиенте ( я только сгенерил код - но гдето вылетает) по UI можешь исправлять переделывать - моешь оставить калечное как есть - мне пока не важно. Важно увидить что каналы и сообщения и публичная переписка в каналах блокчейна работает

2. потестировать и сделать корректное завершение сессии (там есть глюки при завершении сесии)
This commit is contained in:
AidarKC
2026-04-03 11:04:59 +03:00
parent 78e62997d1
commit 8a83ac85d9
11 changed files with 541 additions and 3 deletions
@@ -0,0 +1,100 @@
package server.logic.ws_protocol.JSON.handlers.system;
import org.eclipse.jetty.websocket.api.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.handlers.system.entyties.Net_ClientErrorLog_Request;
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ClientErrorLog_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import java.net.SocketAddress;
/**
* ClientErrorLog — технический endpoint для фронтенд-ошибок.
* Не требует авторизации: клиент должен иметь возможность отправить ошибку
* даже если логин/сессия ещё не установлены.
*/
public class Net_ClientErrorLog_Handler implements JsonMessageHandler {
private static final Logger log = LoggerFactory.getLogger(Net_ClientErrorLog_Handler.class);
@Override
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
Net_ClientErrorLog_Request req = (Net_ClientErrorLog_Request) baseRequest;
if (req.getMessage() == null || req.getMessage().isBlank()) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_FIELDS",
"Поле message обязательно для ClientErrorLog"
);
}
long serverTs = System.currentTimeMillis();
String login = safe(ctx != null ? ctx.getLogin() : null);
String sessionId = safe(ctx != null ? ctx.getSessionId() : null);
String remote = safe(remoteAddress(ctx));
log.error(
"CLIENT_FRONTEND_ERROR kind={} clientTs={} serverTs={} login={} sessionId={} remote={} route={} href={} sourceUrl={} line={} column={} requestOp={} requestIdRef={} message={} userAgent={} context={}",
clip(req.getKind(), 64),
req.getClientTs(),
serverTs,
clip(login, 64),
clip(sessionId, 128),
clip(remote, 128),
clip(req.getRoute(), 200),
clip(req.getHref(), 240),
clip(req.getSourceUrl(), 240),
req.getLineNumber(),
req.getColumnNumber(),
clip(req.getRequestOp(), 64),
clip(req.getRequestIdRef(), 128),
clip(req.getMessage(), 500),
clip(req.getUserAgent(), 240),
clip(req.getContextJson(), 2000)
);
if (req.getStack() != null && !req.getStack().isBlank()) {
log.error("CLIENT_FRONTEND_ERROR_STACK requestId={} stack={}",
clip(req.getRequestId(), 128),
clip(req.getStack(), 8000));
}
Net_ClientErrorLog_Response resp = new Net_ClientErrorLog_Response();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(WireCodes.Status.OK);
resp.setAccepted(true);
resp.setServerTs(serverTs);
return resp;
}
private static String remoteAddress(ConnectionContext ctx) {
if (ctx == null) return "";
Session ws = ctx.getWsSession();
if (ws == null) return "";
SocketAddress remote = ws.getRemoteAddress();
return remote != null ? remote.toString() : "";
}
private static String safe(String value) {
return value == null ? "" : value.trim();
}
private static String clip(String value, int maxLen) {
String cleaned = safe(value)
.replace('\n', ' ')
.replace('\r', ' ');
if (cleaned.length() <= maxLen) {
return cleaned;
}
return cleaned.substring(0, Math.max(0, maxLen - 3)) + "...";
}
}
@@ -0,0 +1,38 @@
package server.logic.ws_protocol.JSON.handlers.system;
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.handlers.system.entyties.Net_GetServerInfo_Request;
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_GetServerInfo_Response;
import server.logic.ws_protocol.WireCodes;
import utils.config.AppConfig;
/**
* GetServerInfo — технический запрос без авторизации.
* Возвращает базовую публичную информацию о сервере, чтобы клиент
* мог проверить доступность узла и показать его в списке серверов.
*/
public class Net_GetServerInfo_Handler implements JsonMessageHandler {
private static final AppConfig CONFIG = AppConfig.getInstance();
@Override
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
Net_GetServerInfo_Request req = (Net_GetServerInfo_Request) baseRequest;
Net_GetServerInfo_Response resp = new Net_GetServerInfo_Response();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(WireCodes.Status.OK);
resp.setUrl(CONFIG.getStringOrEmpty("server.info.url"));
resp.setVersion(CONFIG.getStringOrEmpty("server.version"));
resp.setPhysicalRegion(CONFIG.getStringOrEmpty("server.info.physicalRegion"));
resp.setDescription(CONFIG.getStringOrEmpty("server.info.description"));
resp.setOrigin(CONFIG.getStringOrEmpty("server.info.origin"));
resp.setExtraInfo(CONFIG.getStringOrEmpty("server.info.extraInfo"));
return resp;
}
}
@@ -0,0 +1,81 @@
package server.logic.ws_protocol.JSON.handlers.system.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
/**
* ClientErrorLog:
* {
* "op": "ClientErrorLog",
* "requestId": "req-1",
* "payload": {
* "kind": "global_error",
* "message": "...",
* "stack": "...",
* "sourceUrl": "...",
* "lineNumber": 10,
* "columnNumber": 20,
* "route": "#/channel-view/own-0",
* "href": "https://example/#/channel-view/own-0",
* "userAgent": "...",
* "clientTs": 1700000000123,
* "requestOp": "GetChannelMessages",
* "requestIdRef": "GetChannelMessages-123",
* "contextJson": "{...}"
* }
* }
*/
public class Net_ClientErrorLog_Request extends Net_Request {
private String kind;
private String message;
private String stack;
private String sourceUrl;
private Integer lineNumber;
private Integer columnNumber;
private String route;
private String href;
private String userAgent;
private Long clientTs;
private String requestOp;
private String requestIdRef;
private String contextJson;
public String getKind() { return kind; }
public void setKind(String kind) { this.kind = kind; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getStack() { return stack; }
public void setStack(String stack) { this.stack = stack; }
public String getSourceUrl() { return sourceUrl; }
public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; }
public Integer getLineNumber() { return lineNumber; }
public void setLineNumber(Integer lineNumber) { this.lineNumber = lineNumber; }
public Integer getColumnNumber() { return columnNumber; }
public void setColumnNumber(Integer columnNumber) { this.columnNumber = columnNumber; }
public String getRoute() { return route; }
public void setRoute(String route) { this.route = route; }
public String getHref() { return href; }
public void setHref(String href) { this.href = href; }
public String getUserAgent() { return userAgent; }
public void setUserAgent(String userAgent) { this.userAgent = userAgent; }
public Long getClientTs() { return clientTs; }
public void setClientTs(Long clientTs) { this.clientTs = clientTs; }
public String getRequestOp() { return requestOp; }
public void setRequestOp(String requestOp) { this.requestOp = requestOp; }
public String getRequestIdRef() { return requestIdRef; }
public void setRequestIdRef(String requestIdRef) { this.requestIdRef = requestIdRef; }
public String getContextJson() { return contextJson; }
public void setContextJson(String contextJson) { this.contextJson = contextJson; }
}
@@ -0,0 +1,15 @@
package server.logic.ws_protocol.JSON.handlers.system.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
public class Net_ClientErrorLog_Response extends Net_Response {
private long serverTs;
private boolean accepted;
public long getServerTs() { return serverTs; }
public void setServerTs(long serverTs) { this.serverTs = serverTs; }
public boolean isAccepted() { return accepted; }
public void setAccepted(boolean accepted) { this.accepted = accepted; }
}
@@ -0,0 +1,14 @@
package server.logic.ws_protocol.JSON.handlers.system.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
/**
* GetServerInfo:
* {
* "op": "GetServerInfo",
* "requestId": "req-1",
* "payload": {}
* }
*/
public class Net_GetServerInfo_Request extends Net_Request {
}
@@ -0,0 +1,47 @@
package server.logic.ws_protocol.JSON.handlers.system.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
/**
* Ответ GetServerInfo:
* {
* "op": "GetServerInfo",
* "requestId": "req-1",
* "status": 200,
* "payload": {
* "url": "...",
* "version": "...",
* "physicalRegion": "...",
* "description": "...",
* "origin": "...",
* "extraInfo": "..."
* }
* }
*/
public class Net_GetServerInfo_Response extends Net_Response {
private String url;
private String version;
private String physicalRegion;
private String description;
private String origin;
private String extraInfo;
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getPhysicalRegion() { return physicalRegion; }
public void setPhysicalRegion(String physicalRegion) { this.physicalRegion = physicalRegion; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getOrigin() { return origin; }
public void setOrigin(String origin) { this.origin = origin; }
public String getExtraInfo() { return extraInfo; }
public void setExtraInfo(String extraInfo) { this.extraInfo = extraInfo; }
}
@@ -0,0 +1,44 @@
package server.logic.ws_protocol.JSON.utils;
import server.logic.ws_protocol.Base64Ws;
/**
* Утилиты для строковых публичных ключей, используемых в auth/session API.
*
* Поддерживаемые форматы:
* - legacy: BASE64(32 bytes)
* - explicit: ed25519/BASE64(32 bytes)
*/
public final class AuthKeyUtils {
private AuthKeyUtils() {}
public static String normalize(String key, String fieldName) {
if (key == null) throw new IllegalArgumentException(fieldName + " is null");
String trimmed = key.trim();
if (trimmed.isEmpty()) throw new IllegalArgumentException(fieldName + " is empty");
return trimmed;
}
public static byte[] parseEd25519PublicKey(String key, String fieldName) {
String normalized = normalize(key, fieldName);
int slash = normalized.indexOf('/');
if (slash < 0) {
return Base64Ws.decodeLen(normalized, 32, fieldName);
}
String algorithm = normalized.substring(0, slash).trim();
String encodedKey = normalized.substring(slash + 1).trim();
if (algorithm.isEmpty() || encodedKey.isEmpty()) {
throw new IllegalArgumentException(fieldName + " has bad algorithm/key format");
}
if (!"ed25519".equalsIgnoreCase(algorithm)) {
throw new UnsupportedOperationException(fieldName + " algorithm is not supported: " + algorithm);
}
return Base64Ws.decodeLen(encodedKey, 32, fieldName);
}
}