Дабавил и два запроса на получение параметров, но пока не проверял
This commit is contained in:
AidarKC 2026-01-05 16:14:14 +03:00
parent bfffe44c4a
commit 55d34e2a87
8 changed files with 419 additions and 39 deletions

View File

@ -1,20 +1,27 @@
package server.logic.ws_protocol.JSON;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.*;
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.*;
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.*;
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Request;
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Request;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_AuthChallenge_Handler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_CreateAuthSession__Handler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_RefreshSession_Handler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_CloseActiveSession_Handler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_CreateAuthSession__Handler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_ListSessions_Handler;
import server.logic.ws_protocol.JSON.handlers.auth.Net_RefreshSession_Handler;
import server.logic.ws_protocol.JSON.handlers.auth.entyties.Net_AuthChallenge_Request;
import server.logic.ws_protocol.JSON.handlers.auth.entyties.Net_CloseActiveSession_Request;
import server.logic.ws_protocol.JSON.handlers.auth.entyties.Net_CreateAuthSession_Request;
import server.logic.ws_protocol.JSON.handlers.auth.entyties.Net_ListSessions_Request;
import server.logic.ws_protocol.JSON.handlers.auth.entyties.Net_RefreshSession_Request;
import server.logic.ws_protocol.JSON.handlers.blockchain.Net_AddBlock_Handler;
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Request;
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_AddUser_Handler;
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Request;
import server.logic.ws_protocol.JSON.handlers.userParams.Net_GetUserParam_Handler;
import server.logic.ws_protocol.JSON.handlers.userParams.Net_ListUserParams_Handler;
import server.logic.ws_protocol.JSON.handlers.userParams.Net_UpsertUserParam_Handler;
import server.logic.ws_protocol.JSON.handlers.userParams.entyties.Net_GetUserParam_Request;
import server.logic.ws_protocol.JSON.handlers.userParams.entyties.Net_ListUserParams_Request;
import server.logic.ws_protocol.JSON.handlers.userParams.entyties.Net_UpsertUserParam_Request;
import java.util.Map;
@ -36,8 +43,12 @@ public final class JsonHandlerRegistry {
"CreateAuthSession", new Net_CreateAuthSession__Handler(),
"CloseActiveSession", new Net_CloseActiveSession_Handler(),
"ListSessions", new Net_ListSessions_Handler(),
"AddBlock", new Net_AddBlock_Handler()
// сюда потом добавишь другие операции
"AddBlock", new Net_AddBlock_Handler(),
// --- userParams ---
"UpsertUserParam", new Net_UpsertUserParam_Handler(),
"GetUserParam", new Net_GetUserParam_Handler(),
"ListUserParams", new Net_ListUserParams_Handler()
);
private static final Map<String, Class<? extends Net_Request>> REQUEST_TYPES = Map.of(
@ -47,7 +58,12 @@ public final class JsonHandlerRegistry {
"CreateAuthSession", Net_CreateAuthSession_Request.class,
"CloseActiveSession", Net_CloseActiveSession_Request.class,
"ListSessions", Net_ListSessions_Request.class,
"AddBlock", Net_AddBlock_Request.class
"AddBlock", Net_AddBlock_Request.class,
// --- userParams ---
"UpsertUserParam", Net_UpsertUserParam_Request.class,
"GetUserParam", Net_GetUserParam_Request.class,
"ListUserParams", Net_ListUserParams_Request.class
);
private JsonHandlerRegistry() {

View File

@ -0,0 +1,90 @@
package server.logic.ws_protocol.JSON.handlers.userParams;
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.userParams.entyties.Net_GetUserParam_Request;
import server.logic.ws_protocol.JSON.handlers.userParams.entyties.Net_GetUserParam_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.SqliteDbController;
import shine.db.dao.UserParamsDAO;
import shine.db.entities.UserParamEntry;
import java.sql.Connection;
/**
* GetUserParam получить один параметр пользователя.
*
* ПРО ДОСТУП (на будущее):
* ---------------------------------------------------------------------------------
* Сейчас (MVP) запрос не ограничивает просмотр параметров.
* В будущем, вероятно, потребуется проверка сессии/прав: кто может читать параметры.
* Для MVP эти проверки не нужны.
* ---------------------------------------------------------------------------------
*/
public class Net_GetUserParam_Handler implements JsonMessageHandler {
private static final Logger log = LoggerFactory.getLogger(Net_GetUserParam_Handler.class);
@Override
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
Net_GetUserParam_Request req = (Net_GetUserParam_Request) baseRequest;
if (req.getLogin() == null || req.getLogin().isBlank()
|| req.getParam() == null || req.getParam().isBlank()) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_FIELDS",
"Некорректные поля: login/param"
);
}
String login = req.getLogin().trim();
String param = req.getParam().trim();
try {
SqliteDbController db = SqliteDbController.getInstance();
UserParamsDAO dao = UserParamsDAO.getInstance();
try (Connection c = db.getConnection()) {
UserParamEntry e = dao.getByLoginAndParam(c, login, param);
if (e == null) {
Net_GetUserParam_Response resp = new Net_GetUserParam_Response();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(404);
return resp;
}
Net_GetUserParam_Response resp = new Net_GetUserParam_Response();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(WireCodes.Status.OK);
resp.setLogin(e.getLogin());
resp.setParam(e.getParam());
resp.setTime_ms(e.getTimeMs());
resp.setValue(e.getValue());
resp.setDevice_key(e.getDeviceKey());
resp.setSignature(e.getSignature());
return resp;
}
} catch (Exception e) {
log.error("❌ Internal error GetUserParam", e);
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.INTERNAL_ERROR,
"INTERNAL_ERROR",
"Внутренняя ошибка сервера"
);
}
}
}

