package su.xserver.iikocon; import io.vertx.core.Future; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.templates.SqlTemplate; import org.mindrot.jbcrypt.BCrypt; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class UserService { private final Pool pool; public UserService(Pool pool) { this.pool = pool; } public Future initDatabase() { String createTable = """ CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, login VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, active BOOLEAN DEFAULT FALSE, ip VARCHAR(45), created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) """; return pool.query(createTable).execute().mapEmpty(); } public Future countUsers() { return pool.query("SELECT COUNT(*) AS cnt FROM users") .execute() .map(rows -> rows.iterator().next().getLong("cnt")); } public Future createUser(String login, String email, String password, String ip, boolean active) { String hash = BCrypt.hashpw(password, BCrypt.gensalt()); Map params = Map.of( "login", login, "email", email, "password", hash, "ip", ip, "active", active ); return SqlTemplate.forUpdate(pool, "INSERT INTO users (login, email, password, ip, active) VALUES (#{login}, #{email}, #{password}, #{ip}, #{active})") .execute(params) .mapEmpty(); } // Существующий метод оставляем, но он будет создавать неактивного пользователя (active = false) public Future createUser(String login, String email, String password, String ip) { return createUser(login, email, password, ip, false); } public Future setActive(int id, boolean active) { return SqlTemplate.forUpdate(pool, "UPDATE users SET active = #{active} WHERE id = #{id}") .execute(Map.of("id", id, "active", active)).mapEmpty(); } public Future findByLoginOrEmail(String loginOrEmail) { String sql = "SELECT id, login, email, password, active, ip, created, updated FROM users WHERE login = ? OR email = ?"; return pool.preparedQuery(sql) .execute(Tuple.of(loginOrEmail, loginOrEmail)) .map(rows -> { if (rows.size() == 0) { return null; } Row row = rows.iterator().next(); return toJson(row); }); } public Future findByEmail(String email) { return SqlTemplate.forQuery(pool, "SELECT id, login, email, password, active, ip, created, updated FROM users WHERE email = #{email}") .mapTo(this::toJson) .execute(Map.of("email", email)) .map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null); } public Future findByLogin(String login) { return SqlTemplate.forQuery(pool, "SELECT id, login, password, created, updated, ip FROM users WHERE login = #{login}") .mapTo(row -> new JsonObject() .put("id", row.getInteger("id")) .put("login", row.getString("login")) .put("password", row.getString("password")) .put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null) .put("ip", row.getString("ip"))) .execute(Collections.singletonMap("login", login)) .map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null); } public Future getAllUsers() { return pool.query("SELECT id, login, email, active, ip, created, updated FROM users ORDER BY id") .execute() .map(rows -> { JsonArray array = new JsonArray(); for (Row row : rows) { array.add(new JsonObject() .put("id", row.getInteger("id")) .put("login", row.getString("login")) .put("email", row.getString("email")) .put("active", row.getBoolean("active")) .put("ip", row.getString("ip")) .put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null)); } return array; }); } public Future updateUser(int id, String login, String email, String password, String ip) { Map params = new HashMap<>(); params.put("id", id); params.put("login", login); params.put("email", email); 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}, email = #{email}, password = #{password}, ip = #{ip} WHERE id = #{id}"; } else { sql = "UPDATE users SET login = #{login}, email = #{email}, ip = #{ip} WHERE id = #{id}"; } return SqlTemplate.forUpdate(pool, sql).execute(params).mapEmpty(); } public Future 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); } catch (Exception e) { return false; } } private JsonObject toJson(Row row) { return new JsonObject() .put("id", row.getInteger("id")) .put("login", row.getString("login")) .put("email", row.getString("email")) .put("password", row.getString("password")) // ← ДОБАВИТЬ ЭТУ СТРОКУ .put("active", row.getBoolean("active")) .put("ip", row.getString("ip")) .put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null); } }