up
This commit is contained in:
50
src/main/java/su/xserver/iikocon/DateRangeSetup.java
Normal file
50
src/main/java/su/xserver/iikocon/DateRangeSetup.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package su.xserver.iikocon;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
public class DateRangeSetup {
|
||||
public static void main(String[] args) {
|
||||
// Параметры по умолчанию
|
||||
String login = "4444";
|
||||
String password = "4444";
|
||||
String server = "folk-amber-co.iiko.it";
|
||||
String presetId = "7ddc40c3-9d5f-408f-aa1e-652964b36c6c";
|
||||
|
||||
// Вычисление dateFrom и dateTo
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate dateFrom = today.minusDays(7);
|
||||
LocalDate dateTo = today;
|
||||
|
||||
// Переопределение из аргументов командной строки
|
||||
if (args.length > 0 && args[0] != null && !args[0].isEmpty()) {
|
||||
try {
|
||||
dateFrom = LocalDate.parse(args[0]);
|
||||
} catch (DateTimeParseException e) {
|
||||
System.err.println("Ошибка парсинга dateFrom: " + args[0] + ". Используется значение по умолчанию.");
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length > 1 && args[1] != null && !args[1].isEmpty()) {
|
||||
try {
|
||||
dateTo = LocalDate.parse(args[1]);
|
||||
} catch (DateTimeParseException e) {
|
||||
System.err.println("Ошибка парсинга dateTo: " + args[1] + ". Используется значение по умолчанию.");
|
||||
}
|
||||
}
|
||||
|
||||
// Форматирование дат в YYYY-MM-DD
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
String formattedDateFrom = dateFrom.format(formatter);
|
||||
String formattedDateTo = dateTo.format(formatter);
|
||||
|
||||
// Вывод переменных (можно заменить на дальнейшее использование)
|
||||
System.out.println("login=" + login);
|
||||
System.out.println("password=" + password);
|
||||
System.out.println("server=" + server);
|
||||
System.out.println("presetId=" + presetId);
|
||||
System.out.println("dateFrom=" + formattedDateFrom);
|
||||
System.out.println("dateTo=" + formattedDateTo);
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,42 @@ public class MainVerticle extends AbstractVerticle {
|
||||
}
|
||||
}));
|
||||
|
||||
router.post("/api/admin/users").handler(rc -> {
|
||||
JsonObject body = rc.body().asJsonObject();
|
||||
String login = body.getString("login");
|
||||
String password = body.getString("password");
|
||||
String ip = rc.request().remoteAddress().host();
|
||||
if (login == null || password == null) {
|
||||
rc.response().setStatusCode(400).end("Missing login or password");
|
||||
return;
|
||||
}
|
||||
userService.createUser(login, password, ip)
|
||||
.onSuccess(v -> rc.response().setStatusCode(201).end())
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
router.put("/api/admin/users/:id").handler(rc -> {
|
||||
int id = Integer.parseInt(rc.pathParam("id"));
|
||||
JsonObject body = rc.body().asJsonObject();
|
||||
String login = body.getString("login");
|
||||
String password = body.getString("password");
|
||||
String ip = rc.request().remoteAddress().host();
|
||||
if (login == null) {
|
||||
rc.response().setStatusCode(400).end("Missing login");
|
||||
return;
|
||||
}
|
||||
userService.updateUser(id, login, password, ip)
|
||||
.onSuccess(v -> rc.response().end())
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
router.delete("/api/admin/users/:id").handler(rc -> {
|
||||
int id = Integer.parseInt(rc.pathParam("id"));
|
||||
userService.deleteUser(id)
|
||||
.onSuccess(v -> rc.response().end())
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
// Получение текущего пользователя
|
||||
router.get("/api/admin/me").handler(rc -> {
|
||||
Integer userId = rc.session().get("userId");
|
||||
@@ -171,6 +207,64 @@ public class MainVerticle extends AbstractVerticle {
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/api/admin/restaurants").handler(rc -> restaurantService.getAllRestaurants().onComplete(ar -> {
|
||||
if (ar.succeeded()) {
|
||||
rc.response()
|
||||
.putHeader("Content-Type", "application/json")
|
||||
.end(ar.result().encode());
|
||||
} else {
|
||||
rc.response().setStatusCode(500).end(ar.cause().getMessage());
|
||||
}
|
||||
}));
|
||||
|
||||
router.get("/api/admin/restaurants/:id").handler(rc -> {
|
||||
int id = Integer.parseInt(rc.pathParam("id"));
|
||||
restaurantService.findById(id)
|
||||
.onSuccess(rest -> {
|
||||
if (rest == null) rc.response().setStatusCode(404).end();
|
||||
else rc.response().putHeader("Content-Type", "application/json").end(rest.encode());
|
||||
})
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
router.post("/api/admin/restaurants").handler(rc -> {
|
||||
JsonObject body = rc.body().asJsonObject();
|
||||
String name = body.getString("name");
|
||||
String login = body.getString("login");
|
||||
String password = body.getString("password");
|
||||
String host = body.getString("host");
|
||||
if (name == null || login == null || password == null || host == null) {
|
||||
rc.response().setStatusCode(400).end("Missing fields");
|
||||
return;
|
||||
}
|
||||
restaurantService.createRestaurant(name, login, password, host)
|
||||
.onSuccess(v -> rc.response().setStatusCode(201).end())
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
router.put("/api/admin/restaurants/:id").handler(rc -> {
|
||||
int id = Integer.parseInt(rc.pathParam("id"));
|
||||
JsonObject body = rc.body().asJsonObject();
|
||||
String name = body.getString("name");
|
||||
String login = body.getString("login");
|
||||
String password = body.getString("password");
|
||||
String host = body.getString("host");
|
||||
if (name == null || login == null || host == null) {
|
||||
rc.response().setStatusCode(400).end("Missing required fields");
|
||||
return;
|
||||
}
|
||||
restaurantService.updateRestaurant(id, name, login, password, host)
|
||||
.onSuccess(v -> rc.response().end())
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
router.delete("/api/admin/restaurants/:id").handler(rc -> {
|
||||
int id = Integer.parseInt(rc.pathParam("id"));
|
||||
restaurantService.deleteRestaurant(id)
|
||||
.onSuccess(v -> rc.response().end())
|
||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
|
||||
194
src/main/java/su/xserver/iikocon/ProxyVerticle.java
Normal file
194
src/main/java/su/xserver/iikocon/ProxyVerticle.java
Normal file
@@ -0,0 +1,194 @@
|
||||
package su.xserver.iikocon;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.core.json.Json;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.ext.web.client.WebClientOptions;
|
||||
import io.vertx.ext.web.codec.BodyCodec;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
||||
public class ProxyVerticle extends AbstractVerticle {
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) {
|
||||
webClient = WebClient.create(vertx, new WebClientOptions()
|
||||
.setSsl(true)
|
||||
.setTrustAll(true)
|
||||
.setVerifyHost(false));
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
router.post("/api/proxy").handler(this::handlePost);
|
||||
router.get("/api/proxy").handler(this::handleGet);
|
||||
|
||||
int port = 8080;
|
||||
vertx.createHttpServer()
|
||||
.requestHandler(router)
|
||||
.listen(port).onComplete(http -> {
|
||||
if (http.succeeded()) {
|
||||
System.out.println("Proxy server started on port " + port);
|
||||
startPromise.complete();
|
||||
} else {
|
||||
startPromise.fail(http.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handlePost(RoutingContext ctx) {
|
||||
String apiServer = System.getenv("IIKO_API_SERVER");
|
||||
String apiLogin = System.getenv("IIKO_API_LOGIN");
|
||||
String apiPass = System.getenv("IIKO_API_PASS");
|
||||
String externalEndpoint = System.getenv("IIKO_API_ENDPOINT");
|
||||
if (externalEndpoint == null || externalEndpoint.isBlank()) {
|
||||
externalEndpoint = "/your-endpoint";
|
||||
}
|
||||
|
||||
if (apiServer == null || apiLogin == null || apiPass == null) {
|
||||
fail(ctx, 500, "Missing required environment variables: IIKO_API_SERVER, IIKO_API_LOGIN, IIKO_API_PASS");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject body = ctx.body().asJsonObject();
|
||||
if (body == null) {
|
||||
fail(ctx, 400, "Request body must be JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
String signature = sha1(apiPass);
|
||||
String authUrl = "https://" + apiServer + ":443/resto/api/auth?login=" + apiLogin + "&pass=" + signature;
|
||||
String finalExternalEndpoint = externalEndpoint;
|
||||
webClient.getAbs(authUrl)
|
||||
.as(BodyCodec.string())
|
||||
.send()
|
||||
.onSuccess(authResp -> {
|
||||
if (authResp.statusCode() != 200) {
|
||||
fail(ctx, authResp.statusCode(), "Authentication failed: " + authResp.statusMessage());
|
||||
return;
|
||||
}
|
||||
String token = authResp.body();
|
||||
String targetUrl = "https://" + apiServer + finalExternalEndpoint;
|
||||
webClient.request(HttpMethod.POST, targetUrl)
|
||||
.putHeader("Content-Type", "application/json")
|
||||
.as(BodyCodec.jsonObject())
|
||||
.sendJsonObject(body)
|
||||
.onSuccess(apiResp -> {
|
||||
webClient.getAbs("https://" + apiServer + ":443/resto/api/logout?key=" + token)
|
||||
.send()
|
||||
.onFailure(err -> System.err.println("Logout failed: " + err.getMessage()));
|
||||
if (apiResp.statusCode() == 200) {
|
||||
ctx.response().setStatusCode(200).end(apiResp.body().encode());
|
||||
} else {
|
||||
fail(ctx, apiResp.statusCode(), "External API error: " + apiResp.statusMessage());
|
||||
}
|
||||
})
|
||||
.onFailure(err -> fail(ctx, 500, "Request to external API failed: " + err.getMessage()));
|
||||
})
|
||||
.onFailure(err -> fail(ctx, 500, "Auth request failed: " + err.getMessage()));
|
||||
}
|
||||
|
||||
private void handleGet(RoutingContext ctx) {
|
||||
String presetId = ctx.queryParam("presetId").stream().findFirst().orElse(null);
|
||||
String dateFrom = ctx.queryParam("dateFrom").stream().findFirst().orElse(null);
|
||||
String dateTo = ctx.queryParam("dateTo").stream().findFirst().orElse(null);
|
||||
String server = ctx.queryParam("server").stream().findFirst().orElse(null);
|
||||
String password = ctx.queryParam("password").stream().findFirst().orElse(null);
|
||||
String login = ctx.queryParam("login").stream().findFirst().orElse(null);
|
||||
String type = ctx.queryParam("type").stream().findFirst().orElse(null);
|
||||
String rootType = ctx.queryParam("rootType").stream().findFirst().orElse(null);
|
||||
|
||||
if (server == null || login == null || password == null) {
|
||||
fail(ctx, 400, "Missing required parameters: server, login, password");
|
||||
return;
|
||||
}
|
||||
|
||||
String signature = sha1(password);
|
||||
String authUrl = "https://" + server + ":443/resto/api/auth?login=" + login + "&pass=" + signature;
|
||||
webClient.getAbs(authUrl)
|
||||
.as(BodyCodec.string())
|
||||
.send()
|
||||
.onSuccess(authResp -> {
|
||||
if (authResp.statusCode() != 200) {
|
||||
fail(ctx, authResp.statusCode(), "Authentication failed: " + authResp.statusMessage());
|
||||
return;
|
||||
}
|
||||
String token = authResp.body();
|
||||
String dataUrl;
|
||||
if ("entity".equals(type)) {
|
||||
dataUrl = "https://" + server + "/resto/api/v2/entities/list?key=" + token;
|
||||
if (rootType != null && !rootType.isBlank()) {
|
||||
dataUrl += "&rootType=" + rootType;
|
||||
}
|
||||
} else {
|
||||
if (presetId == null || dateFrom == null || dateTo == null) {
|
||||
fail(ctx, 400, "Missing presetId, dateFrom or dateTo for report request");
|
||||
return;
|
||||
}
|
||||
dataUrl = "https://" + server + "/resto/api/v2/reports/olap/byPresetId/" + presetId +
|
||||
"?key=" + token + "&dateFrom=" + dateFrom + "&dateTo=" + dateTo;
|
||||
}
|
||||
System.out.println("URL: " + dataUrl);
|
||||
webClient.getAbs(dataUrl)
|
||||
.as(BodyCodec.jsonObject())
|
||||
.send()
|
||||
.onSuccess(dataResp -> {
|
||||
// logout (fire and forget)
|
||||
webClient.getAbs("https://" + server + ":443/resto/api/logout?key=" + token)
|
||||
.send()
|
||||
.onFailure(err -> System.err.println("Logout failed: " + err.getMessage()));
|
||||
if (dataResp.statusCode() == 200) {
|
||||
JsonObject responseBody = dataResp.body();
|
||||
if ("entity".equals(type)) {
|
||||
ctx.response().setStatusCode(200).end(responseBody.encode());
|
||||
} else {
|
||||
Object data = responseBody.getValue("data");
|
||||
if (data == null) {
|
||||
ctx.response().setStatusCode(200).end(responseBody.encode());
|
||||
} else {
|
||||
// data может быть массивом, объектом или другим типом
|
||||
ctx.response().setStatusCode(200).end(Json.encode(data));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fail(ctx, dataResp.statusCode(), "External API error: " + dataResp.statusMessage());
|
||||
}
|
||||
})
|
||||
.onFailure(err -> fail(ctx, 500, "Data request failed: " + err.getMessage()));
|
||||
})
|
||||
.onFailure(err -> fail(ctx, 500, "Auth request failed: " + err.getMessage()));
|
||||
}
|
||||
|
||||
private String sha1(String input) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] digest = md.digest(input.getBytes());
|
||||
return HexFormat.of().formatHex(digest).toLowerCase();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(RoutingContext ctx, int status, String message) {
|
||||
System.err.println("Error: " + message);
|
||||
ctx.response().setStatusCode(status).end(new JsonObject().put("error", message).encode());
|
||||
}
|
||||
}
|
||||
|
||||
// > GET /api/proxy?server=folk-amber-co.iiko.it&login=4444&password=4444&presetId=7ddc40c3-9d5f-408f-aa1e-652964b36c6c&dateFrom=2026-04-10&dateTo=2026-04-17 HTTP/1.1
|
||||
// > Host: localhost:8080
|
||||
// > access-token: ddb4ab653b9194ec1ea5448cee2a8a26282b0866c1d4a86e98e9b0f84bc91944
|
||||
// > User-Agent: v2raytun/ios
|
||||
// > X-App-Version: 2.4.3
|
||||
// > X-Device-Model: iPhone 11 Pro
|
||||
// > X-Device-OS: iOS
|
||||
// > X-HWID: HHS8JDJN-F2EB-HFBS-KMWX-234FA7B95JSC
|
||||
// > X-Ver-OS: 26.0
|
||||
// > Accept: */*
|
||||
@@ -90,4 +90,45 @@ public class RestaurantService {
|
||||
return array;
|
||||
});
|
||||
}
|
||||
|
||||
public Future<JsonObject> findById(int id) {
|
||||
return SqlTemplate.forQuery(pool,
|
||||
"SELECT id, name, login, password, host, created, updated FROM restaurants WHERE id = #{id}")
|
||||
.mapTo(row -> new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("name", row.getString("name"))
|
||||
.put("login", row.getString("login"))
|
||||
.put("password", row.getString("password"))
|
||||
.put("host", row.getString("host"))
|
||||
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null)
|
||||
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null))
|
||||
.execute(Collections.singletonMap("id", id))
|
||||
.map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null);
|
||||
}
|
||||
|
||||
public Future<Void> updateRestaurant(int id, String name, String login, String password, String host) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("id", id);
|
||||
params.put("name", name);
|
||||
params.put("login", login);
|
||||
params.put("host", host);
|
||||
|
||||
String sql;
|
||||
if (password != null && !password.isEmpty()) {
|
||||
params.put("password", password);
|
||||
sql = "UPDATE restaurants SET name = #{name}, login = #{login}, password = #{password}, host = #{host} WHERE id = #{id}";
|
||||
} else {
|
||||
sql = "UPDATE restaurants SET name = #{name}, login = #{login}, host = #{host} WHERE id = #{id}";
|
||||
}
|
||||
|
||||
return SqlTemplate.forUpdate(pool, sql)
|
||||
.execute(params)
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
public Future<Void> deleteRestaurant(int id) {
|
||||
return SqlTemplate.forUpdate(pool, "DELETE FROM restaurants WHERE id = #{id}")
|
||||
.execute(Collections.singletonMap("id", id))
|
||||
.mapEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,32 @@ public class UserService {
|
||||
});
|
||||
}
|
||||
|
||||
public Future<Void> updateUser(int id, String login, String password, String ip) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("id", id);
|
||||
params.put("login", login);
|
||||
params.put("ip", ip);
|
||||
|
||||
String sql;
|
||||
if (password != null && !password.isEmpty()) {
|
||||
String hash = BCrypt.hashpw(password, BCrypt.gensalt());
|
||||
params.put("password", hash);
|
||||
sql = "UPDATE users SET login = #{login}, password = #{password}, ip = #{ip} WHERE id = #{id}";
|
||||
} else {
|
||||
sql = "UPDATE users SET login = #{login}, ip = #{ip} WHERE id = #{id}";
|
||||
}
|
||||
|
||||
return SqlTemplate.forUpdate(pool, sql)
|
||||
.execute(params)
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
public Future<Void> deleteUser(int id) {
|
||||
return SqlTemplate.forUpdate(pool, "DELETE FROM users WHERE id = #{id}")
|
||||
.execute(Collections.singletonMap("id", id))
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
public boolean checkPassword(String plain, String hash) {
|
||||
try {
|
||||
return BCrypt.checkpw(plain, hash);
|
||||
|
||||
@@ -31,6 +31,7 @@ public class HealthCheckService {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
if ("PONG".equalsIgnoreCase(response.toString())) {
|
||||
JsonObject data = new JsonObject()
|
||||
.put("name", "redis")
|
||||
.put("latency_ms", time);
|
||||
future.complete(Status.OK(data));
|
||||
} else {
|
||||
@@ -47,6 +48,7 @@ public class HealthCheckService {
|
||||
.onSuccess(rs -> {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
JsonObject data = new JsonObject()
|
||||
.put("name", "database")
|
||||
.put("latency_ms", time);
|
||||
future.complete(Status.OK(data));
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user