View File

@ -0,0 +1,91 @@
package server.logic.ws_protocol.JSON.handlers.userParams;
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.userParams.entyties.Net_ListUserParams_Request;
import server.logic.ws_protocol.JSON.handlers.userParams.entyties.Net_ListUserParams_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;
import shine.db.SqliteDbController;
import shine.db.dao.UserParamsDAO;
import shine.db.entities.UserParamEntry;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
/**
* ListUserParams получить все параметры пользователя.
*
* ПРО ДОСТУП (на будущее):
* ---------------------------------------------------------------------------------
* Сейчас (MVP) запрос не ограничивает просмотр параметров.
* В будущем, вероятно, потребуется проверка сессии/прав: кто может читать параметры.
* Для MVP эти проверки не нужны.
* ---------------------------------------------------------------------------------
*/
public class Net_ListUserParams_Handler implements JsonMessageHandler {
private static final Logger log = LoggerFactory.getLogger(Net_ListUserParams_Handler.class);
@Override
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
Net_ListUserParams_Request req = (Net_ListUserParams_Request) baseRequest;
if (req.getLogin() == null || req.getLogin().isBlank()) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_FIELDS",
"Некорректные поля: login"
);
}
String login = req.getLogin().trim();
try {
SqliteDbController db = SqliteDbController.getInstance();
UserParamsDAO dao = UserParamsDAO.getInstance();
List<UserParamEntry> entries;
try (Connection c = db.getConnection()) {
entries = dao.getByLogin(c, login);
}
Net_ListUserParams_Response resp = new Net_ListUserParams_Response();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(WireCodes.Status.OK);
resp.setLogin(login);
List<Net_ListUserParams_Response.Item> items = new ArrayList<>();
for (UserParamEntry e : entries) {
Net_ListUserParams_Response.Item it = new Net_ListUserParams_Response.Item();
it.setLogin(e.getLogin());
it.setParam(e.getParam());
it.setTime_ms(e.getTimeMs());
it.setValue(e.getValue());
it.setDevice_key(e.getDeviceKey());
it.setSignature(e.getSignature());
items.add(it);
}
resp.setParams(items);
return resp;
} catch (Exception e) {
log.error("❌ Internal error ListUserParams", e);
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.INTERNAL_ERROR,
"INTERNAL_ERROR",
"Внутренняя ошибка сервера"
);
}
}
}

View File

