Files
iiko-connector/src/main/java/su/xserver/iikocon/MainVerticle.java
2026-04-10 15:26:51 +03:00

159 lines
6.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package su.xserver.iikocon;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.healthchecks.HealthChecks;
import io.vertx.ext.healthchecks.Status;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.SessionHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.sstore.SessionStore;
import io.vertx.ext.web.sstore.redis.RedisSessionStore;
import io.vertx.mysqlclient.MySQLConnectOptions;
import io.vertx.redis.client.Redis;
import io.vertx.redis.client.RedisAPI;
import io.vertx.redis.client.RedisOptions;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
public class MainVerticle extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(MainVerticle.class);
private Pool dbPool;
private Redis redisClient;
private UserService userService;
@Override
public void start(Promise<Void> startPromise) {
// Конфигурация из переменных окружения
JsonObject config = new JsonObject()
.put("db_host", System.getenv().getOrDefault("DB_HOST", "localhost"))
.put("db_port", Integer.parseInt(System.getenv().getOrDefault("DB_PORT", "3306")))
.put("db_name", System.getenv().getOrDefault("DB_NAME", "admin_db"))
.put("db_user", System.getenv().getOrDefault("DB_USER", "admin_user"))
.put("db_password", System.getenv().getOrDefault("DB_PASSWORD", "admin_pass"))
.put("redis_host", System.getenv().getOrDefault("REDIS_HOST", "localhost"))
.put("redis_port", Integer.parseInt(System.getenv().getOrDefault("REDIS_PORT", "6379")))
.put("http_port", Integer.parseInt(System.getenv().getOrDefault("HTTP_PORT", "8080")));
// Подключение к MariaDB
MySQLConnectOptions connectOptions = new MySQLConnectOptions()
.setHost(config.getString("db_host"))
.setPort(config.getInteger("db_port"))
.setDatabase(config.getString("db_name"))
.setUser(config.getString("db_user"))
.setPassword(config.getString("db_password"));
PoolOptions poolOptions = new PoolOptions().setMaxSize(5);
dbPool = Pool.pool(vertx, connectOptions, poolOptions);
// Подключение к Redis
RedisOptions redisOptions = new RedisOptions()
.setConnectionString("redis://" + config.getString("redis_host") + ":" + config.getInteger("redis_port"));
redisClient = Redis.createClient(vertx, redisOptions);
// Инициализация сервисов
userService = new UserService(dbPool);
// Инициализация БД (создание таблицы users)
userService.initDatabase().compose(v -> {
// Настройка сессий с Redis
SessionStore sessionStore = RedisSessionStore.create(vertx, redisClient);
SessionHandler sessionHandler = SessionHandler.create(sessionStore)
.setSessionCookieName("admin.session")
.setCookieHttpOnlyFlag(true)
.setCookieSecureFlag(false) // для разработки, в продакшене true + HTTPS
.setSessionTimeout(3600000); // 1 час
// Роутер
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route().handler(sessionHandler);
// Health Checks
HealthChecks hc = HealthChecks.create(vertx);
hc.register("database", promise -> dbPool.getConnection().onComplete(ar -> {
if (ar.succeeded()) {
ar.result().close();
promise.complete(Status.OK());
} else {
promise.fail(ar.cause());
}
}));
hc.register("redis", promise -> {
RedisAPI.api(redisClient)
.ping(Collections.singletonList("ping"))
.onSuccess(response -> {
if ("PONG".equals(response.toString())) {
promise.complete(Status.OK());
} else {
promise.fail("Unexpected ping response: " + response);
}
})
.onFailure(promise::fail);
});
router.get("/health").handler(rc -> hc.checkStatus().onComplete(ar -> {
if (ar.succeeded()) {
rc.response().end(ar.result().toJson().encodePrettily());
} else {
rc.response().setStatusCode(503).end(ar.cause().getMessage());
}
}));
// Статическая раздача фронтенда (из webroot)
router.route("/*").handler(StaticHandler.create("webroot").setCachingEnabled(false).setIndexPage("index.html"));
// API маршруты
AuthHandler authHandler = new AuthHandler(userService);
SetupHandler setupHandler = new SetupHandler(userService);
// Регистрация первого администратора (если таблица пуста)
router.post("/api/setup").handler(setupHandler::handleSetup);
// Логин
router.post("/api/login").handler(authHandler::handleLogin);
// Выход
router.post("/api/logout").handler(authHandler::handleLogout);
// Защищённые маршруты (требуют сессии)
router.route("/api/admin/*").handler(authHandler::requireAuth);
// Пример защищённого эндпоинта - получение списка пользователей
router.get("/api/admin/users").handler(rc -> {
userService.getAllUsers().onComplete(ar -> {
if (ar.succeeded()) {
rc.response().end(ar.result().encode());
} else {
rc.response().setStatusCode(500).end(ar.cause().getMessage());
}
});
});
// Запуск HTTP сервера
int port = config.getInteger("http_port");
return vertx.createHttpServer().requestHandler(router).listen(port);
}).onComplete(ar -> {
if (ar.succeeded()) {
log.info("Server started on port {}", config.getInteger("http_port"));
startPromise.complete();
} else {
log.error("Failed to start", ar.cause());
startPromise.fail(ar.cause());
}
});
}
@Override
public void stop() {
if (dbPool != null) dbPool.close();
}
}