@ -31,7 +31,7 @@ import java.util.Base64;
* 1) Проверяет, что пользователь существует и что device_key действительно его.
* 2) Проверяет, что нет "более нового" значения этого param (time_ms монотонно растёт).
* 3) Проверяет подпись Ed25519 по device_key.
* 4) Пишет в БД (insert или update существующей записи), но только если time_ms новее.
* 4) Пишет в БД только если time_ms строго больше текущего сохранённого.
*
* БОЛЬШОЙ КОММЕНТ ПРО АВТОРИЗАЦИЮ НА БУДУЩЕЕ:
* ---------------------------------------------------------------------------------
@ -56,7 +56,6 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
Net_UpsertUserParam_Request req = (Net_UpsertUserParam_Request) baseRequest;
// ---- basic fields validation ----
if (req.getLogin() == null || req.getLogin().isBlank()
|| req.getParam() == null || req.getParam().isBlank()
|| req.getTime_ms() == null || req.getTime_ms() <= 0
@ -75,12 +74,11 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
final String login = req.getLogin().trim();
final String param = req.getParam().trim();
final long timeMs = req.getTime_ms();
final String value = req.getValue(); // value может быть пустой строкой это ок
final String value = req.getValue();
final String deviceKeyB64 = req.getDevice_key().trim();
final String signatureB64 = req.getSignature().trim();
try {
// 1) parse keys
byte[] pubKey32;
byte[] sig64;
try {
@ -112,7 +110,6 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
);
}
// подписываемая строка
String signText = ShineSignatureConstants.USER_PARAMETER_PREFIX
+ login
+ param
@ -121,7 +118,6 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
byte[] signBytes = signText.getBytes(StandardCharsets.UTF_8);
// 3) verify signature (до БД можно, но нам всё равно нужна БД-проверка device_key->login)
boolean sigOk = Ed25519Util.verify(signBytes, sig64, pubKey32);
if (!sigOk) {
return NetExceptionResponseFactory.error(
@ -132,7 +128,6 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
);
}
// ---- DB checks + upsert in a transaction ----
SqliteDbController db = SqliteDbController.getInstance();
SolanaUsersDAO usersDAO = SolanaUsersDAO.getInstance();
UserParamsDAO paramsDAO = UserParamsDAO.getInstance();
@ -141,13 +136,11 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
boolean oldAuto = c.getAutoCommit();
c.setAutoCommit(false);
// BEGIN IMMEDIATE чтобы избежать гонок (две записи одного param параллельно)
try (Statement st = c.createStatement()) {
st.execute("BEGIN IMMEDIATE");
}
try {
// 1) user exists + device_key is exactly his
SolanaUserEntry user = usersDAO.getByLogin(c, login);
if (user == null) {
c.rollback();
@ -170,7 +163,6 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
);
}
// сравнение строкой: у тебя deviceKey хранится как Base64(32) (в идеале нормализовать)
if (!userDeviceKey.trim().equals(deviceKeyB64)) {
c.rollback();
return NetExceptionResponseFactory.error(
@ -181,11 +173,10 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
);
}
// 2) no newer time_ms already stored
UserParamEntry existing = paramsDAO.getByLoginAndParam(c, login, param);
if (existing != null) {
long existingTime = existing.getTimeMs();
if (existingTime > timeMs) {
// если есть более новое запрет
if (existing != null && existing.getTimeMs() > timeMs) {
c.rollback();
return NetExceptionResponseFactory.error(
req,
@ -194,14 +185,23 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
"Уже есть более новое значение этого параметра (time_ms больше)"
);
}
if (existingTime == timeMs) {
// если пришёл тот же time_ms можно либо принять как идемпотентно,
// либо сравнить value/signature. Для MVP примем как идемпотентно,
// но всё равно сделаем upsert (обновит value/signature тем же временем).
}
// если time_ms равен ничего не делаем (твой кейс)
if (existing != null && existing.getTimeMs() == timeMs) {
c.commit();
c.setAutoCommit(oldAuto);
Net_UpsertUserParam_Response resp = new Net_UpsertUserParam_Response();
resp.setOp(req.getOp());
resp.setRequestId(req.getRequestId());
resp.setStatus(WireCodes.Status.OK);
log.info(" UpsertUserParam noop (same time_ms): login={}, param={}, time_ms={}",
login, param, timeMs);
return resp;
}
// 4) upsert
// иначе existing==null или existingTime < timeMs -> пишем
UserParamEntry e = new UserParamEntry(
login,
param,

View File

@ -0,0 +1,34 @@
package server.logic.ws_protocol.JSON.handlers.userParams.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
/**
* Запрос GetUserParam получить один параметр пользователя.
*
* {
* "op": "GetUserParam",
* "requestId": "req-1",
* "payload": {
* "login": "anya",
* "param": "feed:lastSeenGlobal"
* }
* }
*
* ПРО ДОСТУП (на будущее):
* ---------------------------------------------------------------------------------
* Сейчас (MVP) этот запрос не ограничивает просмотр параметров, т.к. проект в тестовом режиме.
* Позже, вероятно, потребуется ограничить: кто и какие параметры может читать (сессия/права).
* Но для MVP эти проверки не нужны.
* ---------------------------------------------------------------------------------
*/
public class Net_GetUserParam_Request extends Net_Request {
private String login;
private String param;
public String getLogin() { return login; }
public void setLogin(String login) { this.login = login; }
public String getParam() { return param; }
public void setParam(String param) { this.param = param; }
}

View File

@ -0,0 +1,52 @@
package server.logic.ws_protocol.JSON.handlers.userParams.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
/**
* Ответ GetUserParam.
*
* Если найден:
* {
* "op": "GetUserParam",
* "requestId": "req-1",
* "status": 200,
* "payload": {
* "login": "anya",
* "param": "feed:lastSeenGlobal",
* "time_ms": 1736000000123,
* "value": "105",
* "device_key": "base64-32",
* "signature": "base64-64"
* }
* }
*
* Если не найден:
* status=404, payload пустой.
*/
public class Net_GetUserParam_Response extends Net_Response {
private String login;
private String param;
private Long time_ms;
private String value;
private String device_key;
private String signature;
public String getLogin() { return login; }
public void setLogin(String login) { this.login = login; }
public String getParam() { return param; }
public void setParam(String param) { this.param = param; }
public Long getTime_ms() { return time_ms; }
public void setTime_ms(Long time_ms) { this.time_ms = time_ms; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public String getDevice_key() { return device_key; }
public void setDevice_key(String device_key) { this.device_key = device_key; }
public String getSignature() { return signature; }
public void setSignature(String signature) { this.signature = signature; }
}

View File

@ -0,0 +1,29 @@
package server.logic.ws_protocol.JSON.handlers.userParams.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
/**
* Запрос ListUserParams получить все сохранённые параметры пользователя.
*
* {
* "op": "ListUserParams",
* "requestId": "req-2",
* "payload": {
* "login": "anya"
* }
* }
*
* ПРО ДОСТУП (на будущее):
* ---------------------------------------------------------------------------------
* Сейчас (MVP) запрос не ограничивает просмотр параметров.
* В будущем, вероятно, потребуется проверка сессии/прав: кто может читать параметры.
* Для MVP эти проверки не нужны.
* ---------------------------------------------------------------------------------
*/
public class Net_ListUserParams_Request extends Net_Request {
private String login;
public String getLogin() { return login; }
public void setLogin(String login) { this.login = login; }
}

View File

@ -0,0 +1,68 @@
package server.logic.ws_protocol.JSON.handlers.userParams.entyties;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
import java.util.ArrayList;
import java.util.List;
/**
* Ответ ListUserParams список всех параметров пользователя.
*
* {
* "op": "ListUserParams",
* "requestId": "req-2",
* "status": 200,
* "payload": {
* "login": "anya",
* "params": [
* {
* "login": "anya",
* "param": "feed:lastSeenGlobal",
* "time_ms": 1736000000123,
* "value": "105",
* "device_key": "base64-32",
* "signature": "base64-64"
* },
* ...
* ]
* }
* }
*/
public class Net_ListUserParams_Response extends Net_Response {
private String login;
private List<Item> params = new ArrayList<>();
public String getLogin() { return login; }
public void setLogin(String login) { this.login = login; }
public List<Item> getParams() { return params; }
public void setParams(List<Item> params) { this.params = params; }
public static class Item {
private String login;
private String param;
private Long time_ms;
private String value;
private String device_key;
private String signature;
public String getLogin() { return login; }
public void setLogin(String login) { this.login = login; }
public String getParam() { return param; }
public void setParam(String param) { this.param = param; }
public Long getTime_ms() { return time_ms; }
public void setTime_ms(Long time_ms) { this.time_ms = time_ms; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public String getDevice_key() { return device_key; }
public void setDevice_key(String device_key) { this.device_key = device_key; }
public String getSignature() { return signature; }
public void setSignature(String signature) { this.signature = signature; }
}
}